From 0bc773fd0094874f5b135c67339f7654ac150014 Mon Sep 17 00:00:00 2001 From: Alex Date: Tue, 19 Mar 2024 08:40:14 -0500 Subject: [PATCH] refactor(mobile): backup album selection (#8053) * feat(mobile): include album with 0 assets as album option for backup * Show icon instead of thumbnail * Handle backupProgress state transition correctly to always load the backup info * remove todo comment --- .../AppIcon.appiconset/Contents.json | 355 +++++++++++++++++- .../backup/models/available_album.model.dart | 5 +- .../backup/providers/backup.provider.dart | 28 +- .../modules/backup/ui/album_info_card.dart | 41 +- .../backup/ui/album_info_list_tile.dart | 93 ++--- .../views/backup_album_selection_page.dart | 8 +- .../backup/views/backup_controller_page.dart | 33 +- 7 files changed, 437 insertions(+), 126 deletions(-) diff --git a/mobile/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json b/mobile/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json index ffab2548e6..6bf81eb643 100644 --- a/mobile/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json +++ b/mobile/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -1 +1,354 @@ -{"images":[{"size":"60x60","expected-size":"180","filename":"180.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"iphone","scale":"3x"},{"size":"40x40","expected-size":"80","filename":"80.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"iphone","scale":"2x"},{"size":"40x40","expected-size":"120","filename":"120.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"iphone","scale":"3x"},{"size":"60x60","expected-size":"120","filename":"120.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"iphone","scale":"2x"},{"size":"57x57","expected-size":"57","filename":"57.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"iphone","scale":"1x"},{"size":"29x29","expected-size":"58","filename":"58.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"iphone","scale":"2x"},{"size":"29x29","expected-size":"29","filename":"29.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"iphone","scale":"1x"},{"size":"29x29","expected-size":"87","filename":"87.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"iphone","scale":"3x"},{"size":"57x57","expected-size":"114","filename":"114.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"iphone","scale":"2x"},{"size":"20x20","expected-size":"40","filename":"40.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"iphone","scale":"2x"},{"size":"20x20","expected-size":"60","filename":"60.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"iphone","scale":"3x"},{"size":"1024x1024","filename":"1024.png","expected-size":"1024","idiom":"ios-marketing","folder":"Assets.xcassets/AppIcon.appiconset/","scale":"1x"},{"size":"40x40","expected-size":"80","filename":"80.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"ipad","scale":"2x"},{"size":"72x72","expected-size":"72","filename":"72.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"ipad","scale":"1x"},{"size":"76x76","expected-size":"152","filename":"152.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"ipad","scale":"2x"},{"size":"50x50","expected-size":"100","filename":"100.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"ipad","scale":"2x"},{"size":"29x29","expected-size":"58","filename":"58.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"ipad","scale":"2x"},{"size":"76x76","expected-size":"76","filename":"76.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"ipad","scale":"1x"},{"size":"29x29","expected-size":"29","filename":"29.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"ipad","scale":"1x"},{"size":"50x50","expected-size":"50","filename":"50.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"ipad","scale":"1x"},{"size":"72x72","expected-size":"144","filename":"144.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"ipad","scale":"2x"},{"size":"40x40","expected-size":"40","filename":"40.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"ipad","scale":"1x"},{"size":"83.5x83.5","expected-size":"167","filename":"167.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"ipad","scale":"2x"},{"size":"20x20","expected-size":"20","filename":"20.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"ipad","scale":"1x"},{"size":"20x20","expected-size":"40","filename":"40.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"ipad","scale":"2x"},{"idiom":"watch","filename":"172.png","folder":"Assets.xcassets/AppIcon.appiconset/","subtype":"38mm","scale":"2x","size":"86x86","expected-size":"172","role":"quickLook"},{"idiom":"watch","filename":"80.png","folder":"Assets.xcassets/AppIcon.appiconset/","subtype":"38mm","scale":"2x","size":"40x40","expected-size":"80","role":"appLauncher"},{"idiom":"watch","filename":"88.png","folder":"Assets.xcassets/AppIcon.appiconset/","subtype":"40mm","scale":"2x","size":"44x44","expected-size":"88","role":"appLauncher"},{"idiom":"watch","filename":"102.png","folder":"Assets.xcassets/AppIcon.appiconset/","subtype":"41mm","scale":"2x","size":"45x45","expected-size":"102","role":"appLauncher"},{"idiom":"watch","filename":"92.png","folder":"Assets.xcassets/AppIcon.appiconset/","subtype":"41mm","scale":"2x","size":"46x46","expected-size":"92","role":"appLauncher"},{"idiom":"watch","filename":"100.png","folder":"Assets.xcassets/AppIcon.appiconset/","subtype":"44mm","scale":"2x","size":"50x50","expected-size":"100","role":"appLauncher"},{"idiom":"watch","filename":"196.png","folder":"Assets.xcassets/AppIcon.appiconset/","subtype":"42mm","scale":"2x","size":"98x98","expected-size":"196","role":"quickLook"},{"idiom":"watch","filename":"216.png","folder":"Assets.xcassets/AppIcon.appiconset/","subtype":"44mm","scale":"2x","size":"108x108","expected-size":"216","role":"quickLook"},{"idiom":"watch","filename":"48.png","folder":"Assets.xcassets/AppIcon.appiconset/","subtype":"38mm","scale":"2x","size":"24x24","expected-size":"48","role":"notificationCenter"},{"idiom":"watch","filename":"55.png","folder":"Assets.xcassets/AppIcon.appiconset/","subtype":"42mm","scale":"2x","size":"27.5x27.5","expected-size":"55","role":"notificationCenter"},{"idiom":"watch","filename":"66.png","folder":"Assets.xcassets/AppIcon.appiconset/","subtype":"45mm","scale":"2x","size":"33x33","expected-size":"66","role":"notificationCenter"},{"size":"29x29","expected-size":"87","filename":"87.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"watch","role":"companionSettings","scale":"3x"},{"size":"29x29","expected-size":"58","filename":"58.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"watch","role":"companionSettings","scale":"2x"},{"size":"1024x1024","expected-size":"1024","filename":"1024.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"watch-marketing","scale":"1x"},{"size":"128x128","expected-size":"128","filename":"128.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"mac","scale":"1x"},{"size":"256x256","expected-size":"256","filename":"256.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"mac","scale":"1x"},{"size":"128x128","expected-size":"256","filename":"256.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"mac","scale":"2x"},{"size":"256x256","expected-size":"512","filename":"512.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"mac","scale":"2x"},{"size":"32x32","expected-size":"32","filename":"32.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"mac","scale":"1x"},{"size":"512x512","expected-size":"512","filename":"512.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"mac","scale":"1x"},{"size":"16x16","expected-size":"16","filename":"16.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"mac","scale":"1x"},{"size":"16x16","expected-size":"32","filename":"32.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"mac","scale":"2x"},{"size":"32x32","expected-size":"64","filename":"64.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"mac","scale":"2x"},{"size":"512x512","expected-size":"1024","filename":"1024.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"mac","scale":"2x"}]} \ No newline at end of file +{ + "images" : [ + { + "filename" : "40.png", + "idiom" : "iphone", + "scale" : "2x", + "size" : "20x20" + }, + { + "filename" : "60.png", + "idiom" : "iphone", + "scale" : "3x", + "size" : "20x20" + }, + { + "filename" : "29.png", + "idiom" : "iphone", + "scale" : "1x", + "size" : "29x29" + }, + { + "filename" : "58.png", + "idiom" : "iphone", + "scale" : "2x", + "size" : "29x29" + }, + { + "filename" : "87.png", + "idiom" : "iphone", + "scale" : "3x", + "size" : "29x29" + }, + { + "filename" : "80.png", + "idiom" : "iphone", + "scale" : "2x", + "size" : "40x40" + }, + { + "filename" : "120.png", + "idiom" : "iphone", + "scale" : "3x", + "size" : "40x40" + }, + { + "filename" : "57.png", + "idiom" : "iphone", + "scale" : "1x", + "size" : "57x57" + }, + { + "filename" : "114.png", + "idiom" : "iphone", + "scale" : "2x", + "size" : "57x57" + }, + { + "filename" : "120.png", + "idiom" : "iphone", + "scale" : "2x", + "size" : "60x60" + }, + { + "filename" : "180.png", + "idiom" : "iphone", + "scale" : "3x", + "size" : "60x60" + }, + { + "filename" : "20.png", + "idiom" : "ipad", + "scale" : "1x", + "size" : "20x20" + }, + { + "filename" : "40.png", + "idiom" : "ipad", + "scale" : "2x", + "size" : "20x20" + }, + { + "filename" : "29.png", + "idiom" : "ipad", + "scale" : "1x", + "size" : "29x29" + }, + { + "filename" : "58.png", + "idiom" : "ipad", + "scale" : "2x", + "size" : "29x29" + }, + { + "filename" : "40.png", + "idiom" : "ipad", + "scale" : "1x", + "size" : "40x40" + }, + { + "filename" : "80.png", + "idiom" : "ipad", + "scale" : "2x", + "size" : "40x40" + }, + { + "filename" : "50.png", + "idiom" : "ipad", + "scale" : "1x", + "size" : "50x50" + }, + { + "filename" : "100.png", + "idiom" : "ipad", + "scale" : "2x", + "size" : "50x50" + }, + { + "filename" : "72.png", + "idiom" : "ipad", + "scale" : "1x", + "size" : "72x72" + }, + { + "filename" : "144.png", + "idiom" : "ipad", + "scale" : "2x", + "size" : "72x72" + }, + { + "filename" : "76.png", + "idiom" : "ipad", + "scale" : "1x", + "size" : "76x76" + }, + { + "filename" : "152.png", + "idiom" : "ipad", + "scale" : "2x", + "size" : "76x76" + }, + { + "filename" : "167.png", + "idiom" : "ipad", + "scale" : "2x", + "size" : "83.5x83.5" + }, + { + "filename" : "1024.png", + "idiom" : "ios-marketing", + "scale" : "1x", + "size" : "1024x1024" + }, + { + "filename" : "16.png", + "idiom" : "mac", + "scale" : "1x", + "size" : "16x16" + }, + { + "filename" : "32.png", + "idiom" : "mac", + "scale" : "2x", + "size" : "16x16" + }, + { + "filename" : "32.png", + "idiom" : "mac", + "scale" : "1x", + "size" : "32x32" + }, + { + "filename" : "64.png", + "idiom" : "mac", + "scale" : "2x", + "size" : "32x32" + }, + { + "filename" : "128.png", + "idiom" : "mac", + "scale" : "1x", + "size" : "128x128" + }, + { + "filename" : "256.png", + "idiom" : "mac", + "scale" : "2x", + "size" : "128x128" + }, + { + "filename" : "256.png", + "idiom" : "mac", + "scale" : "1x", + "size" : "256x256" + }, + { + "filename" : "512.png", + "idiom" : "mac", + "scale" : "2x", + "size" : "256x256" + }, + { + "filename" : "512.png", + "idiom" : "mac", + "scale" : "1x", + "size" : "512x512" + }, + { + "filename" : "1024.png", + "idiom" : "mac", + "scale" : "2x", + "size" : "512x512" + }, + { + "filename" : "48.png", + "idiom" : "watch", + "role" : "notificationCenter", + "scale" : "2x", + "size" : "24x24", + "subtype" : "38mm" + }, + { + "filename" : "55.png", + "idiom" : "watch", + "role" : "notificationCenter", + "scale" : "2x", + "size" : "27.5x27.5", + "subtype" : "42mm" + }, + { + "filename" : "58.png", + "idiom" : "watch", + "role" : "companionSettings", + "scale" : "2x", + "size" : "29x29" + }, + { + "filename" : "87.png", + "idiom" : "watch", + "role" : "companionSettings", + "scale" : "3x", + "size" : "29x29" + }, + { + "filename" : "66.png", + "idiom" : "watch", + "role" : "notificationCenter", + "scale" : "2x", + "size" : "33x33", + "subtype" : "45mm" + }, + { + "filename" : "80.png", + "idiom" : "watch", + "role" : "appLauncher", + "scale" : "2x", + "size" : "40x40", + "subtype" : "38mm" + }, + { + "filename" : "88.png", + "idiom" : "watch", + "role" : "appLauncher", + "scale" : "2x", + "size" : "44x44", + "subtype" : "40mm" + }, + { + "filename" : "92.png", + "idiom" : "watch", + "role" : "appLauncher", + "scale" : "2x", + "size" : "46x46", + "subtype" : "41mm" + }, + { + "filename" : "100.png", + "idiom" : "watch", + "role" : "appLauncher", + "scale" : "2x", + "size" : "50x50", + "subtype" : "44mm" + }, + { + "idiom" : "watch", + "role" : "appLauncher", + "scale" : "2x", + "size" : "51x51", + "subtype" : "45mm" + }, + { + "idiom" : "watch", + "role" : "appLauncher", + "scale" : "2x", + "size" : "54x54", + "subtype" : "49mm" + }, + { + "filename" : "172.png", + "idiom" : "watch", + "role" : "quickLook", + "scale" : "2x", + "size" : "86x86", + "subtype" : "38mm" + }, + { + "filename" : "196.png", + "idiom" : "watch", + "role" : "quickLook", + "scale" : "2x", + "size" : "98x98", + "subtype" : "42mm" + }, + { + "filename" : "216.png", + "idiom" : "watch", + "role" : "quickLook", + "scale" : "2x", + "size" : "108x108", + "subtype" : "44mm" + }, + { + "idiom" : "watch", + "role" : "quickLook", + "scale" : "2x", + "size" : "117x117", + "subtype" : "45mm" + }, + { + "idiom" : "watch", + "role" : "quickLook", + "scale" : "2x", + "size" : "129x129", + "subtype" : "49mm" + }, + { + "filename" : "1024.png", + "idiom" : "watch-marketing", + "scale" : "1x", + "size" : "1024x1024" + }, + { + "filename" : "102.png", + "idiom" : "watch", + "role" : "appLauncher", + "scale" : "2x", + "size" : "45x45", + "subtype" : "41mm" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/mobile/lib/modules/backup/models/available_album.model.dart b/mobile/lib/modules/backup/models/available_album.model.dart index 7469581bf2..0b428eea0f 100644 --- a/mobile/lib/modules/backup/models/available_album.model.dart +++ b/mobile/lib/modules/backup/models/available_album.model.dart @@ -5,11 +5,9 @@ import 'package:photo_manager/photo_manager.dart'; class AvailableAlbum { final AssetPathEntity albumEntity; final DateTime? lastBackup; - final Uint8List? thumbnailData; AvailableAlbum({ required this.albumEntity, this.lastBackup, - this.thumbnailData, }); AvailableAlbum copyWith({ @@ -20,7 +18,6 @@ class AvailableAlbum { return AvailableAlbum( albumEntity: albumEntity ?? this.albumEntity, lastBackup: lastBackup ?? this.lastBackup, - thumbnailData: thumbnailData ?? this.thumbnailData, ); } @@ -34,7 +31,7 @@ class AvailableAlbum { @override String toString() => - 'AvailableAlbum(albumEntity: $albumEntity, lastBackup: $lastBackup, thumbnailData: $thumbnailData)'; + 'AvailableAlbum(albumEntity: $albumEntity, lastBackup: $lastBackup)'; @override bool operator ==(Object other) { diff --git a/mobile/lib/modules/backup/providers/backup.provider.dart b/mobile/lib/modules/backup/providers/backup.provider.dart index a02ddf4e35..a2de92d6d7 100644 --- a/mobile/lib/modules/backup/providers/backup.provider.dart +++ b/mobile/lib/modules/backup/providers/backup.provider.dart @@ -234,33 +234,9 @@ class BackupNotifier extends StateNotifier { for (AssetPathEntity album in albums) { AvailableAlbum availableAlbum = AvailableAlbum(albumEntity: album); - final assetCountInAlbum = await album.assetCountAsync; - if (assetCountInAlbum > 0) { - final assetList = await album.getAssetListPaged(page: 0, size: 1); + availableAlbums.add(availableAlbum); - // Even though we check assetCountInAlbum to make sure that there are assets in album - // The `getAssetListPaged` method still return empty list and cause not assets get rendered - if (assetList.isEmpty) { - continue; - } - final thumbnailAsset = assetList.first; - try { - final thumbnailData = await thumbnailAsset - .thumbnailDataWithSize(const ThumbnailSize(512, 512)); - availableAlbum = - availableAlbum.copyWith(thumbnailData: thumbnailData); - } catch (e, stack) { - log.severe( - "Failed to get thumbnail for album ${album.name}", - e, - stack, - ); - } - - availableAlbums.add(availableAlbum); - - albumMap[album.id] = album; - } + albumMap[album.id] = album; } state = state.copyWith(availableAlbums: availableAlbums); diff --git a/mobile/lib/modules/backup/ui/album_info_card.dart b/mobile/lib/modules/backup/ui/album_info_card.dart index 0008c0a9e8..5380360ff1 100644 --- a/mobile/lib/modules/backup/ui/album_info_card.dart +++ b/mobile/lib/modules/backup/ui/album_info_card.dart @@ -11,17 +11,16 @@ import 'package:immich_mobile/routing/router.dart'; import 'package:immich_mobile/shared/ui/immich_toast.dart'; class AlbumInfoCard extends HookConsumerWidget { - final Uint8List? imageData; - final AvailableAlbum albumInfo; + final AvailableAlbum album; - const AlbumInfoCard({super.key, this.imageData, required this.albumInfo}); + const AlbumInfoCard({super.key, required this.album}); @override Widget build(BuildContext context, WidgetRef ref) { final bool isSelected = - ref.watch(backupProvider).selectedBackupAlbums.contains(albumInfo); + ref.watch(backupProvider).selectedBackupAlbums.contains(album); final bool isExcluded = - ref.watch(backupProvider).excludedBackupAlbums.contains(albumInfo); + ref.watch(backupProvider).excludedBackupAlbums.contains(album); final isDarkTheme = context.isDarkTheme; ColorFilter selectedFilter = ColorFilter.mode( @@ -82,9 +81,9 @@ class AlbumInfoCard extends HookConsumerWidget { HapticFeedback.selectionClick(); if (isSelected) { - ref.read(backupProvider.notifier).removeAlbumForBackup(albumInfo); + ref.read(backupProvider.notifier).removeAlbumForBackup(album); } else { - ref.read(backupProvider.notifier).addAlbumForBackup(albumInfo); + ref.read(backupProvider.notifier).addAlbumForBackup(album); } }, onDoubleTap: () { @@ -92,13 +91,11 @@ class AlbumInfoCard extends HookConsumerWidget { if (isExcluded) { // Remove from exclude album list - ref - .read(backupProvider.notifier) - .removeExcludedAlbumForBackup(albumInfo); + ref.read(backupProvider.notifier).removeExcludedAlbumForBackup(album); } else { // Add to exclude album list - if (albumInfo.id == 'isAll' || albumInfo.name == 'Recents') { + if (album.id == 'isAll' || album.name == 'Recents') { ImmichToast.show( context: context, msg: 'Cannot exclude album contains all assets', @@ -108,9 +105,7 @@ class AlbumInfoCard extends HookConsumerWidget { return; } - ref - .read(backupProvider.notifier) - .addExcludedAlbumForBackup(albumInfo); + ref.read(backupProvider.notifier).addExcludedAlbumForBackup(album); } }, child: Card( @@ -136,14 +131,12 @@ class AlbumInfoCard extends HookConsumerWidget { children: [ ColorFiltered( colorFilter: buildImageFilter(), - child: Image( + child: const Image( width: double.infinity, height: double.infinity, - image: imageData != null - ? MemoryImage(imageData!) - : const AssetImage( - 'assets/immich-logo.png', - ) as ImageProvider, + image: AssetImage( + 'assets/immich-logo.png', + ), fit: BoxFit.cover, ), ), @@ -168,7 +161,7 @@ class AlbumInfoCard extends HookConsumerWidget { mainAxisAlignment: MainAxisAlignment.center, children: [ Text( - albumInfo.name, + album.name, style: TextStyle( fontSize: 14, color: context.primaryColor, @@ -182,7 +175,7 @@ class AlbumInfoCard extends HookConsumerWidget { if (snapshot.hasData) { return Text( snapshot.data.toString() + - (albumInfo.isAll + (album.isAll ? " (${'backup_all'.tr()})" : ""), style: TextStyle( @@ -193,7 +186,7 @@ class AlbumInfoCard extends HookConsumerWidget { } return const Text("0"); }), - future: albumInfo.assetCount, + future: album.assetCount, ), ), ], @@ -202,7 +195,7 @@ class AlbumInfoCard extends HookConsumerWidget { IconButton( onPressed: () { context.pushRoute( - AlbumPreviewRoute(album: albumInfo.albumEntity), + AlbumPreviewRoute(album: album.albumEntity), ); }, icon: Icon( diff --git a/mobile/lib/modules/backup/ui/album_info_list_tile.dart b/mobile/lib/modules/backup/ui/album_info_list_tile.dart index c87bec09a6..dcf0923a11 100644 --- a/mobile/lib/modules/backup/ui/album_info_list_tile.dart +++ b/mobile/lib/modules/backup/ui/album_info_list_tile.dart @@ -11,47 +11,26 @@ import 'package:immich_mobile/routing/router.dart'; import 'package:immich_mobile/shared/ui/immich_toast.dart'; class AlbumInfoListTile extends HookConsumerWidget { - final Uint8List? imageData; - final AvailableAlbum albumInfo; + final AvailableAlbum album; - const AlbumInfoListTile({super.key, this.imageData, required this.albumInfo}); + const AlbumInfoListTile({super.key, required this.album}); @override Widget build(BuildContext context, WidgetRef ref) { final bool isSelected = - ref.watch(backupProvider).selectedBackupAlbums.contains(albumInfo); + ref.watch(backupProvider).selectedBackupAlbums.contains(album); final bool isExcluded = - ref.watch(backupProvider).excludedBackupAlbums.contains(albumInfo); - - ColorFilter selectedFilter = ColorFilter.mode( - context.primaryColor.withAlpha(100), - BlendMode.darken, - ); - ColorFilter excludedFilter = - ColorFilter.mode(Colors.red.withAlpha(75), BlendMode.darken); - ColorFilter unselectedFilter = - const ColorFilter.mode(Colors.black, BlendMode.color); - + ref.watch(backupProvider).excludedBackupAlbums.contains(album); var assetCount = useState(0); useEffect( () { - albumInfo.assetCount.then((value) => assetCount.value = value); + album.assetCount.then((value) => assetCount.value = value); return null; }, - [albumInfo], + [album], ); - buildImageFilter() { - if (isSelected) { - return selectedFilter; - } else if (isExcluded) { - return excludedFilter; - } else { - return unselectedFilter; - } - } - buildTileColor() { if (isSelected) { return context.isDarkTheme @@ -66,19 +45,38 @@ class AlbumInfoListTile extends HookConsumerWidget { } } + buildIcon() { + if (isSelected) { + return const Icon( + Icons.check_circle_rounded, + color: Colors.green, + ); + } + + if (isExcluded) { + return const Icon( + Icons.remove_circle_rounded, + color: Colors.red, + ); + } + + return Icon( + Icons.circle, + color: context.isDarkTheme ? Colors.grey[400] : Colors.black45, + ); + } + return GestureDetector( onDoubleTap: () { HapticFeedback.selectionClick(); if (isExcluded) { // Remove from exclude album list - ref - .read(backupProvider.notifier) - .removeExcludedAlbumForBackup(albumInfo); + ref.read(backupProvider.notifier).removeExcludedAlbumForBackup(album); } else { // Add to exclude album list - if (albumInfo.id == 'isAll' || albumInfo.name == 'Recents') { + if (album.id == 'isAll' || album.name == 'Recents') { ImmichToast.show( context: context, msg: 'Cannot exclude album contains all assets', @@ -88,9 +86,7 @@ class AlbumInfoListTile extends HookConsumerWidget { return; } - ref - .read(backupProvider.notifier) - .addExcludedAlbumForBackup(albumInfo); + ref.read(backupProvider.notifier).addExcludedAlbumForBackup(album); } }, child: ListTile( @@ -99,33 +95,14 @@ class AlbumInfoListTile extends HookConsumerWidget { onTap: () { HapticFeedback.selectionClick(); if (isSelected) { - ref.read(backupProvider.notifier).removeAlbumForBackup(albumInfo); + ref.read(backupProvider.notifier).removeAlbumForBackup(album); } else { - ref.read(backupProvider.notifier).addAlbumForBackup(albumInfo); + ref.read(backupProvider.notifier).addAlbumForBackup(album); } }, - leading: ClipRRect( - borderRadius: BorderRadius.circular(12), - child: SizedBox( - height: 80, - width: 80, - child: ColorFiltered( - colorFilter: buildImageFilter(), - child: Image( - width: double.infinity, - height: double.infinity, - image: imageData != null - ? MemoryImage(imageData!) - : const AssetImage( - 'assets/immich-logo.png', - ) as ImageProvider, - fit: BoxFit.cover, - ), - ), - ), - ), + leading: buildIcon(), title: Text( - albumInfo.name, + album.name, style: const TextStyle( fontSize: 14, fontWeight: FontWeight.bold, @@ -135,7 +112,7 @@ class AlbumInfoListTile extends HookConsumerWidget { trailing: IconButton( onPressed: () { context.pushRoute( - AlbumPreviewRoute(album: albumInfo.albumEntity), + AlbumPreviewRoute(album: album.albumEntity), ); }, icon: Icon( diff --git a/mobile/lib/modules/backup/views/backup_album_selection_page.dart b/mobile/lib/modules/backup/views/backup_album_selection_page.dart index e892e57c1d..c2bcbb15cd 100644 --- a/mobile/lib/modules/backup/views/backup_album_selection_page.dart +++ b/mobile/lib/modules/backup/views/backup_album_selection_page.dart @@ -43,10 +43,8 @@ class BackupAlbumSelectionPage extends HookConsumerWidget { sliver: SliverList( delegate: SliverChildBuilderDelegate( ((context, index) { - var thumbnailData = albums[index].thumbnailData; return AlbumInfoListTile( - imageData: thumbnailData, - albumInfo: albums[index], + album: albums[index], ); }), childCount: albums.length, @@ -74,10 +72,8 @@ class BackupAlbumSelectionPage extends HookConsumerWidget { ), itemCount: albums.length, itemBuilder: ((context, index) { - var thumbnailData = albums[index].thumbnailData; return AlbumInfoCard( - imageData: thumbnailData, - albumInfo: albums[index], + album: albums[index], ); }), ), diff --git a/mobile/lib/modules/backup/views/backup_controller_page.dart b/mobile/lib/modules/backup/views/backup_controller_page.dart index 3b219e3f6c..0e22adeb97 100644 --- a/mobile/lib/modules/backup/views/backup_controller_page.dart +++ b/mobile/lib/modules/backup/views/backup_controller_page.dart @@ -26,7 +26,7 @@ class BackupControllerPage extends HookConsumerWidget { Widget build(BuildContext context, WidgetRef ref) { BackUpState backupState = ref.watch(backupProvider); final hasAnyAlbum = backupState.selectedBackupAlbums.isNotEmpty; - + final didGetBackupInfo = useState(false); bool hasExclusiveAccess = backupState.backupProgress != BackUpProgressEnum.inBackground; bool shouldBackup = backupState.allUniqueAssets.length - @@ -38,11 +38,6 @@ class BackupControllerPage extends HookConsumerWidget { useEffect( () { - if (backupState.backupProgress != BackUpProgressEnum.inProgress && - backupState.backupProgress != BackUpProgressEnum.manualInProgress) { - ref.watch(backupProvider.notifier).getBackupInfo(); - } - // Update the background settings information just to make sure we // have the latest, since the platform channel will not update // automatically @@ -58,6 +53,18 @@ class BackupControllerPage extends HookConsumerWidget { [], ); + useEffect( + () { + if (backupState.backupProgress == BackUpProgressEnum.idle && + !didGetBackupInfo.value) { + ref.watch(backupProvider.notifier).getBackupInfo(); + didGetBackupInfo.value = true; + } + return null; + }, + [backupState.backupProgress], + ); + Widget buildSelectedAlbumName() { var text = "backup_controller_page_backup_selected".tr(); var albums = ref.watch(backupProvider).selectedBackupAlbums; @@ -235,6 +242,15 @@ class BackupControllerPage extends HookConsumerWidget { ); } + buildLoadingIndicator() { + return const Padding( + padding: EdgeInsets.only(top: 42.0), + child: Center( + child: CircularProgressIndicator(), + ), + ); + } + return Scaffold( appBar: AppBar( elevation: 0, @@ -297,7 +313,10 @@ class BackupControllerPage extends HookConsumerWidget { if (!hasExclusiveAccess) buildBackgroundBackupInfo(), buildBackupButton(), ] - : [buildFolderSelectionTile()], + : [ + buildFolderSelectionTile(), + if (!didGetBackupInfo.value) buildLoadingIndicator(), + ], ), ), );