mirror of
https://github.com/immich-app/immich.git
synced 2025-09-29 15:31:13 -04:00
feat: resurrect advanced info (#21633)
* feat: resurrect advanced info * display null values as well * add exif details --------- Co-authored-by: shenlong-tanwen <139912620+shalong-tanwen@users.noreply.github.com>
This commit is contained in:
parent
39eee6a634
commit
67a8cab286
@ -1978,6 +1978,7 @@
|
||||
"trash_page_select_assets_btn": "Select assets",
|
||||
"trash_page_title": "Trash ({count})",
|
||||
"trashed_items_will_be_permanently_deleted_after": "Trashed items will be permanently deleted after {days, plural, one {# day} other {# days}}.",
|
||||
"troubleshoot": "Troubleshoot",
|
||||
"type": "Type",
|
||||
"unable_to_change_pin_code": "Unable to change PIN code",
|
||||
"unable_to_setup_pin_code": "Unable to setup PIN code",
|
||||
|
@ -1,3 +1,4 @@
|
||||
import 'package:immich_mobile/domain/models/album/local_album.model.dart';
|
||||
import 'package:immich_mobile/domain/models/asset/base_asset.model.dart';
|
||||
import 'package:immich_mobile/domain/models/exif.model.dart';
|
||||
import 'package:immich_mobile/infrastructure/repositories/local_asset.repository.dart';
|
||||
@ -27,6 +28,14 @@ class AssetService {
|
||||
return asset is LocalAsset ? _localAssetRepository.watch(id) : _remoteAssetRepository.watch(id);
|
||||
}
|
||||
|
||||
Future<List<LocalAsset?>> getLocalAssetsByChecksum(String checksum) {
|
||||
return _localAssetRepository.getByChecksum(checksum);
|
||||
}
|
||||
|
||||
Future<RemoteAsset?> getRemoteAssetByChecksum(String checksum) {
|
||||
return _remoteAssetRepository.getByChecksum(checksum);
|
||||
}
|
||||
|
||||
Future<RemoteAsset?> getRemoteAsset(String id) {
|
||||
return _remoteAssetRepository.get(id);
|
||||
}
|
||||
@ -89,4 +98,8 @@ class AssetService {
|
||||
Future<int> getLocalHashedCount() {
|
||||
return _localAssetRepository.getHashedCount();
|
||||
}
|
||||
|
||||
Future<List<LocalAlbum>> getSourceAlbums(String localAssetId, {BackupSelection? backupSelection}) {
|
||||
return _localAssetRepository.getSourceAlbums(localAssetId, backupSelection: backupSelection);
|
||||
}
|
||||
}
|
||||
|
@ -4,7 +4,6 @@ import 'package:drift/drift.dart';
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:immich_mobile/domain/models/album/local_album.model.dart';
|
||||
import 'package:immich_mobile/domain/models/asset/base_asset.model.dart';
|
||||
import 'package:immich_mobile/infrastructure/entities/local_album.entity.dart';
|
||||
import 'package:immich_mobile/infrastructure/entities/local_asset.entity.dart';
|
||||
import 'package:immich_mobile/infrastructure/repositories/db.repository.dart';
|
||||
import 'package:immich_mobile/providers/infrastructure/db.provider.dart';
|
||||
@ -138,22 +137,4 @@ class DriftBackupRepository extends DriftDatabaseRepository {
|
||||
|
||||
return query.map((localAsset) => localAsset.toDto()).get();
|
||||
}
|
||||
|
||||
FutureOr<List<LocalAlbum>> getSourceAlbums(String localAssetId) {
|
||||
final query = _db.localAlbumEntity.select()
|
||||
..where(
|
||||
(lae) =>
|
||||
existsQuery(
|
||||
_db.localAlbumAssetEntity.selectOnly()
|
||||
..addColumns([_db.localAlbumAssetEntity.albumId])
|
||||
..where(
|
||||
_db.localAlbumAssetEntity.albumId.equalsExp(lae.id) &
|
||||
_db.localAlbumAssetEntity.assetId.equals(localAssetId),
|
||||
),
|
||||
) &
|
||||
lae.backupSelection.equalsValue(BackupSelection.selected),
|
||||
)
|
||||
..orderBy([(lae) => OrderingTerm.asc(lae.name)]);
|
||||
return query.map((localAlbum) => localAlbum.toDto()).get();
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,8 @@
|
||||
import 'package:collection/collection.dart';
|
||||
import 'package:drift/drift.dart';
|
||||
import 'package:immich_mobile/domain/models/album/local_album.model.dart';
|
||||
import 'package:immich_mobile/domain/models/asset/base_asset.model.dart';
|
||||
import 'package:immich_mobile/infrastructure/entities/local_album.entity.dart';
|
||||
import 'package:immich_mobile/infrastructure/entities/local_asset.entity.dart';
|
||||
import 'package:immich_mobile/infrastructure/entities/local_asset.entity.drift.dart';
|
||||
import 'package:immich_mobile/infrastructure/repositories/db.repository.dart';
|
||||
@ -26,6 +28,12 @@ class DriftLocalAssetRepository extends DriftDatabaseRepository {
|
||||
|
||||
Future<LocalAsset?> get(String id) => _assetSelectable(id).getSingleOrNull();
|
||||
|
||||
Future<List<LocalAsset?>> getByChecksum(String checksum) {
|
||||
final query = _db.localAssetEntity.select()..where((lae) => lae.checksum.equals(checksum));
|
||||
|
||||
return query.map((row) => row.toDto()).get();
|
||||
}
|
||||
|
||||
Stream<LocalAsset?> watch(String id) => _assetSelectable(id).watchSingleOrNull();
|
||||
|
||||
Future<void> updateHashes(Iterable<LocalAsset> hashes) {
|
||||
@ -69,4 +77,23 @@ class DriftLocalAssetRepository extends DriftDatabaseRepository {
|
||||
Future<int> getHashedCount() {
|
||||
return _db.managers.localAssetEntity.filter((e) => e.checksum.isNull().not()).count();
|
||||
}
|
||||
|
||||
Future<List<LocalAlbum>> getSourceAlbums(String localAssetId, {BackupSelection? backupSelection}) {
|
||||
final query = _db.localAlbumEntity.select()
|
||||
..where(
|
||||
(lae) => existsQuery(
|
||||
_db.localAlbumAssetEntity.selectOnly()
|
||||
..addColumns([_db.localAlbumAssetEntity.albumId])
|
||||
..where(
|
||||
_db.localAlbumAssetEntity.albumId.equalsExp(lae.id) &
|
||||
_db.localAlbumAssetEntity.assetId.equals(localAssetId),
|
||||
),
|
||||
),
|
||||
)
|
||||
..orderBy([(lae) => OrderingTerm.asc(lae.name)]);
|
||||
if (backupSelection != null) {
|
||||
query.where((lae) => lae.backupSelection.equalsValue(backupSelection));
|
||||
}
|
||||
return query.map((localAlbum) => localAlbum.toDto()).get();
|
||||
}
|
||||
}
|
||||
|
@ -55,6 +55,12 @@ class RemoteAssetRepository extends DriftDatabaseRepository {
|
||||
return _assetSelectable(id).getSingleOrNull();
|
||||
}
|
||||
|
||||
Future<RemoteAsset?> getByChecksum(String checksum) {
|
||||
final query = _db.remoteAssetEntity.select()..where((row) => row.checksum.equals(checksum));
|
||||
|
||||
return query.map((row) => row.toDto()).getSingleOrNull();
|
||||
}
|
||||
|
||||
Future<List<RemoteAsset>> getStackChildren(RemoteAsset asset) {
|
||||
if (asset.stackId == null) {
|
||||
return Future.value([]);
|
||||
|
345
mobile/lib/presentation/pages/drift_asset_troubleshoot.page.dart
Normal file
345
mobile/lib/presentation/pages/drift_asset_troubleshoot.page.dart
Normal file
@ -0,0 +1,345 @@
|
||||
import 'package:auto_route/auto_route.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:immich_mobile/domain/models/asset/base_asset.model.dart';
|
||||
import 'package:immich_mobile/domain/models/exif.model.dart';
|
||||
import 'package:immich_mobile/providers/infrastructure/asset.provider.dart';
|
||||
|
||||
@RoutePage()
|
||||
class AssetTroubleshootPage extends ConsumerWidget {
|
||||
final BaseAsset asset;
|
||||
|
||||
const AssetTroubleshootPage({super.key, required this.asset});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(title: const Text("Asset Troubleshoot")),
|
||||
body: SingleChildScrollView(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(16.0),
|
||||
child: _AssetDetailsView(asset: asset),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _AssetDetailsView extends ConsumerWidget {
|
||||
final BaseAsset asset;
|
||||
|
||||
const _AssetDetailsView({required this.asset});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
_AssetPropertiesSection(asset: asset),
|
||||
const SizedBox(height: 16),
|
||||
Text('Matching Assets', style: Theme.of(context).textTheme.titleMedium?.copyWith(fontWeight: FontWeight.bold)),
|
||||
if (asset.checksum != null) ...[
|
||||
_LocalAssetsSection(asset: asset),
|
||||
const SizedBox(height: 16),
|
||||
_RemoteAssetSection(asset: asset),
|
||||
] else ...[
|
||||
const _PropertySectionCard(
|
||||
title: 'Local Assets',
|
||||
properties: [_PropertyItem(label: 'Status', value: 'No checksum available - cannot fetch local assets')],
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
const _PropertySectionCard(
|
||||
title: 'Remote Assets',
|
||||
properties: [_PropertyItem(label: 'Status', value: 'No checksum available - cannot fetch remote asset')],
|
||||
),
|
||||
],
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _AssetPropertiesSection extends ConsumerStatefulWidget {
|
||||
final BaseAsset asset;
|
||||
|
||||
const _AssetPropertiesSection({required this.asset});
|
||||
|
||||
@override
|
||||
ConsumerState createState() => _AssetPropertiesSectionState();
|
||||
}
|
||||
|
||||
class _AssetPropertiesSectionState extends ConsumerState<_AssetPropertiesSection> {
|
||||
List<_PropertyItem> properties = [];
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_buildAssetProperties(widget.asset).whenComplete(() {
|
||||
if (mounted) {
|
||||
setState(() {});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final title = _getAssetTypeTitle(widget.asset);
|
||||
|
||||
return _PropertySectionCard(title: title, properties: properties);
|
||||
}
|
||||
|
||||
Future<void> _buildAssetProperties(BaseAsset asset) async {
|
||||
_addCommonProperties();
|
||||
|
||||
if (asset is LocalAsset) {
|
||||
await _addLocalAssetProperties(asset);
|
||||
} else if (asset is RemoteAsset) {
|
||||
await _addRemoteAssetProperties(asset);
|
||||
}
|
||||
}
|
||||
|
||||
void _addCommonProperties() {
|
||||
final asset = widget.asset;
|
||||
properties.addAll([
|
||||
_PropertyItem(label: 'Name', value: asset.name),
|
||||
_PropertyItem(label: 'Checksum', value: asset.checksum),
|
||||
_PropertyItem(label: 'Type', value: asset.type.toString()),
|
||||
_PropertyItem(label: 'Created At', value: asset.createdAt.toString()),
|
||||
_PropertyItem(label: 'Updated At', value: asset.updatedAt.toString()),
|
||||
_PropertyItem(label: 'Width', value: asset.width?.toString()),
|
||||
_PropertyItem(label: 'Height', value: asset.height?.toString()),
|
||||
_PropertyItem(
|
||||
label: 'Duration',
|
||||
value: asset.durationInSeconds != null ? '${asset.durationInSeconds} seconds' : null,
|
||||
),
|
||||
_PropertyItem(label: 'Is Favorite', value: asset.isFavorite.toString()),
|
||||
_PropertyItem(label: 'Live Photo Video ID', value: asset.livePhotoVideoId),
|
||||
]);
|
||||
}
|
||||
|
||||
Future<void> _addLocalAssetProperties(LocalAsset asset) async {
|
||||
properties.insertAll(0, [
|
||||
_PropertyItem(label: 'Local ID', value: asset.id),
|
||||
_PropertyItem(label: 'Remote ID', value: asset.remoteId),
|
||||
]);
|
||||
|
||||
properties.insert(4, _PropertyItem(label: 'Orientation', value: asset.orientation.toString()));
|
||||
final albums = await ref.read(assetServiceProvider).getSourceAlbums(asset.id);
|
||||
properties.add(_PropertyItem(label: 'Album', value: albums.map((a) => a.name).join(', ')));
|
||||
}
|
||||
|
||||
Future<void> _addRemoteAssetProperties(RemoteAsset asset) async {
|
||||
properties.insertAll(0, [
|
||||
_PropertyItem(label: 'Remote ID', value: asset.id),
|
||||
_PropertyItem(label: 'Local ID', value: asset.localId),
|
||||
_PropertyItem(label: 'Owner ID', value: asset.ownerId),
|
||||
]);
|
||||
|
||||
final additionalProps = <_PropertyItem>[
|
||||
_PropertyItem(label: 'Thumb Hash', value: asset.thumbHash),
|
||||
_PropertyItem(label: 'Visibility', value: asset.visibility.toString()),
|
||||
_PropertyItem(label: 'Stack ID', value: asset.stackId),
|
||||
];
|
||||
|
||||
properties.insertAll(4, additionalProps);
|
||||
|
||||
final exif = await ref.read(assetServiceProvider).getExif(asset);
|
||||
if (exif != null) {
|
||||
_addExifProperties(exif);
|
||||
} else {
|
||||
properties.add(const _PropertyItem(label: 'EXIF', value: null));
|
||||
}
|
||||
}
|
||||
|
||||
void _addExifProperties(ExifInfo exif) {
|
||||
properties.addAll([
|
||||
_PropertyItem(
|
||||
label: 'File Size',
|
||||
value: exif.fileSize != null ? '${(exif.fileSize! / 1024 / 1024).toStringAsFixed(2)} MB' : null,
|
||||
),
|
||||
_PropertyItem(label: 'Description', value: exif.description),
|
||||
_PropertyItem(label: 'EXIF Width', value: exif.width?.toString()),
|
||||
_PropertyItem(label: 'EXIF Height', value: exif.height?.toString()),
|
||||
_PropertyItem(label: 'Date Taken', value: exif.dateTimeOriginal?.toString()),
|
||||
_PropertyItem(label: 'Time Zone', value: exif.timeZone),
|
||||
_PropertyItem(label: 'Camera Make', value: exif.make),
|
||||
_PropertyItem(label: 'Camera Model', value: exif.model),
|
||||
_PropertyItem(label: 'Lens', value: exif.lens),
|
||||
_PropertyItem(label: 'F-Number', value: exif.f != null ? 'f/${exif.fNumber}' : null),
|
||||
_PropertyItem(label: 'Focal Length', value: exif.mm != null ? '${exif.focalLength}mm' : null),
|
||||
_PropertyItem(label: 'ISO', value: exif.iso?.toString()),
|
||||
_PropertyItem(label: 'Exposure Time', value: exif.exposureTime.isNotEmpty ? exif.exposureTime : null),
|
||||
_PropertyItem(
|
||||
label: 'GPS Coordinates',
|
||||
value: exif.hasCoordinates ? '${exif.latitude}, ${exif.longitude}' : null,
|
||||
),
|
||||
_PropertyItem(
|
||||
label: 'Location',
|
||||
value: [exif.city, exif.state, exif.country].where((e) => e != null && e.isNotEmpty).join(', '),
|
||||
),
|
||||
]);
|
||||
}
|
||||
|
||||
String _getAssetTypeTitle(BaseAsset asset) {
|
||||
if (asset is LocalAsset) return 'Local Asset';
|
||||
if (asset is RemoteAsset) return 'Remote Asset';
|
||||
return 'Base Asset';
|
||||
}
|
||||
}
|
||||
|
||||
class _LocalAssetsSection extends ConsumerWidget {
|
||||
final BaseAsset asset;
|
||||
|
||||
const _LocalAssetsSection({required this.asset});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final assetService = ref.watch(assetServiceProvider);
|
||||
|
||||
return FutureBuilder<List<LocalAsset?>>(
|
||||
future: assetService.getLocalAssetsByChecksum(asset.checksum!),
|
||||
builder: (context, snapshot) {
|
||||
if (snapshot.connectionState == ConnectionState.waiting) {
|
||||
return const _PropertySectionCard(
|
||||
title: 'Local Assets',
|
||||
properties: [_PropertyItem(label: 'Status', value: 'Loading...')],
|
||||
);
|
||||
}
|
||||
|
||||
if (snapshot.hasError) {
|
||||
return _PropertySectionCard(
|
||||
title: 'Local Assets',
|
||||
properties: [_PropertyItem(label: 'Error', value: snapshot.error.toString())],
|
||||
);
|
||||
}
|
||||
|
||||
final localAssets = snapshot.data?.cast<LocalAsset>() ?? [];
|
||||
if (asset is LocalAsset) {
|
||||
localAssets.removeWhere((a) => a.id == (asset as LocalAsset).id);
|
||||
|
||||
if (localAssets.isEmpty) {
|
||||
return const SizedBox.shrink();
|
||||
}
|
||||
}
|
||||
|
||||
if (localAssets.isEmpty) {
|
||||
return const _PropertySectionCard(
|
||||
title: 'Local Assets',
|
||||
properties: [_PropertyItem(label: 'Status', value: 'No local assets found with this checksum')],
|
||||
);
|
||||
}
|
||||
|
||||
return Column(
|
||||
children: [
|
||||
if (localAssets.length > 1)
|
||||
_PropertySectionCard(
|
||||
title: 'Local Assets Summary',
|
||||
properties: [_PropertyItem(label: 'Total Count', value: localAssets.length.toString())],
|
||||
),
|
||||
...localAssets.map((localAsset) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.only(top: 16),
|
||||
child: _AssetPropertiesSection(asset: localAsset),
|
||||
);
|
||||
}),
|
||||
],
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _RemoteAssetSection extends ConsumerWidget {
|
||||
final BaseAsset asset;
|
||||
|
||||
const _RemoteAssetSection({required this.asset});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final assetService = ref.watch(assetServiceProvider);
|
||||
|
||||
if (asset is RemoteAsset) {
|
||||
return const SizedBox.shrink();
|
||||
}
|
||||
|
||||
return FutureBuilder<RemoteAsset?>(
|
||||
future: assetService.getRemoteAssetByChecksum(asset.checksum!),
|
||||
builder: (context, snapshot) {
|
||||
if (snapshot.connectionState == ConnectionState.waiting) {
|
||||
return const _PropertySectionCard(
|
||||
title: 'Remote Assets',
|
||||
properties: [_PropertyItem(label: 'Status', value: 'Loading...')],
|
||||
);
|
||||
}
|
||||
|
||||
if (snapshot.hasError) {
|
||||
return _PropertySectionCard(
|
||||
title: 'Remote Assets',
|
||||
properties: [_PropertyItem(label: 'Error', value: snapshot.error.toString())],
|
||||
);
|
||||
}
|
||||
|
||||
final remoteAsset = snapshot.data;
|
||||
|
||||
if (remoteAsset == null) {
|
||||
return const _PropertySectionCard(
|
||||
title: 'Remote Assets',
|
||||
properties: [_PropertyItem(label: 'Status', value: 'No remote asset found with this checksum')],
|
||||
);
|
||||
}
|
||||
|
||||
return _AssetPropertiesSection(asset: remoteAsset);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _PropertySectionCard extends StatelessWidget {
|
||||
final String title;
|
||||
final List<_PropertyItem> properties;
|
||||
|
||||
const _PropertySectionCard({required this.title, required this.properties});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Card(
|
||||
margin: const EdgeInsets.symmetric(vertical: 8),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(12),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(title, style: Theme.of(context).textTheme.titleMedium?.copyWith(fontWeight: FontWeight.bold)),
|
||||
const SizedBox(height: 8),
|
||||
...properties,
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _PropertyItem extends StatelessWidget {
|
||||
final String label;
|
||||
final String? value;
|
||||
|
||||
const _PropertyItem({required this.label, this.value});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 4),
|
||||
child: Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
SizedBox(
|
||||
width: 120,
|
||||
child: Text('$label:', style: const TextStyle(fontWeight: FontWeight.w500)),
|
||||
),
|
||||
Expanded(
|
||||
child: Text(value ?? 'N/A', style: TextStyle(color: Theme.of(context).colorScheme.secondary)),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
@ -0,0 +1,30 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:immich_mobile/constants/enums.dart';
|
||||
import 'package:immich_mobile/extensions/translate_extensions.dart';
|
||||
import 'package:immich_mobile/presentation/widgets/action_buttons/base_action_button.widget.dart';
|
||||
import 'package:immich_mobile/providers/infrastructure/action.provider.dart';
|
||||
|
||||
class AdvancedInfoActionButton extends ConsumerWidget {
|
||||
final ActionSource source;
|
||||
|
||||
const AdvancedInfoActionButton({super.key, required this.source});
|
||||
|
||||
void _onTap(BuildContext context, WidgetRef ref) async {
|
||||
if (!context.mounted) {
|
||||
return;
|
||||
}
|
||||
|
||||
ref.read(actionProvider.notifier).troubleshoot(source, context);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
return BaseActionButton(
|
||||
maxWidth: 115.0,
|
||||
iconData: Icons.help_outline_rounded,
|
||||
label: "troubleshoot".t(context: context),
|
||||
onPressed: () => _onTap(context, ref),
|
||||
);
|
||||
}
|
||||
}
|
@ -5,6 +5,7 @@ import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:immich_mobile/constants/enums.dart';
|
||||
import 'package:immich_mobile/domain/models/asset/base_asset.model.dart';
|
||||
import 'package:immich_mobile/domain/models/exif.model.dart';
|
||||
import 'package:immich_mobile/domain/models/setting.model.dart';
|
||||
import 'package:immich_mobile/extensions/build_context_extensions.dart';
|
||||
import 'package:immich_mobile/extensions/translate_extensions.dart';
|
||||
import 'package:immich_mobile/presentation/widgets/asset_viewer/bottom_sheet/sheet_location_details.widget.dart';
|
||||
@ -14,6 +15,7 @@ import 'package:immich_mobile/providers/haptic_feedback.provider.dart';
|
||||
import 'package:immich_mobile/providers/infrastructure/action.provider.dart';
|
||||
import 'package:immich_mobile/providers/infrastructure/asset_viewer/current_asset.provider.dart';
|
||||
import 'package:immich_mobile/providers/infrastructure/current_album.provider.dart';
|
||||
import 'package:immich_mobile/providers/infrastructure/setting.provider.dart';
|
||||
import 'package:immich_mobile/providers/routes.provider.dart';
|
||||
import 'package:immich_mobile/providers/server_info.provider.dart';
|
||||
import 'package:immich_mobile/providers/user.provider.dart';
|
||||
@ -41,6 +43,7 @@ class AssetDetailBottomSheet extends ConsumerWidget {
|
||||
final isInLockedView = ref.watch(inLockedViewProvider);
|
||||
final currentAlbum = ref.watch(currentRemoteAlbumProvider);
|
||||
final isArchived = asset is RemoteAsset && asset.visibility == AssetVisibility.archive;
|
||||
final advancedTroubleshooting = ref.watch(settingsProvider.notifier).get(Setting.advancedTroubleshooting);
|
||||
|
||||
final buttonContext = ActionButtonContext(
|
||||
asset: asset,
|
||||
@ -49,6 +52,7 @@ class AssetDetailBottomSheet extends ConsumerWidget {
|
||||
isTrashEnabled: isTrashEnable,
|
||||
isInLockedView: isInLockedView,
|
||||
currentAlbum: currentAlbum,
|
||||
advancedTroubleshooting: advancedTroubleshooting,
|
||||
source: ActionSource.viewer,
|
||||
);
|
||||
|
||||
@ -122,6 +126,10 @@ class _AssetDetailBottomSheet extends ConsumerWidget {
|
||||
return [fNumber, exposureTime, focalLength, iso].where((spec) => spec != null && spec.isNotEmpty).join(_kSeparator);
|
||||
}
|
||||
|
||||
Future<void> _editDateTime(BuildContext context, WidgetRef ref) async {
|
||||
await ref.read(actionProvider.notifier).editDateTime(ActionSource.viewer, context);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final asset = ref.watch(currentAssetNotifier);
|
||||
@ -132,10 +140,6 @@ class _AssetDetailBottomSheet extends ConsumerWidget {
|
||||
final exifInfo = ref.watch(currentAssetExifProvider).valueOrNull;
|
||||
final cameraTitle = _getCameraInfoTitle(exifInfo);
|
||||
|
||||
Future<void> editDateTime() async {
|
||||
await ref.read(actionProvider.notifier).editDateTime(ActionSource.viewer, context);
|
||||
}
|
||||
|
||||
return SliverList.list(
|
||||
children: [
|
||||
// Asset Date and Time
|
||||
@ -143,7 +147,7 @@ class _AssetDetailBottomSheet extends ConsumerWidget {
|
||||
title: _getDateTime(context, asset),
|
||||
titleStyle: context.textTheme.bodyMedium?.copyWith(fontWeight: FontWeight.w600),
|
||||
trailing: asset.hasRemote ? const Icon(Icons.edit, size: 18) : null,
|
||||
onTap: asset.hasRemote ? () async => await editDateTime() : null,
|
||||
onTap: asset.hasRemote ? () async => await _editDateTime(context, ref) : null,
|
||||
),
|
||||
if (exifInfo != null) _SheetAssetDescription(exif: exifInfo),
|
||||
const SheetPeopleDetails(),
|
||||
|
@ -1,8 +1,8 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:immich_mobile/constants/enums.dart';
|
||||
import 'package:immich_mobile/presentation/widgets/action_buttons/delete_permanent_action_button.widget.dart';
|
||||
import 'package:immich_mobile/presentation/widgets/action_buttons/delete_local_action_button.widget.dart';
|
||||
import 'package:immich_mobile/presentation/widgets/action_buttons/delete_permanent_action_button.widget.dart';
|
||||
import 'package:immich_mobile/presentation/widgets/action_buttons/download_action_button.widget.dart';
|
||||
import 'package:immich_mobile/presentation/widgets/action_buttons/edit_date_time_action_button.widget.dart';
|
||||
import 'package:immich_mobile/presentation/widgets/action_buttons/edit_location_action_button.widget.dart';
|
||||
|
@ -4,6 +4,8 @@ import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:immich_mobile/constants/enums.dart';
|
||||
import 'package:immich_mobile/domain/models/album/album.model.dart';
|
||||
import 'package:immich_mobile/domain/models/asset/base_asset.model.dart';
|
||||
import 'package:immich_mobile/domain/models/setting.model.dart';
|
||||
import 'package:immich_mobile/presentation/widgets/action_buttons/advanced_info_action_button.widget.dart';
|
||||
import 'package:immich_mobile/presentation/widgets/action_buttons/archive_action_button.widget.dart';
|
||||
import 'package:immich_mobile/presentation/widgets/action_buttons/delete_action_button.widget.dart';
|
||||
import 'package:immich_mobile/presentation/widgets/action_buttons/delete_local_action_button.widget.dart';
|
||||
@ -21,6 +23,7 @@ import 'package:immich_mobile/presentation/widgets/action_buttons/upload_action_
|
||||
import 'package:immich_mobile/presentation/widgets/album/album_selector.widget.dart';
|
||||
import 'package:immich_mobile/presentation/widgets/bottom_sheet/base_bottom_sheet.widget.dart';
|
||||
import 'package:immich_mobile/providers/infrastructure/album.provider.dart';
|
||||
import 'package:immich_mobile/providers/infrastructure/setting.provider.dart';
|
||||
import 'package:immich_mobile/providers/server_info.provider.dart';
|
||||
import 'package:immich_mobile/providers/timeline/multiselect.provider.dart';
|
||||
import 'package:immich_mobile/widgets/common/immich_toast.dart';
|
||||
@ -51,6 +54,7 @@ class _GeneralBottomSheetState extends ConsumerState<GeneralBottomSheet> {
|
||||
Widget build(BuildContext context) {
|
||||
final multiselect = ref.watch(multiSelectProvider);
|
||||
final isTrashEnable = ref.watch(serverInfoProvider.select((state) => state.serverFeatures.trash));
|
||||
final advancedTroubleshooting = ref.watch(settingsProvider.notifier).get(Setting.advancedTroubleshooting);
|
||||
|
||||
Future<void> addAssetsToAlbum(RemoteAlbum album) async {
|
||||
final selectedAssets = multiselect.selectedAssets;
|
||||
@ -88,6 +92,9 @@ class _GeneralBottomSheetState extends ConsumerState<GeneralBottomSheet> {
|
||||
maxChildSize: 0.85,
|
||||
shouldCloseOnMinExtent: false,
|
||||
actions: [
|
||||
if (multiselect.selectedAssets.length == 1 && advancedTroubleshooting) ...[
|
||||
const AdvancedInfoActionButton(source: ActionSource.timeline),
|
||||
],
|
||||
const ShareActionButton(source: ActionSource.timeline),
|
||||
if (multiselect.hasRemote) ...[
|
||||
const ShareLinkActionButton(source: ActionSource.timeline),
|
||||
|
@ -6,11 +6,11 @@ import 'package:background_downloader/background_downloader.dart';
|
||||
import 'package:collection/collection.dart';
|
||||
import 'package:flutter/widgets.dart';
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
|
||||
import 'package:immich_mobile/constants/constants.dart';
|
||||
import 'package:immich_mobile/domain/models/album/local_album.model.dart';
|
||||
import 'package:immich_mobile/domain/models/asset/base_asset.model.dart';
|
||||
import 'package:immich_mobile/infrastructure/repositories/backup.repository.dart';
|
||||
import 'package:immich_mobile/providers/infrastructure/asset.provider.dart';
|
||||
import 'package:immich_mobile/providers/user.provider.dart';
|
||||
import 'package:immich_mobile/services/upload.service.dart';
|
||||
import 'package:logging/logging.dart';
|
||||
@ -380,5 +380,5 @@ final driftCandidateBackupAlbumInfoProvider = FutureProvider.autoDispose.family<
|
||||
ref,
|
||||
assetId,
|
||||
) {
|
||||
return ref.read(backupRepositoryProvider).getSourceAlbums(assetId);
|
||||
return ref.read(localAssetRepository).getSourceAlbums(assetId, backupSelection: BackupSelection.selected);
|
||||
});
|
||||
|
@ -1,3 +1,4 @@
|
||||
import 'package:auto_route/auto_route.dart';
|
||||
import 'package:background_downloader/background_downloader.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:immich_mobile/constants/enums.dart';
|
||||
@ -6,6 +7,7 @@ import 'package:immich_mobile/models/download/livephotos_medatada.model.dart';
|
||||
import 'package:immich_mobile/providers/infrastructure/asset_viewer/current_asset.provider.dart';
|
||||
import 'package:immich_mobile/providers/timeline/multiselect.provider.dart';
|
||||
import 'package:immich_mobile/providers/user.provider.dart';
|
||||
import 'package:immich_mobile/routing/router.dart';
|
||||
import 'package:immich_mobile/services/action.service.dart';
|
||||
import 'package:immich_mobile/services/download.service.dart';
|
||||
import 'package:immich_mobile/services/timeline.service.dart';
|
||||
@ -115,6 +117,16 @@ class ActionNotifier extends Notifier<void> {
|
||||
};
|
||||
}
|
||||
|
||||
Future<ActionResult> troubleshoot(ActionSource source, BuildContext context) async {
|
||||
final assets = _getAssets(source);
|
||||
if (assets.length > 1) {
|
||||
return ActionResult(count: assets.length, success: false, error: 'Cannot troubleshoot multiple assets');
|
||||
}
|
||||
context.pushRoute(AssetTroubleshootRoute(asset: assets.first));
|
||||
|
||||
return ActionResult(count: assets.length, success: true);
|
||||
}
|
||||
|
||||
Future<ActionResult> shareLink(ActionSource source, BuildContext context) async {
|
||||
final ids = _getRemoteIdsForSource(source);
|
||||
try {
|
||||
|
@ -86,6 +86,7 @@ import 'package:immich_mobile/presentation/pages/drift_album.page.dart';
|
||||
import 'package:immich_mobile/presentation/pages/drift_album_options.page.dart';
|
||||
import 'package:immich_mobile/presentation/pages/drift_archive.page.dart';
|
||||
import 'package:immich_mobile/presentation/pages/drift_asset_selection_timeline.page.dart';
|
||||
import 'package:immich_mobile/presentation/pages/drift_asset_troubleshoot.page.dart';
|
||||
import 'package:immich_mobile/presentation/pages/drift_create_album.page.dart';
|
||||
import 'package:immich_mobile/presentation/pages/drift_favorite.page.dart';
|
||||
import 'package:immich_mobile/presentation/pages/drift_library.page.dart';
|
||||
@ -343,6 +344,7 @@ class AppRouter extends RootStackRouter {
|
||||
AutoRoute(page: DriftFilterImageRoute.page),
|
||||
AutoRoute(page: DriftActivitiesRoute.page, guards: [_authGuard, _duplicateGuard]),
|
||||
AutoRoute(page: DriftBackupAssetDetailRoute.page, guards: [_authGuard, _duplicateGuard]),
|
||||
AutoRoute(page: AssetTroubleshootRoute.page, guards: [_authGuard, _duplicateGuard]),
|
||||
// required to handle all deeplinks in deep_link.service.dart
|
||||
// auto_route_library#1722
|
||||
RedirectRoute(path: '*', redirectTo: '/'),
|
||||
|
@ -403,6 +403,43 @@ class ArchiveRoute extends PageRouteInfo<void> {
|
||||
);
|
||||
}
|
||||
|
||||
/// generated route for
|
||||
/// [AssetTroubleshootPage]
|
||||
class AssetTroubleshootRoute extends PageRouteInfo<AssetTroubleshootRouteArgs> {
|
||||
AssetTroubleshootRoute({
|
||||
Key? key,
|
||||
required BaseAsset asset,
|
||||
List<PageRouteInfo>? children,
|
||||
}) : super(
|
||||
AssetTroubleshootRoute.name,
|
||||
args: AssetTroubleshootRouteArgs(key: key, asset: asset),
|
||||
initialChildren: children,
|
||||
);
|
||||
|
||||
static const String name = 'AssetTroubleshootRoute';
|
||||
|
||||
static PageInfo page = PageInfo(
|
||||
name,
|
||||
builder: (data) {
|
||||
final args = data.argsAs<AssetTroubleshootRouteArgs>();
|
||||
return AssetTroubleshootPage(key: args.key, asset: args.asset);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
class AssetTroubleshootRouteArgs {
|
||||
const AssetTroubleshootRouteArgs({this.key, required this.asset});
|
||||
|
||||
final Key? key;
|
||||
|
||||
final BaseAsset asset;
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'AssetTroubleshootRouteArgs{key: $key, asset: $asset}';
|
||||
}
|
||||
}
|
||||
|
||||
/// generated route for
|
||||
/// [AssetViewerPage]
|
||||
class AssetViewerRoute extends PageRouteInfo<AssetViewerRouteArgs> {
|
||||
|
@ -1,6 +1,8 @@
|
||||
import 'package:flutter/widgets.dart';
|
||||
import 'package:immich_mobile/constants/enums.dart';
|
||||
import 'package:immich_mobile/domain/models/album/album.model.dart';
|
||||
import 'package:immich_mobile/domain/models/asset/base_asset.model.dart';
|
||||
import 'package:immich_mobile/presentation/widgets/action_buttons/advanced_info_action_button.widget.dart';
|
||||
import 'package:immich_mobile/presentation/widgets/action_buttons/archive_action_button.widget.dart';
|
||||
import 'package:immich_mobile/presentation/widgets/action_buttons/delete_action_button.widget.dart';
|
||||
import 'package:immich_mobile/presentation/widgets/action_buttons/delete_local_action_button.widget.dart';
|
||||
@ -15,7 +17,6 @@ import 'package:immich_mobile/presentation/widgets/action_buttons/share_link_act
|
||||
import 'package:immich_mobile/presentation/widgets/action_buttons/trash_action_button.widget.dart';
|
||||
import 'package:immich_mobile/presentation/widgets/action_buttons/unarchive_action_button.widget.dart';
|
||||
import 'package:immich_mobile/presentation/widgets/action_buttons/upload_action_button.widget.dart';
|
||||
import 'package:immich_mobile/domain/models/asset/base_asset.model.dart';
|
||||
|
||||
class ActionButtonContext {
|
||||
final BaseAsset asset;
|
||||
@ -24,6 +25,7 @@ class ActionButtonContext {
|
||||
final bool isTrashEnabled;
|
||||
final bool isInLockedView;
|
||||
final RemoteAlbum? currentAlbum;
|
||||
final bool advancedTroubleshooting;
|
||||
final ActionSource source;
|
||||
|
||||
const ActionButtonContext({
|
||||
@ -33,11 +35,13 @@ class ActionButtonContext {
|
||||
required this.isTrashEnabled,
|
||||
required this.isInLockedView,
|
||||
required this.currentAlbum,
|
||||
required this.advancedTroubleshooting,
|
||||
required this.source,
|
||||
});
|
||||
}
|
||||
|
||||
enum ActionButtonType {
|
||||
advancedInfo,
|
||||
share,
|
||||
shareLink,
|
||||
archive,
|
||||
@ -55,6 +59,7 @@ enum ActionButtonType {
|
||||
|
||||
bool shouldShow(ActionButtonContext context) {
|
||||
return switch (this) {
|
||||
ActionButtonType.advancedInfo => context.advancedTroubleshooting,
|
||||
ActionButtonType.share => true,
|
||||
ActionButtonType.shareLink =>
|
||||
!context.isInLockedView && //
|
||||
@ -115,6 +120,7 @@ enum ActionButtonType {
|
||||
|
||||
Widget buildButton(ActionButtonContext context) {
|
||||
return switch (this) {
|
||||
ActionButtonType.advancedInfo => AdvancedInfoActionButton(source: context.source),
|
||||
ActionButtonType.share => ShareActionButton(source: context.source),
|
||||
ActionButtonType.shareLink => ShareLinkActionButton(source: context.source),
|
||||
ActionButtonType.archive => ArchiveActionButton(source: context.source),
|
||||
@ -138,6 +144,7 @@ enum ActionButtonType {
|
||||
|
||||
class ActionButtonBuilder {
|
||||
static const List<ActionButtonType> _actionTypes = [
|
||||
ActionButtonType.advancedInfo,
|
||||
ActionButtonType.share,
|
||||
ActionButtonType.shareLink,
|
||||
ActionButtonType.likeActivity,
|
||||
|
@ -81,6 +81,7 @@ void main() {
|
||||
isTrashEnabled: true,
|
||||
isInLockedView: false,
|
||||
currentAlbum: null,
|
||||
advancedTroubleshooting: false,
|
||||
source: ActionSource.timeline,
|
||||
);
|
||||
|
||||
@ -110,6 +111,7 @@ void main() {
|
||||
isTrashEnabled: true,
|
||||
isInLockedView: false,
|
||||
currentAlbum: null,
|
||||
advancedTroubleshooting: false,
|
||||
source: ActionSource.timeline,
|
||||
);
|
||||
|
||||
@ -124,6 +126,7 @@ void main() {
|
||||
isTrashEnabled: true,
|
||||
isInLockedView: true,
|
||||
currentAlbum: null,
|
||||
advancedTroubleshooting: false,
|
||||
source: ActionSource.timeline,
|
||||
);
|
||||
|
||||
@ -141,6 +144,7 @@ void main() {
|
||||
isTrashEnabled: true,
|
||||
isInLockedView: false,
|
||||
currentAlbum: null,
|
||||
advancedTroubleshooting: false,
|
||||
source: ActionSource.timeline,
|
||||
);
|
||||
|
||||
@ -156,6 +160,7 @@ void main() {
|
||||
isTrashEnabled: true,
|
||||
isInLockedView: true,
|
||||
currentAlbum: null,
|
||||
advancedTroubleshooting: false,
|
||||
source: ActionSource.timeline,
|
||||
);
|
||||
|
||||
@ -171,6 +176,7 @@ void main() {
|
||||
isTrashEnabled: true,
|
||||
isInLockedView: false,
|
||||
currentAlbum: null,
|
||||
advancedTroubleshooting: false,
|
||||
source: ActionSource.timeline,
|
||||
);
|
||||
|
||||
@ -188,6 +194,7 @@ void main() {
|
||||
isTrashEnabled: true,
|
||||
isInLockedView: false,
|
||||
currentAlbum: null,
|
||||
advancedTroubleshooting: false,
|
||||
source: ActionSource.timeline,
|
||||
);
|
||||
|
||||
@ -203,6 +210,7 @@ void main() {
|
||||
isTrashEnabled: true,
|
||||
isInLockedView: false,
|
||||
currentAlbum: null,
|
||||
advancedTroubleshooting: false,
|
||||
source: ActionSource.timeline,
|
||||
);
|
||||
|
||||
@ -218,6 +226,7 @@ void main() {
|
||||
isTrashEnabled: true,
|
||||
isInLockedView: true,
|
||||
currentAlbum: null,
|
||||
advancedTroubleshooting: false,
|
||||
source: ActionSource.timeline,
|
||||
);
|
||||
|
||||
@ -233,6 +242,7 @@ void main() {
|
||||
isTrashEnabled: true,
|
||||
isInLockedView: false,
|
||||
currentAlbum: null,
|
||||
advancedTroubleshooting: false,
|
||||
source: ActionSource.timeline,
|
||||
);
|
||||
|
||||
@ -248,6 +258,7 @@ void main() {
|
||||
isTrashEnabled: true,
|
||||
isInLockedView: false,
|
||||
currentAlbum: null,
|
||||
advancedTroubleshooting: false,
|
||||
source: ActionSource.timeline,
|
||||
);
|
||||
|
||||
@ -265,6 +276,7 @@ void main() {
|
||||
isTrashEnabled: true,
|
||||
isInLockedView: false,
|
||||
currentAlbum: null,
|
||||
advancedTroubleshooting: false,
|
||||
source: ActionSource.timeline,
|
||||
);
|
||||
|
||||
@ -280,6 +292,7 @@ void main() {
|
||||
isTrashEnabled: true,
|
||||
isInLockedView: false,
|
||||
currentAlbum: null,
|
||||
advancedTroubleshooting: false,
|
||||
source: ActionSource.timeline,
|
||||
);
|
||||
|
||||
@ -295,6 +308,7 @@ void main() {
|
||||
isTrashEnabled: true,
|
||||
isInLockedView: false,
|
||||
currentAlbum: null,
|
||||
advancedTroubleshooting: false,
|
||||
source: ActionSource.timeline,
|
||||
);
|
||||
|
||||
@ -312,6 +326,7 @@ void main() {
|
||||
isTrashEnabled: true,
|
||||
isInLockedView: false,
|
||||
currentAlbum: null,
|
||||
advancedTroubleshooting: false,
|
||||
source: ActionSource.timeline,
|
||||
);
|
||||
|
||||
@ -327,6 +342,7 @@ void main() {
|
||||
isTrashEnabled: true,
|
||||
isInLockedView: false,
|
||||
currentAlbum: null,
|
||||
advancedTroubleshooting: false,
|
||||
source: ActionSource.timeline,
|
||||
);
|
||||
|
||||
@ -342,6 +358,7 @@ void main() {
|
||||
isTrashEnabled: true,
|
||||
isInLockedView: true,
|
||||
currentAlbum: null,
|
||||
advancedTroubleshooting: false,
|
||||
source: ActionSource.timeline,
|
||||
);
|
||||
|
||||
@ -359,6 +376,7 @@ void main() {
|
||||
isTrashEnabled: true,
|
||||
isInLockedView: false,
|
||||
currentAlbum: null,
|
||||
advancedTroubleshooting: false,
|
||||
source: ActionSource.timeline,
|
||||
);
|
||||
|
||||
@ -374,6 +392,7 @@ void main() {
|
||||
isTrashEnabled: false,
|
||||
isInLockedView: false,
|
||||
currentAlbum: null,
|
||||
advancedTroubleshooting: false,
|
||||
source: ActionSource.timeline,
|
||||
);
|
||||
|
||||
@ -391,6 +410,7 @@ void main() {
|
||||
isTrashEnabled: false,
|
||||
isInLockedView: false,
|
||||
currentAlbum: null,
|
||||
advancedTroubleshooting: false,
|
||||
source: ActionSource.timeline,
|
||||
);
|
||||
|
||||
@ -406,6 +426,7 @@ void main() {
|
||||
isTrashEnabled: true,
|
||||
isInLockedView: false,
|
||||
currentAlbum: null,
|
||||
advancedTroubleshooting: false,
|
||||
source: ActionSource.timeline,
|
||||
);
|
||||
|
||||
@ -423,6 +444,7 @@ void main() {
|
||||
isTrashEnabled: true,
|
||||
isInLockedView: false,
|
||||
currentAlbum: null,
|
||||
advancedTroubleshooting: false,
|
||||
source: ActionSource.timeline,
|
||||
);
|
||||
|
||||
@ -440,6 +462,7 @@ void main() {
|
||||
isTrashEnabled: true,
|
||||
isInLockedView: false,
|
||||
currentAlbum: null,
|
||||
advancedTroubleshooting: false,
|
||||
source: ActionSource.timeline,
|
||||
);
|
||||
|
||||
@ -457,6 +480,7 @@ void main() {
|
||||
isTrashEnabled: true,
|
||||
isInLockedView: false,
|
||||
currentAlbum: null,
|
||||
advancedTroubleshooting: false,
|
||||
source: ActionSource.timeline,
|
||||
);
|
||||
|
||||
@ -472,6 +496,7 @@ void main() {
|
||||
isTrashEnabled: true,
|
||||
isInLockedView: false,
|
||||
currentAlbum: null,
|
||||
advancedTroubleshooting: false,
|
||||
source: ActionSource.timeline,
|
||||
);
|
||||
|
||||
@ -489,6 +514,7 @@ void main() {
|
||||
isTrashEnabled: true,
|
||||
isInLockedView: false,
|
||||
currentAlbum: null,
|
||||
advancedTroubleshooting: false,
|
||||
source: ActionSource.timeline,
|
||||
);
|
||||
|
||||
@ -506,6 +532,7 @@ void main() {
|
||||
isTrashEnabled: true,
|
||||
isInLockedView: false,
|
||||
currentAlbum: album,
|
||||
advancedTroubleshooting: false,
|
||||
source: ActionSource.timeline,
|
||||
);
|
||||
|
||||
@ -520,6 +547,7 @@ void main() {
|
||||
isTrashEnabled: true,
|
||||
isInLockedView: false,
|
||||
currentAlbum: null,
|
||||
advancedTroubleshooting: false,
|
||||
source: ActionSource.timeline,
|
||||
);
|
||||
|
||||
@ -537,6 +565,7 @@ void main() {
|
||||
isTrashEnabled: true,
|
||||
isInLockedView: false,
|
||||
currentAlbum: album,
|
||||
advancedTroubleshooting: false,
|
||||
source: ActionSource.timeline,
|
||||
);
|
||||
|
||||
@ -552,6 +581,7 @@ void main() {
|
||||
isTrashEnabled: true,
|
||||
isInLockedView: false,
|
||||
currentAlbum: album,
|
||||
advancedTroubleshooting: false,
|
||||
source: ActionSource.timeline,
|
||||
);
|
||||
|
||||
@ -567,6 +597,7 @@ void main() {
|
||||
isTrashEnabled: true,
|
||||
isInLockedView: false,
|
||||
currentAlbum: album,
|
||||
advancedTroubleshooting: false,
|
||||
source: ActionSource.timeline,
|
||||
);
|
||||
|
||||
@ -581,12 +612,45 @@ void main() {
|
||||
isTrashEnabled: true,
|
||||
isInLockedView: false,
|
||||
currentAlbum: null,
|
||||
advancedTroubleshooting: false,
|
||||
source: ActionSource.timeline,
|
||||
);
|
||||
|
||||
expect(ActionButtonType.likeActivity.shouldShow(context), isFalse);
|
||||
});
|
||||
});
|
||||
|
||||
group('advancedTroubleshooting button', () {
|
||||
test('should show when in advanced troubleshooting mode', () {
|
||||
final context = ActionButtonContext(
|
||||
asset: mergedAsset,
|
||||
isOwner: true,
|
||||
isArchived: false,
|
||||
isTrashEnabled: true,
|
||||
isInLockedView: false,
|
||||
currentAlbum: null,
|
||||
advancedTroubleshooting: true,
|
||||
source: ActionSource.timeline,
|
||||
);
|
||||
|
||||
expect(ActionButtonType.advancedInfo.shouldShow(context), isTrue);
|
||||
});
|
||||
|
||||
test('should not show when not in advanced troubleshooting mode', () {
|
||||
final context = ActionButtonContext(
|
||||
asset: mergedAsset,
|
||||
isOwner: true,
|
||||
isArchived: false,
|
||||
isTrashEnabled: true,
|
||||
isInLockedView: false,
|
||||
currentAlbum: null,
|
||||
advancedTroubleshooting: false,
|
||||
source: ActionSource.timeline,
|
||||
);
|
||||
|
||||
expect(ActionButtonType.advancedInfo.shouldShow(context), isFalse);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
group('ActionButtonType.buildButton', () {
|
||||
@ -602,6 +666,7 @@ void main() {
|
||||
isTrashEnabled: true,
|
||||
isInLockedView: false,
|
||||
currentAlbum: null,
|
||||
advancedTroubleshooting: false,
|
||||
source: ActionSource.timeline,
|
||||
);
|
||||
});
|
||||
@ -617,6 +682,7 @@ void main() {
|
||||
isTrashEnabled: true,
|
||||
isInLockedView: false,
|
||||
currentAlbum: album,
|
||||
advancedTroubleshooting: false,
|
||||
source: ActionSource.timeline,
|
||||
);
|
||||
final widget = buttonType.buildButton(contextWithAlbum);
|
||||
@ -639,6 +705,7 @@ void main() {
|
||||
isTrashEnabled: true,
|
||||
isInLockedView: false,
|
||||
currentAlbum: null,
|
||||
advancedTroubleshooting: false,
|
||||
source: ActionSource.timeline,
|
||||
);
|
||||
|
||||
@ -658,6 +725,7 @@ void main() {
|
||||
isTrashEnabled: true,
|
||||
isInLockedView: false,
|
||||
currentAlbum: album,
|
||||
advancedTroubleshooting: false,
|
||||
source: ActionSource.timeline,
|
||||
);
|
||||
|
||||
@ -675,6 +743,7 @@ void main() {
|
||||
isTrashEnabled: true,
|
||||
isInLockedView: false,
|
||||
currentAlbum: null,
|
||||
advancedTroubleshooting: false,
|
||||
source: ActionSource.timeline,
|
||||
);
|
||||
|
||||
@ -693,6 +762,7 @@ void main() {
|
||||
isTrashEnabled: true,
|
||||
isInLockedView: false,
|
||||
currentAlbum: null,
|
||||
advancedTroubleshooting: false,
|
||||
source: ActionSource.timeline,
|
||||
);
|
||||
|
||||
@ -705,6 +775,7 @@ void main() {
|
||||
isTrashEnabled: true,
|
||||
isInLockedView: false,
|
||||
currentAlbum: null,
|
||||
advancedTroubleshooting: false,
|
||||
source: ActionSource.timeline,
|
||||
);
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user