Prechádzať zdrojové kódy

Fix backup not resuming after closed and reopen (#266)

* Fixed app not resuming backup after closing and reopening the app

* Fixed cosmetic effect of backup button doesn't change state right away after pressing start backup

* Fixed grammar

* Fixed deep copy problem that cause incorrect asset count when backing up

* Format code
Alex 3 rokov pred
rodič
commit
40a8115101
63 zmenil súbory, kde vykonal 677 pridanie a 300 odobranie
  1. 8 4
      mobile/lib/modules/asset_viewer/models/image_viewer_page_state.model.dart
  2. 5 2
      mobile/lib/modules/asset_viewer/providers/image_viewer_page_state.provider.dart
  3. 5 3
      mobile/lib/modules/asset_viewer/services/image_viewer.service.dart
  4. 5 2
      mobile/lib/modules/backup/models/available_album.model.dart
  5. 8 7
      mobile/lib/modules/backup/models/backup_state.model.dart
  6. 4 2
      mobile/lib/modules/backup/models/hive_backup_albums.model.dart
  7. 23 20
      mobile/lib/modules/backup/providers/backup.provider.dart
  8. 41 17
      mobile/lib/modules/backup/ui/album_info_card.dart
  9. 6 1
      mobile/lib/modules/backup/ui/backup_info_card.dart
  10. 9 4
      mobile/lib/modules/backup/views/album_preview_page.dart
  11. 39 13
      mobile/lib/modules/backup/views/backup_album_selection_page.dart
  12. 13 13
      mobile/lib/modules/backup/views/backup_controller_page.dart
  13. 5 2
      mobile/lib/modules/home/models/delete_asset_response.model.dart
  14. 13 6
      mobile/lib/modules/home/models/get_all_asset_response.model.dart
  15. 8 3
      mobile/lib/modules/home/models/home_page_state.model.dart
  16. 8 4
      mobile/lib/modules/home/providers/home_page_state.provider.dart
  17. 7 2
      mobile/lib/modules/home/ui/control_bottom_app_bar.dart
  18. 35 13
      mobile/lib/modules/home/ui/daily_title_text.dart
  19. 5 2
      mobile/lib/modules/home/ui/delete_diaglog.dart
  20. 4 1
      mobile/lib/modules/home/ui/image_grid.dart
  21. 28 11
      mobile/lib/modules/home/ui/thumbnail_image.dart
  22. 2 1
      mobile/lib/modules/login/models/authentication_state.model.dart
  23. 5 1
      mobile/lib/modules/login/models/hive_saved_login_info.model.dart
  24. 2 1
      mobile/lib/modules/login/models/login_response.model.dart
  25. 33 14
      mobile/lib/modules/login/ui/login_form.dart
  26. 7 2
      mobile/lib/modules/search/models/curated_location.model.dart
  27. 7 2
      mobile/lib/modules/search/models/curated_object.model.dart
  28. 6 3
      mobile/lib/modules/search/models/search_page_state.model.dart
  29. 8 3
      mobile/lib/modules/search/models/search_result_page_state.model.dart
  30. 3 1
      mobile/lib/modules/search/ui/search_bar.dart
  31. 7 3
      mobile/lib/modules/search/ui/search_suggestion_list.dart
  32. 8 2
      mobile/lib/modules/search/ui/thumbnail_with_info.dart
  33. 7 3
      mobile/lib/modules/sharing/models/album_viewer_page_state.model.dart
  34. 18 8
      mobile/lib/modules/sharing/models/asset_selection_page_result.model.dart
  35. 32 16
      mobile/lib/modules/sharing/models/asset_selection_state.model.dart
  36. 8 4
      mobile/lib/modules/sharing/models/shared_album.model.dart
  37. 2 1
      mobile/lib/modules/sharing/providers/album_title.provider.dart
  38. 25 8
      mobile/lib/modules/sharing/providers/asset_selection.provider.dart
  39. 7 2
      mobile/lib/modules/sharing/ui/album_action_outlined_button.dart
  40. 2 1
      mobile/lib/modules/sharing/ui/album_title_text_field.dart
  41. 8 3
      mobile/lib/modules/sharing/ui/album_viewer_editable_title.dart
  42. 20 8
      mobile/lib/modules/sharing/ui/album_viewer_thumbnail.dart
  43. 2 1
      mobile/lib/modules/sharing/ui/asset_grid_by_month.dart
  44. 25 9
      mobile/lib/modules/sharing/ui/month_group_title.dart
  45. 22 9
      mobile/lib/modules/sharing/ui/selection_thumbnail_image.dart
  46. 6 3
      mobile/lib/modules/sharing/ui/shared_album_thumbnail_image.dart
  47. 10 5
      mobile/lib/modules/sharing/ui/sharing_sliver_appbar.dart
  48. 30 12
      mobile/lib/modules/sharing/views/select_additional_user_for_sharing_page.dart
  49. 16 6
      mobile/lib/modules/sharing/views/sharing_page.dart
  50. 2 1
      mobile/lib/shared/models/exif.model.dart
  51. 2 1
      mobile/lib/shared/models/immich_asset.model.dart
  52. 4 2
      mobile/lib/shared/models/immich_asset_with_exif.model.dart
  53. 7 3
      mobile/lib/shared/models/mapbox_info.model.dart
  54. 2 1
      mobile/lib/shared/models/server_info.model.dart
  55. 4 2
      mobile/lib/shared/models/server_info_state.model.dart
  56. 2 1
      mobile/lib/shared/models/server_version.model.dart
  57. 7 3
      mobile/lib/shared/models/upload_profile_image_repsonse.model.dart
  58. 7 6
      mobile/lib/shared/models/user.model.dart
  59. 1 1
      mobile/lib/shared/providers/asset.provider.dart
  60. 9 4
      mobile/lib/shared/providers/websocket.provider.dart
  61. 7 3
      mobile/lib/shared/ui/immich_sliver_persistent_app_bar_delegate.dart
  62. 4 2
      mobile/lib/shared/views/splash_screen.dart
  63. 12 6
      mobile/lib/shared/views/tab_controller_page.dart

+ 8 - 4
mobile/lib/modules/asset_viewer/models/image_viewer_page_state.model.dart

@@ -28,22 +28,26 @@ class ImageViewerPageState {
 
 
   factory ImageViewerPageState.fromMap(Map<String, dynamic> map) {
   factory ImageViewerPageState.fromMap(Map<String, dynamic> map) {
     return ImageViewerPageState(
     return ImageViewerPageState(
-      downloadAssetStatus: DownloadAssetStatus.values[map['downloadAssetStatus'] ?? 0],
+      downloadAssetStatus:
+          DownloadAssetStatus.values[map['downloadAssetStatus'] ?? 0],
     );
     );
   }
   }
 
 
   String toJson() => json.encode(toMap());
   String toJson() => json.encode(toMap());
 
 
-  factory ImageViewerPageState.fromJson(String source) => ImageViewerPageState.fromMap(json.decode(source));
+  factory ImageViewerPageState.fromJson(String source) =>
+      ImageViewerPageState.fromMap(json.decode(source));
 
 
   @override
   @override
-  String toString() => 'ImageViewerPageState(downloadAssetStatus: $downloadAssetStatus)';
+  String toString() =>
+      'ImageViewerPageState(downloadAssetStatus: $downloadAssetStatus)';
 
 
   @override
   @override
   bool operator ==(Object other) {
   bool operator ==(Object other) {
     if (identical(this, other)) return true;
     if (identical(this, other)) return true;
 
 
-    return other is ImageViewerPageState && other.downloadAssetStatus == downloadAssetStatus;
+    return other is ImageViewerPageState &&
+        other.downloadAssetStatus == downloadAssetStatus;
   }
   }
 
 
   @override
   @override

+ 5 - 2
mobile/lib/modules/asset_viewer/providers/image_viewer_page_state.provider.dart

@@ -9,7 +9,9 @@ import 'package:immich_mobile/shared/ui/immich_toast.dart';
 class ImageViewerStateNotifier extends StateNotifier<ImageViewerPageState> {
 class ImageViewerStateNotifier extends StateNotifier<ImageViewerPageState> {
   final ImageViewerService _imageViewerService = ImageViewerService();
   final ImageViewerService _imageViewerService = ImageViewerService();
 
 
-  ImageViewerStateNotifier() : super(ImageViewerPageState(downloadAssetStatus: DownloadAssetStatus.idle));
+  ImageViewerStateNotifier()
+      : super(ImageViewerPageState(
+            downloadAssetStatus: DownloadAssetStatus.idle));
 
 
   void downloadAsset(ImmichAsset asset, BuildContext context) async {
   void downloadAsset(ImmichAsset asset, BuildContext context) async {
     state = state.copyWith(downloadAssetStatus: DownloadAssetStatus.loading);
     state = state.copyWith(downloadAssetStatus: DownloadAssetStatus.loading);
@@ -40,4 +42,5 @@ class ImageViewerStateNotifier extends StateNotifier<ImageViewerPageState> {
 }
 }
 
 
 final imageViewerStateProvider =
 final imageViewerStateProvider =
-    StateNotifierProvider<ImageViewerStateNotifier, ImageViewerPageState>(((ref) => ImageViewerStateNotifier()));
+    StateNotifierProvider<ImageViewerStateNotifier, ImageViewerPageState>(
+        ((ref) => ImageViewerStateNotifier()));

+ 5 - 3
mobile/lib/modules/asset_viewer/services/image_viewer.service.dart

@@ -15,12 +15,14 @@ class ImageViewerService {
     try {
     try {
       String fileName = p.basename(asset.originalPath);
       String fileName = p.basename(asset.originalPath);
       var savedEndpoint = Hive.box(userInfoBox).get(serverEndpointKey);
       var savedEndpoint = Hive.box(userInfoBox).get(serverEndpointKey);
-      Uri filePath =
-          Uri.parse("$savedEndpoint/asset/download?aid=${asset.deviceAssetId}&did=${asset.deviceId}&isThumb=false");
+      Uri filePath = Uri.parse(
+          "$savedEndpoint/asset/download?aid=${asset.deviceAssetId}&did=${asset.deviceId}&isThumb=false");
 
 
       var res = await http.get(
       var res = await http.get(
         filePath,
         filePath,
-        headers: {"Authorization": "Bearer ${Hive.box(userInfoBox).get(accessTokenKey)}"},
+        headers: {
+          "Authorization": "Bearer ${Hive.box(userInfoBox).get(accessTokenKey)}"
+        },
       );
       );
 
 
       final AssetEntity? entity;
       final AssetEntity? entity;

+ 5 - 2
mobile/lib/modules/backup/models/available_album.model.dart

@@ -21,13 +21,16 @@ class AvailableAlbum {
   }
   }
 
 
   @override
   @override
-  String toString() => 'AvailableAlbum(albumEntity: $albumEntity, thumbnailData: $thumbnailData)';
+  String toString() =>
+      'AvailableAlbum(albumEntity: $albumEntity, thumbnailData: $thumbnailData)';
 
 
   @override
   @override
   bool operator ==(Object other) {
   bool operator ==(Object other) {
     if (identical(this, other)) return true;
     if (identical(this, other)) return true;
 
 
-    return other is AvailableAlbum && other.albumEntity == albumEntity && other.thumbnailData == thumbnailData;
+    return other is AvailableAlbum &&
+        other.albumEntity == albumEntity &&
+        other.thumbnailData == thumbnailData;
   }
   }
 
 
   @override
   @override

+ 8 - 7
mobile/lib/modules/backup/models/backup_state.model.dart

@@ -10,7 +10,7 @@ enum BackUpProgressEnum { idle, inProgress, done }
 class BackUpState extends Equatable {
 class BackUpState extends Equatable {
   // enum
   // enum
   final BackUpProgressEnum backupProgress;
   final BackUpProgressEnum backupProgress;
-  final List<String> allAssetOnDatabase;
+  final List<String> allAssetsInDatabase;
   final double progressInPercentage;
   final double progressInPercentage;
   final CancellationToken cancelToken;
   final CancellationToken cancelToken;
   final ServerInfo serverInfo;
   final ServerInfo serverInfo;
@@ -28,7 +28,7 @@ class BackUpState extends Equatable {
 
 
   const BackUpState({
   const BackUpState({
     required this.backupProgress,
     required this.backupProgress,
-    required this.allAssetOnDatabase,
+    required this.allAssetsInDatabase,
     required this.progressInPercentage,
     required this.progressInPercentage,
     required this.cancelToken,
     required this.cancelToken,
     required this.serverInfo,
     required this.serverInfo,
@@ -41,7 +41,7 @@ class BackUpState extends Equatable {
 
 
   BackUpState copyWith({
   BackUpState copyWith({
     BackUpProgressEnum? backupProgress,
     BackUpProgressEnum? backupProgress,
-    List<String>? allAssetOnDatabase,
+    List<String>? allAssetsInDatabase,
     double? progressInPercentage,
     double? progressInPercentage,
     CancellationToken? cancelToken,
     CancellationToken? cancelToken,
     ServerInfo? serverInfo,
     ServerInfo? serverInfo,
@@ -53,7 +53,7 @@ class BackUpState extends Equatable {
   }) {
   }) {
     return BackUpState(
     return BackUpState(
       backupProgress: backupProgress ?? this.backupProgress,
       backupProgress: backupProgress ?? this.backupProgress,
-      allAssetOnDatabase: allAssetOnDatabase ?? this.allAssetOnDatabase,
+      allAssetsInDatabase: allAssetsInDatabase ?? this.allAssetsInDatabase,
       progressInPercentage: progressInPercentage ?? this.progressInPercentage,
       progressInPercentage: progressInPercentage ?? this.progressInPercentage,
       cancelToken: cancelToken ?? this.cancelToken,
       cancelToken: cancelToken ?? this.cancelToken,
       serverInfo: serverInfo ?? this.serverInfo,
       serverInfo: serverInfo ?? this.serverInfo,
@@ -61,20 +61,21 @@ class BackUpState extends Equatable {
       selectedBackupAlbums: selectedBackupAlbums ?? this.selectedBackupAlbums,
       selectedBackupAlbums: selectedBackupAlbums ?? this.selectedBackupAlbums,
       excludedBackupAlbums: excludedBackupAlbums ?? this.excludedBackupAlbums,
       excludedBackupAlbums: excludedBackupAlbums ?? this.excludedBackupAlbums,
       allUniqueAssets: allUniqueAssets ?? this.allUniqueAssets,
       allUniqueAssets: allUniqueAssets ?? this.allUniqueAssets,
-      selectedAlbumsBackupAssetsIds: selectedAlbumsBackupAssetsIds ?? this.selectedAlbumsBackupAssetsIds,
+      selectedAlbumsBackupAssetsIds:
+          selectedAlbumsBackupAssetsIds ?? this.selectedAlbumsBackupAssetsIds,
     );
     );
   }
   }
 
 
   @override
   @override
   String toString() {
   String toString() {
-    return 'BackUpState(backupProgress: $backupProgress, allAssetOnDatabase: $allAssetOnDatabase, progressInPercentage: $progressInPercentage, cancelToken: $cancelToken, serverInfo: $serverInfo, availableAlbums: $availableAlbums, selectedBackupAlbums: $selectedBackupAlbums, excludedBackupAlbums: $excludedBackupAlbums, allUniqueAssets: $allUniqueAssets, selectedAlbumsBackupAssetsIds: $selectedAlbumsBackupAssetsIds)';
+    return 'BackUpState(backupProgress: $backupProgress, allAssetsInDatabase: $allAssetsInDatabase, progressInPercentage: $progressInPercentage, cancelToken: $cancelToken, serverInfo: $serverInfo, availableAlbums: $availableAlbums, selectedBackupAlbums: $selectedBackupAlbums, excludedBackupAlbums: $excludedBackupAlbums, allUniqueAssets: $allUniqueAssets, selectedAlbumsBackupAssetsIds: $selectedAlbumsBackupAssetsIds)';
   }
   }
 
 
   @override
   @override
   List<Object> get props {
   List<Object> get props {
     return [
     return [
       backupProgress,
       backupProgress,
-      allAssetOnDatabase,
+      allAssetsInDatabase,
       progressInPercentage,
       progressInPercentage,
       cancelToken,
       cancelToken,
       serverInfo,
       serverInfo,

+ 4 - 2
mobile/lib/modules/backup/models/hive_backup_albums.model.dart

@@ -19,7 +19,8 @@ class HiveBackupAlbums {
   });
   });
 
 
   @override
   @override
-  String toString() => 'HiveBackupAlbums(selectedAlbumIds: $selectedAlbumIds, excludedAlbumsIds: $excludedAlbumsIds)';
+  String toString() =>
+      'HiveBackupAlbums(selectedAlbumIds: $selectedAlbumIds, excludedAlbumsIds: $excludedAlbumsIds)';
 
 
   HiveBackupAlbums copyWith({
   HiveBackupAlbums copyWith({
     List<String>? selectedAlbumIds,
     List<String>? selectedAlbumIds,
@@ -49,7 +50,8 @@ class HiveBackupAlbums {
 
 
   String toJson() => json.encode(toMap());
   String toJson() => json.encode(toMap());
 
 
-  factory HiveBackupAlbums.fromJson(String source) => HiveBackupAlbums.fromMap(json.decode(source));
+  factory HiveBackupAlbums.fromJson(String source) =>
+      HiveBackupAlbums.fromMap(json.decode(source));
 
 
   @override
   @override
   bool operator ==(Object other) {
   bool operator ==(Object other) {

+ 23 - 20
mobile/lib/modules/backup/providers/backup.provider.dart

@@ -18,7 +18,7 @@ class BackupNotifier extends StateNotifier<BackUpState> {
       : super(
       : super(
           BackUpState(
           BackUpState(
             backupProgress: BackUpProgressEnum.idle,
             backupProgress: BackUpProgressEnum.idle,
-            allAssetOnDatabase: const [],
+            allAssetsInDatabase: const [],
             progressInPercentage: 0,
             progressInPercentage: 0,
             cancelToken: CancellationToken(),
             cancelToken: CancellationToken(),
             serverInfo: ServerInfo(
             serverInfo: ServerInfo(
@@ -36,7 +36,9 @@ class BackupNotifier extends StateNotifier<BackUpState> {
             allUniqueAssets: const {},
             allUniqueAssets: const {},
             selectedAlbumsBackupAssetsIds: const {},
             selectedAlbumsBackupAssetsIds: const {},
           ),
           ),
-        );
+        ) {
+    getBackupInfo();
+  }
 
 
   final BackupService _backupService;
   final BackupService _backupService;
   final ServerInfoService _serverInfoService;
   final ServerInfoService _serverInfoService;
@@ -93,7 +95,7 @@ class BackupNotifier extends StateNotifier<BackUpState> {
   /// If this is the first time performing backup - set the default selected album to be
   /// If this is the first time performing backup - set the default selected album to be
   /// the one that has all assets (Recent on Android, Recents on iOS)
   /// the one that has all assets (Recent on Android, Recents on iOS)
   ///
   ///
-  Future<void> getBackupAlbumsInfo() async {
+  Future<void> _getBackupAlbumsInfo() async {
     // Get all albums on the device
     // Get all albums on the device
     List<AvailableAlbum> availableAlbums = [];
     List<AvailableAlbum> availableAlbums = [];
     List<AssetPathEntity> albums = await PhotoManager.getAssetPathList(
     List<AssetPathEntity> albums = await PhotoManager.getAssetPathList(
@@ -177,7 +179,7 @@ class BackupNotifier extends StateNotifier<BackUpState> {
   /// Find the assets that are not overlapping between the two sets
   /// Find the assets that are not overlapping between the two sets
   /// Those assets are unique and are used as the total assets
   /// Those assets are unique and are used as the total assets
   ///
   ///
-  void _updateBackupAssetCount() async {
+  Future<void> _updateBackupAssetCount() async {
     Set<AssetEntity> assetsFromSelectedAlbums = {};
     Set<AssetEntity> assetsFromSelectedAlbums = {};
     Set<AssetEntity> assetsFromExcludedAlbums = {};
     Set<AssetEntity> assetsFromExcludedAlbums = {};
 
 
@@ -195,27 +197,27 @@ class BackupNotifier extends StateNotifier<BackUpState> {
 
 
     Set<AssetEntity> allUniqueAssets =
     Set<AssetEntity> allUniqueAssets =
         assetsFromSelectedAlbums.difference(assetsFromExcludedAlbums);
         assetsFromSelectedAlbums.difference(assetsFromExcludedAlbums);
-    List<String> allAssetOnDatabase =
+    List<String> allAssetsInDatabase =
         await _backupService.getDeviceBackupAsset();
         await _backupService.getDeviceBackupAsset();
 
 
     // Find asset that were backup from selected albums
     // Find asset that were backup from selected albums
     Set<String> selectedAlbumsBackupAssets =
     Set<String> selectedAlbumsBackupAssets =
         Set.from(allUniqueAssets.map((e) => e.id));
         Set.from(allUniqueAssets.map((e) => e.id));
     selectedAlbumsBackupAssets
     selectedAlbumsBackupAssets
-        .removeWhere((assetId) => !allAssetOnDatabase.contains(assetId));
+        .removeWhere((assetId) => !allAssetsInDatabase.contains(assetId));
 
 
     if (allUniqueAssets.isEmpty) {
     if (allUniqueAssets.isEmpty) {
       debugPrint("No Asset On Device");
       debugPrint("No Asset On Device");
       state = state.copyWith(
       state = state.copyWith(
         backupProgress: BackUpProgressEnum.idle,
         backupProgress: BackUpProgressEnum.idle,
-        allAssetOnDatabase: allAssetOnDatabase,
+        allAssetsInDatabase: allAssetsInDatabase,
         allUniqueAssets: {},
         allUniqueAssets: {},
         selectedAlbumsBackupAssetsIds: selectedAlbumsBackupAssets,
         selectedAlbumsBackupAssetsIds: selectedAlbumsBackupAssets,
       );
       );
       return;
       return;
     } else {
     } else {
       state = state.copyWith(
       state = state.copyWith(
-        allAssetOnDatabase: allAssetOnDatabase,
+        allAssetsInDatabase: allAssetsInDatabase,
         allUniqueAssets: allUniqueAssets,
         allUniqueAssets: allUniqueAssets,
         selectedAlbumsBackupAssetsIds: selectedAlbumsBackupAssets,
         selectedAlbumsBackupAssetsIds: selectedAlbumsBackupAssets,
       );
       );
@@ -223,6 +225,8 @@ class BackupNotifier extends StateNotifier<BackUpState> {
 
 
     // Save to persistent storage
     // Save to persistent storage
     _updatePersistentAlbumsSelection();
     _updatePersistentAlbumsSelection();
+
+    return;
   }
   }
 
 
   ///
   ///
@@ -230,10 +234,10 @@ class BackupNotifier extends StateNotifier<BackUpState> {
   /// which albums are selected or excluded
   /// which albums are selected or excluded
   /// and then update the UI according to those information
   /// and then update the UI according to those information
   ///
   ///
-  void getBackupInfo() async {
-    await getBackupAlbumsInfo();
-    _updateServerInfo();
-    _updateBackupAssetCount();
+  Future<void> getBackupInfo() async {
+    await _getBackupAlbumsInfo();
+    await _updateServerInfo();
+    await _updateBackupAssetCount();
   }
   }
 
 
   ///
   ///
@@ -256,11 +260,10 @@ class BackupNotifier extends StateNotifier<BackUpState> {
   /// Invoke backup process
   /// Invoke backup process
   ///
   ///
   void startBackupProcess() async {
   void startBackupProcess() async {
-    _updateServerInfo();
-    _updateBackupAssetCount();
-
     state = state.copyWith(backupProgress: BackUpProgressEnum.inProgress);
     state = state.copyWith(backupProgress: BackUpProgressEnum.inProgress);
 
 
+    await getBackupInfo();
+
     var authResult = await PhotoManager.requestPermissionExtend();
     var authResult = await PhotoManager.requestPermissionExtend();
     if (authResult.isAuth) {
     if (authResult.isAuth) {
       await PhotoManager.clearFileCache();
       await PhotoManager.clearFileCache();
@@ -271,10 +274,10 @@ class BackupNotifier extends StateNotifier<BackUpState> {
         return;
         return;
       }
       }
 
 
-      Set<AssetEntity> assetsWillBeBackup = state.allUniqueAssets;
+      Set<AssetEntity> assetsWillBeBackup = Set.from(state.allUniqueAssets);
 
 
       // Remove item that has already been backed up
       // Remove item that has already been backed up
-      for (var assetId in state.allAssetOnDatabase) {
+      for (var assetId in state.allAssetsInDatabase) {
         assetsWillBeBackup.removeWhere((e) => e.id == assetId);
         assetsWillBeBackup.removeWhere((e) => e.id == assetId);
       }
       }
 
 
@@ -301,8 +304,8 @@ class BackupNotifier extends StateNotifier<BackUpState> {
     state = state.copyWith(selectedAlbumsBackupAssetsIds: {
     state = state.copyWith(selectedAlbumsBackupAssetsIds: {
       ...state.selectedAlbumsBackupAssetsIds,
       ...state.selectedAlbumsBackupAssetsIds,
       deviceAssetId
       deviceAssetId
-    }, allAssetOnDatabase: [
-      ...state.allAssetOnDatabase,
+    }, allAssetsInDatabase: [
+      ...state.allAssetsInDatabase,
       deviceAssetId
       deviceAssetId
     ]);
     ]);
 
 
@@ -321,7 +324,7 @@ class BackupNotifier extends StateNotifier<BackUpState> {
         progressInPercentage: (sent.toDouble() / total.toDouble() * 100));
         progressInPercentage: (sent.toDouble() / total.toDouble() * 100));
   }
   }
 
 
-  void _updateServerInfo() async {
+  Future<void> _updateServerInfo() async {
     var serverInfo = await _serverInfoService.getServerInfo();
     var serverInfo = await _serverInfoService.getServerInfo();
 
 
     // Update server info
     // Update server info

+ 41 - 17
mobile/lib/modules/backup/ui/album_info_card.dart

@@ -14,16 +14,22 @@ class AlbumInfoCard extends HookConsumerWidget {
   final Uint8List? imageData;
   final Uint8List? imageData;
   final AssetPathEntity albumInfo;
   final AssetPathEntity albumInfo;
 
 
-  const AlbumInfoCard({Key? key, this.imageData, required this.albumInfo}) : super(key: key);
+  const AlbumInfoCard({Key? key, this.imageData, required this.albumInfo})
+      : super(key: key);
 
 
   @override
   @override
   Widget build(BuildContext context, WidgetRef ref) {
   Widget build(BuildContext context, WidgetRef ref) {
-    final bool isSelected = ref.watch(backupProvider).selectedBackupAlbums.contains(albumInfo);
-    final bool isExcluded = ref.watch(backupProvider).excludedBackupAlbums.contains(albumInfo);
+    final bool isSelected =
+        ref.watch(backupProvider).selectedBackupAlbums.contains(albumInfo);
+    final bool isExcluded =
+        ref.watch(backupProvider).excludedBackupAlbums.contains(albumInfo);
 
 
-    ColorFilter selectedFilter = ColorFilter.mode(Theme.of(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);
+    ColorFilter selectedFilter = ColorFilter.mode(
+        Theme.of(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);
 
 
     _buildSelectedTextBox() {
     _buildSelectedTextBox() {
       if (isSelected) {
       if (isSelected) {
@@ -32,7 +38,8 @@ class AlbumInfoCard extends HookConsumerWidget {
           shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(5)),
           shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(5)),
           label: const Text(
           label: const Text(
             "INCLUDED",
             "INCLUDED",
-            style: TextStyle(fontSize: 10, color: Colors.white, fontWeight: FontWeight.bold),
+            style: TextStyle(
+                fontSize: 10, color: Colors.white, fontWeight: FontWeight.bold),
           ),
           ),
           backgroundColor: Theme.of(context).primaryColor,
           backgroundColor: Theme.of(context).primaryColor,
         );
         );
@@ -42,7 +49,8 @@ class AlbumInfoCard extends HookConsumerWidget {
           shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(5)),
           shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(5)),
           label: const Text(
           label: const Text(
             "EXCLUDED",
             "EXCLUDED",
-            style: TextStyle(fontSize: 10, color: Colors.white, fontWeight: FontWeight.bold),
+            style: TextStyle(
+                fontSize: 10, color: Colors.white, fontWeight: FontWeight.bold),
           ),
           ),
           backgroundColor: Colors.red[300],
           backgroundColor: Colors.red[300],
         );
         );
@@ -85,10 +93,15 @@ class AlbumInfoCard extends HookConsumerWidget {
         HapticFeedback.selectionClick();
         HapticFeedback.selectionClick();
 
 
         if (isExcluded) {
         if (isExcluded) {
-          ref.watch(backupProvider.notifier).removeExcludedAlbumForBackup(albumInfo);
+          ref
+              .watch(backupProvider.notifier)
+              .removeExcludedAlbumForBackup(albumInfo);
         } else {
         } else {
           if (ref.watch(backupProvider).selectedBackupAlbums.length == 1 &&
           if (ref.watch(backupProvider).selectedBackupAlbums.length == 1 &&
-              ref.watch(backupProvider).selectedBackupAlbums.contains(albumInfo)) {
+              ref
+                  .watch(backupProvider)
+                  .selectedBackupAlbums
+                  .contains(albumInfo)) {
             ImmichToast.show(
             ImmichToast.show(
               context: context,
               context: context,
               msg: "Cannot exclude the only album",
               msg: "Cannot exclude the only album",
@@ -98,7 +111,9 @@ class AlbumInfoCard extends HookConsumerWidget {
             return;
             return;
           }
           }
 
 
-          ref.watch(backupProvider.notifier).addExcludedAlbumForBackup(albumInfo);
+          ref
+              .watch(backupProvider.notifier)
+              .addExcludedAlbumForBackup(albumInfo);
         }
         }
       },
       },
       child: Card(
       child: Card(
@@ -121,12 +136,16 @@ class AlbumInfoCard extends HookConsumerWidget {
                   width: 200,
                   width: 200,
                   height: 200,
                   height: 200,
                   decoration: BoxDecoration(
                   decoration: BoxDecoration(
-                    borderRadius: const BorderRadius.only(topLeft: Radius.circular(12), topRight: Radius.circular(12)),
+                    borderRadius: const BorderRadius.only(
+                        topLeft: Radius.circular(12),
+                        topRight: Radius.circular(12)),
                     image: DecorationImage(
                     image: DecorationImage(
                       colorFilter: _buildImageFilter(),
                       colorFilter: _buildImageFilter(),
                       image: imageData != null
                       image: imageData != null
                           ? MemoryImage(imageData!)
                           ? MemoryImage(imageData!)
-                          : const AssetImage('assets/immich-logo-no-outline.png') as ImageProvider,
+                          : const AssetImage(
+                                  'assets/immich-logo-no-outline.png')
+                              as ImageProvider,
                       fit: BoxFit.cover,
                       fit: BoxFit.cover,
                     ),
                     ),
                   ),
                   ),
@@ -150,13 +169,17 @@ class AlbumInfoCard extends HookConsumerWidget {
                           Text(
                           Text(
                             albumInfo.name,
                             albumInfo.name,
                             style: TextStyle(
                             style: TextStyle(
-                                fontSize: 14, color: Theme.of(context).primaryColor, fontWeight: FontWeight.bold),
+                                fontSize: 14,
+                                color: Theme.of(context).primaryColor,
+                                fontWeight: FontWeight.bold),
                           ),
                           ),
                           Padding(
                           Padding(
                             padding: const EdgeInsets.only(top: 2.0),
                             padding: const EdgeInsets.only(top: 2.0),
                             child: Text(
                             child: Text(
-                              albumInfo.assetCount.toString() + (albumInfo.isAll ? " (ALL)" : ""),
-                              style: TextStyle(fontSize: 12, color: Colors.grey[600]),
+                              albumInfo.assetCount.toString() +
+                                  (albumInfo.isAll ? " (ALL)" : ""),
+                              style: TextStyle(
+                                  fontSize: 12, color: Colors.grey[600]),
                             ),
                             ),
                           )
                           )
                         ],
                         ],
@@ -165,7 +188,8 @@ class AlbumInfoCard extends HookConsumerWidget {
                   ),
                   ),
                   IconButton(
                   IconButton(
                     onPressed: () {
                     onPressed: () {
-                      AutoRouter.of(context).push(AlbumPreviewRoute(album: albumInfo));
+                      AutoRouter.of(context)
+                          .push(AlbumPreviewRoute(album: albumInfo));
                     },
                     },
                     icon: Icon(
                     icon: Icon(
                       Icons.image_outlined,
                       Icons.image_outlined,

+ 6 - 1
mobile/lib/modules/backup/ui/backup_info_card.dart

@@ -4,7 +4,12 @@ class BackupInfoCard extends StatelessWidget {
   final String title;
   final String title;
   final String subtitle;
   final String subtitle;
   final String info;
   final String info;
-  const BackupInfoCard({Key? key, required this.title, required this.subtitle, required this.info}) : super(key: key);
+  const BackupInfoCard(
+      {Key? key,
+      required this.title,
+      required this.subtitle,
+      required this.info})
+      : super(key: key);
 
 
   @override
   @override
   Widget build(BuildContext context) {
   Widget build(BuildContext context) {

+ 9 - 4
mobile/lib/modules/backup/views/album_preview_page.dart

@@ -16,7 +16,8 @@ class AlbumPreviewPage extends HookConsumerWidget {
     final assets = useState<List<AssetEntity>>([]);
     final assets = useState<List<AssetEntity>>([]);
 
 
     _getAssetsInAlbum() async {
     _getAssetsInAlbum() async {
-      assets.value = await album.getAssetListRange(start: 0, end: album.assetCount);
+      assets.value =
+          await album.getAssetListRange(start: 0, end: album.assetCount);
     }
     }
 
 
     useEffect(() {
     useEffect(() {
@@ -37,7 +38,10 @@ class AlbumPreviewPage extends HookConsumerWidget {
               padding: const EdgeInsets.only(top: 4.0),
               padding: const EdgeInsets.only(top: 4.0),
               child: Text(
               child: Text(
                 "ID ${album.id}",
                 "ID ${album.id}",
-                style: TextStyle(fontSize: 10, color: Colors.grey[600], fontWeight: FontWeight.bold),
+                style: TextStyle(
+                    fontSize: 10,
+                    color: Colors.grey[600],
+                    fontWeight: FontWeight.bold),
               ),
               ),
             ),
             ),
           ],
           ],
@@ -55,8 +59,9 @@ class AlbumPreviewPage extends HookConsumerWidget {
         ),
         ),
         itemCount: assets.value.length,
         itemCount: assets.value.length,
         itemBuilder: (context, index) {
         itemBuilder: (context, index) {
-          Future<Uint8List?> thumbData =
-              assets.value[index].thumbnailDataWithSize(const ThumbnailSize(200, 200), quality: 50);
+          Future<Uint8List?> thumbData = assets.value[index]
+              .thumbnailDataWithSize(const ThumbnailSize(200, 200),
+                  quality: 50);
 
 
           return FutureBuilder<Uint8List?>(
           return FutureBuilder<Uint8List?>(
             future: thumbData,
             future: thumbData,

+ 39 - 13
mobile/lib/modules/backup/views/backup_album_selection_page.dart

@@ -17,7 +17,7 @@ class BackupAlbumSelectionPage extends HookConsumerWidget {
     final excludedBackupAlbums = ref.watch(backupProvider).excludedBackupAlbums;
     final excludedBackupAlbums = ref.watch(backupProvider).excludedBackupAlbums;
 
 
     useEffect(() {
     useEffect(() {
-      ref.read(backupProvider.notifier).getBackupAlbumsInfo();
+      ref.read(backupProvider.notifier).getBackupInfo();
       return null;
       return null;
     }, []);
     }, []);
 
 
@@ -37,8 +37,12 @@ class BackupAlbumSelectionPage extends HookConsumerWidget {
           itemBuilder: ((context, index) {
           itemBuilder: ((context, index) {
             var thumbnailData = availableAlbums[index].thumbnailData;
             var thumbnailData = availableAlbums[index].thumbnailData;
             return Padding(
             return Padding(
-              padding: index == 0 ? const EdgeInsets.only(left: 16.00) : const EdgeInsets.all(0),
-              child: AlbumInfoCard(imageData: thumbnailData, albumInfo: availableAlbums[index].albumEntity),
+              padding: index == 0
+                  ? const EdgeInsets.only(left: 16.00)
+                  : const EdgeInsets.all(0),
+              child: AlbumInfoCard(
+                  imageData: thumbnailData,
+                  albumInfo: availableAlbums[index].albumEntity),
             );
             );
           }),
           }),
         ),
         ),
@@ -67,10 +71,14 @@ class BackupAlbumSelectionPage extends HookConsumerWidget {
             onTap: removeSelection,
             onTap: removeSelection,
             child: Chip(
             child: Chip(
               visualDensity: VisualDensity.compact,
               visualDensity: VisualDensity.compact,
-              shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(5)),
+              shape: RoundedRectangleBorder(
+                  borderRadius: BorderRadius.circular(5)),
               label: Text(
               label: Text(
                 album.name,
                 album.name,
-                style: const TextStyle(fontSize: 10, color: Colors.white, fontWeight: FontWeight.bold),
+                style: const TextStyle(
+                    fontSize: 10,
+                    color: Colors.white,
+                    fontWeight: FontWeight.bold),
               ),
               ),
               backgroundColor: Theme.of(context).primaryColor,
               backgroundColor: Theme.of(context).primaryColor,
               deleteIconColor: Colors.white,
               deleteIconColor: Colors.white,
@@ -88,7 +96,9 @@ class BackupAlbumSelectionPage extends HookConsumerWidget {
     _buildExcludedAlbumNameChip() {
     _buildExcludedAlbumNameChip() {
       return excludedBackupAlbums.map((album) {
       return excludedBackupAlbums.map((album) {
         void removeSelection() {
         void removeSelection() {
-          ref.watch(backupProvider.notifier).removeExcludedAlbumForBackup(album);
+          ref
+              .watch(backupProvider.notifier)
+              .removeExcludedAlbumForBackup(album);
         }
         }
 
 
         return GestureDetector(
         return GestureDetector(
@@ -97,10 +107,14 @@ class BackupAlbumSelectionPage extends HookConsumerWidget {
             padding: const EdgeInsets.only(right: 8.0),
             padding: const EdgeInsets.only(right: 8.0),
             child: Chip(
             child: Chip(
               visualDensity: VisualDensity.compact,
               visualDensity: VisualDensity.compact,
-              shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(5)),
+              shape: RoundedRectangleBorder(
+                  borderRadius: BorderRadius.circular(5)),
               label: Text(
               label: Text(
                 album.name,
                 album.name,
-                style: const TextStyle(fontSize: 10, color: Colors.white, fontWeight: FontWeight.bold),
+                style: const TextStyle(
+                    fontSize: 10,
+                    color: Colors.white,
+                    fontWeight: FontWeight.bold),
               ),
               ),
               backgroundColor: Colors.red[300],
               backgroundColor: Colors.red[300],
               deleteIconColor: Colors.white,
               deleteIconColor: Colors.white,
@@ -142,7 +156,10 @@ class BackupAlbumSelectionPage extends HookConsumerWidget {
           Padding(
           Padding(
             padding: const EdgeInsets.symmetric(horizontal: 16.0),
             padding: const EdgeInsets.symmetric(horizontal: 16.0),
             child: Wrap(
             child: Wrap(
-              children: [..._buildSelectedAlbumNameChip(), ..._buildExcludedAlbumNameChip()],
+              children: [
+                ..._buildSelectedAlbumNameChip(),
+                ..._buildExcludedAlbumNameChip()
+              ],
             ),
             ),
           ),
           ),
 
 
@@ -165,10 +182,17 @@ class BackupAlbumSelectionPage extends HookConsumerWidget {
                     visualDensity: VisualDensity.compact,
                     visualDensity: VisualDensity.compact,
                     title: Text(
                     title: Text(
                       "Total unique assets",
                       "Total unique assets",
-                      style: TextStyle(fontWeight: FontWeight.bold, fontSize: 14, color: Colors.grey[700]),
+                      style: TextStyle(
+                          fontWeight: FontWeight.bold,
+                          fontSize: 14,
+                          color: Colors.grey[700]),
                     ),
                     ),
                     trailing: Text(
                     trailing: Text(
-                      ref.watch(backupProvider).allUniqueAssets.length.toString(),
+                      ref
+                          .watch(backupProvider)
+                          .allUniqueAssets
+                          .length
+                          .toString(),
                       style: const TextStyle(fontWeight: FontWeight.bold),
                       style: const TextStyle(fontWeight: FontWeight.bold),
                     ),
                     ),
                   ),
                   ),
@@ -206,7 +230,8 @@ class BackupAlbumSelectionPage extends HookConsumerWidget {
                   context: context,
                   context: context,
                   builder: (BuildContext context) {
                   builder: (BuildContext context) {
                     return AlertDialog(
                     return AlertDialog(
-                      shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
+                      shape: RoundedRectangleBorder(
+                          borderRadius: BorderRadius.circular(12)),
                       elevation: 5,
                       elevation: 5,
                       title: Text(
                       title: Text(
                         'Selection Info',
                         'Selection Info',
@@ -221,7 +246,8 @@ class BackupAlbumSelectionPage extends HookConsumerWidget {
                           children: [
                           children: [
                             Text(
                             Text(
                               'Assets can scatter across multiple albums. Thus, albums can be included or excluded during the backup process.',
                               'Assets can scatter across multiple albums. Thus, albums can be included or excluded during the backup process.',
-                              style: TextStyle(fontSize: 14, color: Colors.grey[700]),
+                              style: TextStyle(
+                                  fontSize: 14, color: Colors.grey[700]),
                             ),
                             ),
                           ],
                           ],
                         ),
                         ),

+ 13 - 13
mobile/lib/modules/backup/views/backup_controller_page.dart

@@ -26,7 +26,7 @@ class BackupControllerPage extends HookConsumerWidget {
 
 
     useEffect(() {
     useEffect(() {
       if (backupState.backupProgress != BackUpProgressEnum.inProgress) {
       if (backupState.backupProgress != BackUpProgressEnum.inProgress) {
-        ref.read(backupProvider.notifier).getBackupInfo();
+        ref.watch(backupProvider.notifier).getBackupInfo();
       }
       }
 
 
       ref
       ref
@@ -112,13 +112,15 @@ class BackupControllerPage extends HookConsumerWidget {
                     ),
                     ),
                   ),
                   ),
                   onPressed: () {
                   onPressed: () {
-                    isAutoBackup
-                        ? ref
-                            .watch(authenticationProvider.notifier)
-                            .setAutoBackup(false)
-                        : ref
-                            .watch(authenticationProvider.notifier)
-                            .setAutoBackup(true);
+                    if (isAutoBackup) {
+                      ref
+                          .read(authenticationProvider.notifier)
+                          .setAutoBackup(false);
+                    } else {
+                      ref
+                          .read(authenticationProvider.notifier)
+                          .setAutoBackup(true);
+                    }
                   },
                   },
                   child: Text("Turn $backupBtnText Backup",
                   child: Text("Turn $backupBtnText Backup",
                       style: const TextStyle(fontWeight: FontWeight.bold)),
                       style: const TextStyle(fontWeight: FontWeight.bold)),
@@ -212,7 +214,7 @@ class BackupControllerPage extends HookConsumerWidget {
               crossAxisAlignment: CrossAxisAlignment.start,
               crossAxisAlignment: CrossAxisAlignment.start,
               children: [
               children: [
                 const Text(
                 const Text(
-                  "Albums to be backup",
+                  "Albums to be backed up",
                   style: TextStyle(color: Color(0xFF808080), fontSize: 12),
                   style: TextStyle(color: Color(0xFF808080), fontSize: 12),
                 ),
                 ),
                 _buildSelectedAlbumName(),
                 _buildSelectedAlbumName(),
@@ -282,14 +284,12 @@ class BackupControllerPage extends HookConsumerWidget {
             ),
             ),
             BackupInfoCard(
             BackupInfoCard(
               title: "Backup",
               title: "Backup",
-              subtitle:
-                  "Photos and videos from selected albums that are backup",
+              subtitle: "Backed up photos and videos",
               info: "${backupState.selectedAlbumsBackupAssetsIds.length}",
               info: "${backupState.selectedAlbumsBackupAssetsIds.length}",
             ),
             ),
             BackupInfoCard(
             BackupInfoCard(
               title: "Remainder",
               title: "Remainder",
-              subtitle:
-                  "Photos and videos that has not been backing up from selected albums",
+              subtitle: "Remaining photos and albums to back up from selection",
               info:
               info:
                   "${backupState.allUniqueAssets.length - backupState.selectedAlbumsBackupAssetsIds.length}",
                   "${backupState.allUniqueAssets.length - backupState.selectedAlbumsBackupAssetsIds.length}",
             ),
             ),

+ 5 - 2
mobile/lib/modules/home/models/delete_asset_response.model.dart

@@ -35,7 +35,8 @@ class DeleteAssetResponse {
 
 
   String toJson() => json.encode(toMap());
   String toJson() => json.encode(toMap());
 
 
-  factory DeleteAssetResponse.fromJson(String source) => DeleteAssetResponse.fromMap(json.decode(source));
+  factory DeleteAssetResponse.fromJson(String source) =>
+      DeleteAssetResponse.fromMap(json.decode(source));
 
 
   @override
   @override
   String toString() => 'DeleteAssetResponse(id: $id, status: $status)';
   String toString() => 'DeleteAssetResponse(id: $id, status: $status)';
@@ -44,7 +45,9 @@ class DeleteAssetResponse {
   bool operator ==(Object other) {
   bool operator ==(Object other) {
     if (identical(this, other)) return true;
     if (identical(this, other)) return true;
 
 
-    return other is DeleteAssetResponse && other.id == id && other.status == status;
+    return other is DeleteAssetResponse &&
+        other.id == id &&
+        other.status == status;
   }
   }
 
 
   @override
   @override

+ 13 - 6
mobile/lib/modules/home/models/get_all_asset_response.model.dart

@@ -31,13 +31,15 @@ class ImmichAssetGroupByDate {
   factory ImmichAssetGroupByDate.fromMap(Map<String, dynamic> map) {
   factory ImmichAssetGroupByDate.fromMap(Map<String, dynamic> map) {
     return ImmichAssetGroupByDate(
     return ImmichAssetGroupByDate(
       date: map['date'] ?? '',
       date: map['date'] ?? '',
-      assets: List<ImmichAsset>.from(map['assets']?.map((x) => ImmichAsset.fromMap(x))),
+      assets: List<ImmichAsset>.from(
+          map['assets']?.map((x) => ImmichAsset.fromMap(x))),
     );
     );
   }
   }
 
 
   String toJson() => json.encode(toMap());
   String toJson() => json.encode(toMap());
 
 
-  factory ImmichAssetGroupByDate.fromJson(String source) => ImmichAssetGroupByDate.fromMap(json.decode(source));
+  factory ImmichAssetGroupByDate.fromJson(String source) =>
+      ImmichAssetGroupByDate.fromMap(json.decode(source));
 
 
   @override
   @override
   String toString() => 'ImmichAssetGroupByDate(date: $date, assets: $assets)';
   String toString() => 'ImmichAssetGroupByDate(date: $date, assets: $assets)';
@@ -46,7 +48,9 @@ class ImmichAssetGroupByDate {
   bool operator ==(Object other) {
   bool operator ==(Object other) {
     if (identical(this, other)) return true;
     if (identical(this, other)) return true;
 
 
-    return other is ImmichAssetGroupByDate && other.date == date && listEquals(other.assets, assets);
+    return other is ImmichAssetGroupByDate &&
+        other.date == date &&
+        listEquals(other.assets, assets);
   }
   }
 
 
   @override
   @override
@@ -86,17 +90,20 @@ class GetAllAssetResponse {
   factory GetAllAssetResponse.fromMap(Map<String, dynamic> map) {
   factory GetAllAssetResponse.fromMap(Map<String, dynamic> map) {
     return GetAllAssetResponse(
     return GetAllAssetResponse(
       count: map['count']?.toInt() ?? 0,
       count: map['count']?.toInt() ?? 0,
-      data: List<ImmichAssetGroupByDate>.from(map['data']?.map((x) => ImmichAssetGroupByDate.fromMap(x))),
+      data: List<ImmichAssetGroupByDate>.from(
+          map['data']?.map((x) => ImmichAssetGroupByDate.fromMap(x))),
       nextPageKey: map['nextPageKey'] ?? '',
       nextPageKey: map['nextPageKey'] ?? '',
     );
     );
   }
   }
 
 
   String toJson() => json.encode(toMap());
   String toJson() => json.encode(toMap());
 
 
-  factory GetAllAssetResponse.fromJson(String source) => GetAllAssetResponse.fromMap(json.decode(source));
+  factory GetAllAssetResponse.fromJson(String source) =>
+      GetAllAssetResponse.fromMap(json.decode(source));
 
 
   @override
   @override
-  String toString() => 'GetAllAssetResponse(count: $count, data: $data, nextPageKey: $nextPageKey)';
+  String toString() =>
+      'GetAllAssetResponse(count: $count, data: $data, nextPageKey: $nextPageKey)';
 
 
   @override
   @override
   bool operator ==(Object other) {
   bool operator ==(Object other) {

+ 8 - 3
mobile/lib/modules/home/models/home_page_state.model.dart

@@ -37,14 +37,16 @@ class HomePageState {
   factory HomePageState.fromMap(Map<String, dynamic> map) {
   factory HomePageState.fromMap(Map<String, dynamic> map) {
     return HomePageState(
     return HomePageState(
       isMultiSelectEnable: map['isMultiSelectEnable'] ?? false,
       isMultiSelectEnable: map['isMultiSelectEnable'] ?? false,
-      selectedItems: Set<ImmichAsset>.from(map['selectedItems']?.map((x) => ImmichAsset.fromMap(x))),
+      selectedItems: Set<ImmichAsset>.from(
+          map['selectedItems']?.map((x) => ImmichAsset.fromMap(x))),
       selectedDateGroup: Set<String>.from(map['selectedDateGroup']),
       selectedDateGroup: Set<String>.from(map['selectedDateGroup']),
     );
     );
   }
   }
 
 
   String toJson() => json.encode(toMap());
   String toJson() => json.encode(toMap());
 
 
-  factory HomePageState.fromJson(String source) => HomePageState.fromMap(json.decode(source));
+  factory HomePageState.fromJson(String source) =>
+      HomePageState.fromMap(json.decode(source));
 
 
   @override
   @override
   String toString() =>
   String toString() =>
@@ -62,5 +64,8 @@ class HomePageState {
   }
   }
 
 
   @override
   @override
-  int get hashCode => isMultiSelectEnable.hashCode ^ selectedItems.hashCode ^ selectedDateGroup.hashCode;
+  int get hashCode =>
+      isMultiSelectEnable.hashCode ^
+      selectedItems.hashCode ^
+      selectedDateGroup.hashCode;
 }
 }

+ 8 - 4
mobile/lib/modules/home/providers/home_page_state.provider.dart

@@ -13,7 +13,8 @@ class HomePageStateNotifier extends StateNotifier<HomePageState> {
         );
         );
 
 
   void addSelectedDateGroup(String dateGroupTitle) {
   void addSelectedDateGroup(String dateGroupTitle) {
-    state = state.copyWith(selectedDateGroup: {...state.selectedDateGroup, dateGroupTitle});
+    state = state.copyWith(
+        selectedDateGroup: {...state.selectedDateGroup, dateGroupTitle});
   }
   }
 
 
   void removeSelectedDateGroup(String dateGroupTitle) {
   void removeSelectedDateGroup(String dateGroupTitle) {
@@ -25,11 +26,13 @@ class HomePageStateNotifier extends StateNotifier<HomePageState> {
   }
   }
 
 
   void enableMultiSelect(Set<ImmichAsset> selectedItems) {
   void enableMultiSelect(Set<ImmichAsset> selectedItems) {
-    state = state.copyWith(isMultiSelectEnable: true, selectedItems: selectedItems);
+    state =
+        state.copyWith(isMultiSelectEnable: true, selectedItems: selectedItems);
   }
   }
 
 
   void disableMultiSelect() {
   void disableMultiSelect() {
-    state = state.copyWith(isMultiSelectEnable: false, selectedItems: {}, selectedDateGroup: {});
+    state = state.copyWith(
+        isMultiSelectEnable: false, selectedItems: {}, selectedDateGroup: {});
   }
   }
 
 
   void addSingleSelectedItem(ImmichAsset asset) {
   void addSingleSelectedItem(ImmichAsset asset) {
@@ -60,4 +63,5 @@ class HomePageStateNotifier extends StateNotifier<HomePageState> {
 }
 }
 
 
 final homePageStateProvider =
 final homePageStateProvider =
-    StateNotifierProvider<HomePageStateNotifier, HomePageState>(((ref) => HomePageStateNotifier()));
+    StateNotifierProvider<HomePageStateNotifier, HomePageState>(
+        ((ref) => HomePageStateNotifier()));

+ 7 - 2
mobile/lib/modules/home/ui/control_bottom_app_bar.dart

@@ -13,7 +13,8 @@ class ControlBottomAppBar extends StatelessWidget {
         width: MediaQuery.of(context).size.width,
         width: MediaQuery.of(context).size.width,
         height: MediaQuery.of(context).size.height * 0.15,
         height: MediaQuery.of(context).size.height * 0.15,
         decoration: BoxDecoration(
         decoration: BoxDecoration(
-          borderRadius: const BorderRadius.only(topLeft: Radius.circular(15), topRight: Radius.circular(15)),
+          borderRadius: const BorderRadius.only(
+              topLeft: Radius.circular(15), topRight: Radius.circular(15)),
           color: Colors.grey[300]?.withOpacity(0.98),
           color: Colors.grey[300]?.withOpacity(0.98),
         ),
         ),
         child: Column(
         child: Column(
@@ -46,7 +47,11 @@ class ControlBottomAppBar extends StatelessWidget {
 }
 }
 
 
 class ControlBoxButton extends StatelessWidget {
 class ControlBoxButton extends StatelessWidget {
-  const ControlBoxButton({Key? key, required this.label, required this.iconData, required this.onPressed})
+  const ControlBoxButton(
+      {Key? key,
+      required this.label,
+      required this.iconData,
+      required this.onPressed})
       : super(key: key);
       : super(key: key);
 
 
   final String label;
   final String label;

+ 35 - 13
mobile/lib/modules/home/ui/daily_title_text.dart

@@ -18,9 +18,12 @@ class DailyTitleText extends ConsumerWidget {
   Widget build(BuildContext context, WidgetRef ref) {
   Widget build(BuildContext context, WidgetRef ref) {
     var currentYear = DateTime.now().year;
     var currentYear = DateTime.now().year;
     var groupYear = DateTime.parse(isoDate).year;
     var groupYear = DateTime.parse(isoDate).year;
-    var formatDateTemplate = currentYear == groupYear ? 'E, MMM dd' : 'E, MMM dd, yyyy';
-    var dateText = DateFormat(formatDateTemplate).format(DateTime.parse(isoDate));
-    var isMultiSelectEnable = ref.watch(homePageStateProvider).isMultiSelectEnable;
+    var formatDateTemplate =
+        currentYear == groupYear ? 'E, MMM dd' : 'E, MMM dd, yyyy';
+    var dateText =
+        DateFormat(formatDateTemplate).format(DateTime.parse(isoDate));
+    var isMultiSelectEnable =
+        ref.watch(homePageStateProvider).isMultiSelectEnable;
     var selectedDateGroup = ref.watch(homePageStateProvider).selectedDateGroup;
     var selectedDateGroup = ref.watch(homePageStateProvider).selectedDateGroup;
     var selectedItems = ref.watch(homePageStateProvider).selectedItems;
     var selectedItems = ref.watch(homePageStateProvider).selectedItems;
 
 
@@ -35,23 +38,42 @@ class DailyTitleText extends ConsumerWidget {
           selectedDateGroup.contains(dateText) &&
           selectedDateGroup.contains(dateText) &&
           selectedItems.length != assetGroup.length) {
           selectedItems.length != assetGroup.length) {
         // Multi select is active - click again on the icon while it is not the only active group -> remove that group from selected group/items
         // Multi select is active - click again on the icon while it is not the only active group -> remove that group from selected group/items
-        ref.watch(homePageStateProvider.notifier).removeSelectedDateGroup(dateText);
-        ref.watch(homePageStateProvider.notifier).removeMultipleSelectedItem(assetGroup);
-      } else if (isMultiSelectEnable && selectedDateGroup.contains(dateText) && selectedDateGroup.length > 1) {
-        ref.watch(homePageStateProvider.notifier).removeSelectedDateGroup(dateText);
-        ref.watch(homePageStateProvider.notifier).removeMultipleSelectedItem(assetGroup);
+        ref
+            .watch(homePageStateProvider.notifier)
+            .removeSelectedDateGroup(dateText);
+        ref
+            .watch(homePageStateProvider.notifier)
+            .removeMultipleSelectedItem(assetGroup);
+      } else if (isMultiSelectEnable &&
+          selectedDateGroup.contains(dateText) &&
+          selectedDateGroup.length > 1) {
+        ref
+            .watch(homePageStateProvider.notifier)
+            .removeSelectedDateGroup(dateText);
+        ref
+            .watch(homePageStateProvider.notifier)
+            .removeMultipleSelectedItem(assetGroup);
       } else if (isMultiSelectEnable && !selectedDateGroup.contains(dateText)) {
       } else if (isMultiSelectEnable && !selectedDateGroup.contains(dateText)) {
-        ref.watch(homePageStateProvider.notifier).addSelectedDateGroup(dateText);
-        ref.watch(homePageStateProvider.notifier).addMultipleSelectedItems(assetGroup);
+        ref
+            .watch(homePageStateProvider.notifier)
+            .addSelectedDateGroup(dateText);
+        ref
+            .watch(homePageStateProvider.notifier)
+            .addMultipleSelectedItems(assetGroup);
       } else {
       } else {
-        ref.watch(homePageStateProvider.notifier).enableMultiSelect(assetGroup.toSet());
-        ref.watch(homePageStateProvider.notifier).addSelectedDateGroup(dateText);
+        ref
+            .watch(homePageStateProvider.notifier)
+            .enableMultiSelect(assetGroup.toSet());
+        ref
+            .watch(homePageStateProvider.notifier)
+            .addSelectedDateGroup(dateText);
       }
       }
     }
     }
 
 
     return SliverToBoxAdapter(
     return SliverToBoxAdapter(
       child: Padding(
       child: Padding(
-        padding: const EdgeInsets.only(top: 29.0, bottom: 29.0, left: 12.0, right: 12.0),
+        padding: const EdgeInsets.only(
+            top: 29.0, bottom: 29.0, left: 12.0, right: 12.0),
         child: Row(
         child: Row(
           children: [
           children: [
             Text(
             Text(

+ 5 - 2
mobile/lib/modules/home/ui/delete_diaglog.dart

@@ -14,7 +14,8 @@ class DeleteDialog extends ConsumerWidget {
       backgroundColor: Colors.grey[200],
       backgroundColor: Colors.grey[200],
       shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(10)),
       shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(10)),
       title: const Text("Delete Permanently"),
       title: const Text("Delete Permanently"),
-      content: const Text("These items will be permanently deleted from Immich and from your device"),
+      content: const Text(
+          "These items will be permanently deleted from Immich and from your device"),
       actions: [
       actions: [
         TextButton(
         TextButton(
           onPressed: () {
           onPressed: () {
@@ -27,7 +28,9 @@ class DeleteDialog extends ConsumerWidget {
         ),
         ),
         TextButton(
         TextButton(
           onPressed: () {
           onPressed: () {
-            ref.watch(assetProvider.notifier).deleteAssets(homePageState.selectedItems);
+            ref
+                .watch(assetProvider.notifier)
+                .deleteAssets(homePageState.selectedItems);
             ref.watch(homePageStateProvider.notifier).disableMultiSelect();
             ref.watch(homePageStateProvider.notifier).disableMultiSelect();
 
 
             Navigator.of(context).pop();
             Navigator.of(context).pop();

+ 4 - 1
mobile/lib/modules/home/ui/image_grid.dart

@@ -33,7 +33,10 @@ class ImageGrid extends ConsumerWidget {
                         child: Row(
                         child: Row(
                           children: [
                           children: [
                             Text(
                             Text(
-                              assetGroup[index].duration.toString().substring(0, 7),
+                              assetGroup[index]
+                                  .duration
+                                  .toString()
+                                  .substring(0, 7),
                               style: const TextStyle(
                               style: const TextStyle(
                                 color: Colors.white,
                                 color: Colors.white,
                                 fontSize: 10,
                                 fontSize: 10,

+ 28 - 11
mobile/lib/modules/home/ui/thumbnail_image.dart

@@ -25,7 +25,8 @@ class ThumbnailImage extends HookConsumerWidget {
         '${box.get(serverEndpointKey)}/asset/file?aid=${asset.deviceAssetId}&did=${asset.deviceId}&isThumb=true';
         '${box.get(serverEndpointKey)}/asset/file?aid=${asset.deviceAssetId}&did=${asset.deviceId}&isThumb=true';
 
 
     var selectedAsset = ref.watch(homePageStateProvider).selectedItems;
     var selectedAsset = ref.watch(homePageStateProvider).selectedItems;
-    var isMultiSelectEnable = ref.watch(homePageStateProvider).isMultiSelectEnable;
+    var isMultiSelectEnable =
+        ref.watch(homePageStateProvider).isMultiSelectEnable;
     var deviceId = ref.watch(authenticationProvider).deviceId;
     var deviceId = ref.watch(authenticationProvider).deviceId;
 
 
     Widget _buildSelectionIcon(ImmichAsset asset) {
     Widget _buildSelectionIcon(ImmichAsset asset) {
@@ -45,12 +46,20 @@ class ThumbnailImage extends HookConsumerWidget {
     return GestureDetector(
     return GestureDetector(
       onTap: () {
       onTap: () {
         debugPrint("View ${asset.id}");
         debugPrint("View ${asset.id}");
-        if (isMultiSelectEnable && selectedAsset.contains(asset) && selectedAsset.length == 1) {
+        if (isMultiSelectEnable &&
+            selectedAsset.contains(asset) &&
+            selectedAsset.length == 1) {
           ref.watch(homePageStateProvider.notifier).disableMultiSelect();
           ref.watch(homePageStateProvider.notifier).disableMultiSelect();
-        } else if (isMultiSelectEnable && selectedAsset.contains(asset) && selectedAsset.length > 1) {
-          ref.watch(homePageStateProvider.notifier).removeSingleSelectedItem(asset);
+        } else if (isMultiSelectEnable &&
+            selectedAsset.contains(asset) &&
+            selectedAsset.length > 1) {
+          ref
+              .watch(homePageStateProvider.notifier)
+              .removeSingleSelectedItem(asset);
         } else if (isMultiSelectEnable && !selectedAsset.contains(asset)) {
         } else if (isMultiSelectEnable && !selectedAsset.contains(asset)) {
-          ref.watch(homePageStateProvider.notifier).addSingleSelectedItem(asset);
+          ref
+              .watch(homePageStateProvider.notifier)
+              .addSingleSelectedItem(asset);
         } else {
         } else {
           if (asset.type == 'IMAGE') {
           if (asset.type == 'IMAGE') {
             AutoRouter.of(context).push(
             AutoRouter.of(context).push(
@@ -65,7 +74,8 @@ class ThumbnailImage extends HookConsumerWidget {
           } else {
           } else {
             AutoRouter.of(context).push(
             AutoRouter.of(context).push(
               VideoViewerRoute(
               VideoViewerRoute(
-                  videoUrl: '${box.get(serverEndpointKey)}/asset/file?aid=${asset.deviceAssetId}&did=${asset.deviceId}',
+                  videoUrl:
+                      '${box.get(serverEndpointKey)}/asset/file?aid=${asset.deviceAssetId}&did=${asset.deviceId}',
                   asset: asset),
                   asset: asset),
             );
             );
           }
           }
@@ -83,7 +93,8 @@ class ThumbnailImage extends HookConsumerWidget {
             Container(
             Container(
               decoration: BoxDecoration(
               decoration: BoxDecoration(
                 border: isMultiSelectEnable && selectedAsset.contains(asset)
                 border: isMultiSelectEnable && selectedAsset.contains(asset)
-                    ? Border.all(color: Theme.of(context).primaryColorLight, width: 10)
+                    ? Border.all(
+                        color: Theme.of(context).primaryColorLight, width: 10)
                     : const Border(),
                     : const Border(),
               ),
               ),
               child: CachedNetworkImage(
               child: CachedNetworkImage(
@@ -93,11 +104,15 @@ class ThumbnailImage extends HookConsumerWidget {
                 memCacheHeight: asset.type == 'IMAGE' ? 250 : 400,
                 memCacheHeight: asset.type == 'IMAGE' ? 250 : 400,
                 fit: BoxFit.cover,
                 fit: BoxFit.cover,
                 imageUrl: thumbnailRequestUrl,
                 imageUrl: thumbnailRequestUrl,
-                httpHeaders: {"Authorization": "Bearer ${box.get(accessTokenKey)}"},
+                httpHeaders: {
+                  "Authorization": "Bearer ${box.get(accessTokenKey)}"
+                },
                 fadeInDuration: const Duration(milliseconds: 250),
                 fadeInDuration: const Duration(milliseconds: 250),
-                progressIndicatorBuilder: (context, url, downloadProgress) => Transform.scale(
+                progressIndicatorBuilder: (context, url, downloadProgress) =>
+                    Transform.scale(
                   scale: 0.2,
                   scale: 0.2,
-                  child: CircularProgressIndicator(value: downloadProgress.progress),
+                  child: CircularProgressIndicator(
+                      value: downloadProgress.progress),
                 ),
                 ),
                 errorWidget: (context, url, error) {
                 errorWidget: (context, url, error) {
                   return Icon(
                   return Icon(
@@ -122,7 +137,9 @@ class ThumbnailImage extends HookConsumerWidget {
               right: 10,
               right: 10,
               bottom: 5,
               bottom: 5,
               child: Icon(
               child: Icon(
-                (deviceId != asset.deviceId) ? Icons.cloud_done_outlined : Icons.photo_library_rounded,
+                (deviceId != asset.deviceId)
+                    ? Icons.cloud_done_outlined
+                    : Icons.photo_library_rounded,
                 color: Colors.white,
                 color: Colors.white,
                 size: 18,
                 size: 18,
               ),
               ),

+ 2 - 1
mobile/lib/modules/login/models/authentication_state.model.dart

@@ -98,7 +98,8 @@ class AuthenticationState {
 
 
   String toJson() => json.encode(toMap());
   String toJson() => json.encode(toMap());
 
 
-  factory AuthenticationState.fromJson(String source) => AuthenticationState.fromMap(json.decode(source));
+  factory AuthenticationState.fromJson(String source) =>
+      AuthenticationState.fromMap(json.decode(source));
 
 
   @override
   @override
   bool operator ==(Object other) {
   bool operator ==(Object other) {

+ 5 - 1
mobile/lib/modules/login/models/hive_saved_login_info.model.dart

@@ -16,5 +16,9 @@ class HiveSavedLoginInfo {
   @HiveField(3)
   @HiveField(3)
   bool isSaveLogin;
   bool isSaveLogin;
 
 
-  HiveSavedLoginInfo({required this.email, required this.password, required this.serverUrl, required this.isSaveLogin});
+  HiveSavedLoginInfo(
+      {required this.email,
+      required this.password,
+      required this.serverUrl,
+      required this.isSaveLogin});
 }
 }

+ 2 - 1
mobile/lib/modules/login/models/login_response.model.dart

@@ -73,7 +73,8 @@ class LogInReponse {
 
 
   String toJson() => json.encode(toMap());
   String toJson() => json.encode(toMap());
 
 
-  factory LogInReponse.fromJson(String source) => LogInReponse.fromMap(json.decode(source));
+  factory LogInReponse.fromJson(String source) =>
+      LogInReponse.fromMap(json.decode(source));
 
 
   @override
   @override
   String toString() {
   String toString() {

+ 33 - 14
mobile/lib/modules/login/ui/login_form.dart

@@ -15,13 +15,17 @@ class LoginForm extends HookConsumerWidget {
 
 
   @override
   @override
   Widget build(BuildContext context, WidgetRef ref) {
   Widget build(BuildContext context, WidgetRef ref) {
-    final usernameController = useTextEditingController.fromValue(TextEditingValue.empty);
-    final passwordController = useTextEditingController.fromValue(TextEditingValue.empty);
-    final serverEndpointController = useTextEditingController(text: 'http://your-server-ip:2283');
+    final usernameController =
+        useTextEditingController.fromValue(TextEditingValue.empty);
+    final passwordController =
+        useTextEditingController.fromValue(TextEditingValue.empty);
+    final serverEndpointController =
+        useTextEditingController(text: 'http://your-server-ip:2283');
     final isSaveLoginInfo = useState<bool>(false);
     final isSaveLoginInfo = useState<bool>(false);
 
 
     useEffect(() {
     useEffect(() {
-      var loginInfo = Hive.box<HiveSavedLoginInfo>(hiveLoginInfoBox).get(savedLoginInfoKey);
+      var loginInfo =
+          Hive.box<HiveSavedLoginInfo>(hiveLoginInfoBox).get(savedLoginInfoKey);
 
 
       if (loginInfo != null) {
       if (loginInfo != null) {
         usernameController.text = loginInfo.email;
         usernameController.text = loginInfo.email;
@@ -64,11 +68,15 @@ class LoginForm extends HookConsumerWidget {
                 contentPadding: const EdgeInsets.symmetric(horizontal: 8),
                 contentPadding: const EdgeInsets.symmetric(horizontal: 8),
                 dense: true,
                 dense: true,
                 side: const BorderSide(color: Colors.grey, width: 1.5),
                 side: const BorderSide(color: Colors.grey, width: 1.5),
-                shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(5)),
+                shape: RoundedRectangleBorder(
+                    borderRadius: BorderRadius.circular(5)),
                 enableFeedback: true,
                 enableFeedback: true,
                 title: const Text(
                 title: const Text(
                   "Stay logged in",
                   "Stay logged in",
-                  style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold, color: Colors.grey),
+                  style: TextStyle(
+                      fontSize: 16,
+                      fontWeight: FontWeight.bold,
+                      color: Colors.grey),
                 ),
                 ),
                 value: isSaveLoginInfo.value,
                 value: isSaveLoginInfo.value,
                 onChanged: (switchValue) {
                 onChanged: (switchValue) {
@@ -94,11 +102,13 @@ class LoginForm extends HookConsumerWidget {
 class ServerEndpointInput extends StatelessWidget {
 class ServerEndpointInput extends StatelessWidget {
   final TextEditingController controller;
   final TextEditingController controller;
 
 
-  const ServerEndpointInput({Key? key, required this.controller}) : super(key: key);
+  const ServerEndpointInput({Key? key, required this.controller})
+      : super(key: key);
 
 
   String? _validateInput(String? url) {
   String? _validateInput(String? url) {
     if (url == null) return null;
     if (url == null) return null;
-    if (!url.startsWith(RegExp(r'https?://'))) return 'Please specify http:// or https://';
+    if (!url.startsWith(RegExp(r'https?://')))
+      return 'Please specify http:// or https://';
     return null;
     return null;
   }
   }
 
 
@@ -107,7 +117,9 @@ class ServerEndpointInput extends StatelessWidget {
     return TextFormField(
     return TextFormField(
       controller: controller,
       controller: controller,
       decoration: const InputDecoration(
       decoration: const InputDecoration(
-          labelText: 'Server Endpoint URL', border: OutlineInputBorder(), hintText: 'http://your-server-ip:port'),
+          labelText: 'Server Endpoint URL',
+          border: OutlineInputBorder(),
+          hintText: 'http://your-server-ip:port'),
       validator: _validateInput,
       validator: _validateInput,
       autovalidateMode: AutovalidateMode.always,
       autovalidateMode: AutovalidateMode.always,
     );
     );
@@ -131,8 +143,10 @@ class EmailInput extends StatelessWidget {
   Widget build(BuildContext context) {
   Widget build(BuildContext context) {
     return TextFormField(
     return TextFormField(
       controller: controller,
       controller: controller,
-      decoration:
-          const InputDecoration(labelText: 'Email', border: OutlineInputBorder(), hintText: 'youremail@email.com'),
+      decoration: const InputDecoration(
+          labelText: 'Email',
+          border: OutlineInputBorder(),
+          hintText: 'youremail@email.com'),
       validator: _validateInput,
       validator: _validateInput,
       autovalidateMode: AutovalidateMode.always,
       autovalidateMode: AutovalidateMode.always,
     );
     );
@@ -149,7 +163,10 @@ class PasswordInput extends StatelessWidget {
     return TextFormField(
     return TextFormField(
       obscureText: true,
       obscureText: true,
       controller: controller,
       controller: controller,
-      decoration: const InputDecoration(labelText: 'Password', border: OutlineInputBorder(), hintText: 'password'),
+      decoration: const InputDecoration(
+          labelText: 'Password',
+          border: OutlineInputBorder(),
+          hintText: 'password'),
     );
     );
   }
   }
 }
 }
@@ -184,7 +201,8 @@ class LoginButton extends ConsumerWidget {
 
 
           var isAuthenticated = await ref
           var isAuthenticated = await ref
               .read(authenticationProvider.notifier)
               .read(authenticationProvider.notifier)
-              .login(emailController.text, passwordController.text, serverEndpointController.text, isSavedLoginInfo);
+              .login(emailController.text, passwordController.text,
+                  serverEndpointController.text, isSavedLoginInfo);
 
 
           if (isAuthenticated) {
           if (isAuthenticated) {
             // Resume backup (if enable) then navigate
             // Resume backup (if enable) then navigate
@@ -193,7 +211,8 @@ class LoginButton extends ConsumerWidget {
           } else {
           } else {
             ImmichToast.show(
             ImmichToast.show(
               context: context,
               context: context,
-              msg: "Error logging you in, check server url, email and password!",
+              msg:
+                  "Error logging you in, check server url, email and password!",
               toastType: ToastType.error,
               toastType: ToastType.error,
             );
             );
           }
           }

+ 7 - 2
mobile/lib/modules/search/models/curated_location.model.dart

@@ -53,7 +53,8 @@ class CuratedLocation {
 
 
   String toJson() => json.encode(toMap());
   String toJson() => json.encode(toMap());
 
 
-  factory CuratedLocation.fromJson(String source) => CuratedLocation.fromMap(json.decode(source));
+  factory CuratedLocation.fromJson(String source) =>
+      CuratedLocation.fromMap(json.decode(source));
 
 
   @override
   @override
   String toString() {
   String toString() {
@@ -74,6 +75,10 @@ class CuratedLocation {
 
 
   @override
   @override
   int get hashCode {
   int get hashCode {
-    return id.hashCode ^ city.hashCode ^ resizePath.hashCode ^ deviceAssetId.hashCode ^ deviceId.hashCode;
+    return id.hashCode ^
+        city.hashCode ^
+        resizePath.hashCode ^
+        deviceAssetId.hashCode ^
+        deviceId.hashCode;
   }
   }
 }
 }

+ 7 - 2
mobile/lib/modules/search/models/curated_object.model.dart

@@ -54,7 +54,8 @@ class CuratedObject {
 
 
   String toJson() => json.encode(toMap());
   String toJson() => json.encode(toMap());
 
 
-  factory CuratedObject.fromJson(String source) => CuratedObject.fromMap(json.decode(source));
+  factory CuratedObject.fromJson(String source) =>
+      CuratedObject.fromMap(json.decode(source));
 
 
   @override
   @override
   String toString() {
   String toString() {
@@ -75,6 +76,10 @@ class CuratedObject {
 
 
   @override
   @override
   int get hashCode {
   int get hashCode {
-    return id.hashCode ^ object.hashCode ^ resizePath.hashCode ^ deviceAssetId.hashCode ^ deviceId.hashCode;
+    return id.hashCode ^
+        object.hashCode ^
+        resizePath.hashCode ^
+        deviceAssetId.hashCode ^
+        deviceId.hashCode;
   }
   }
 }
 }

+ 6 - 3
mobile/lib/modules/search/models/search_page_state.model.dart

@@ -25,7 +25,8 @@ class SearchPageState {
       searchTerm: searchTerm ?? this.searchTerm,
       searchTerm: searchTerm ?? this.searchTerm,
       isSearchEnabled: isSearchEnabled ?? this.isSearchEnabled,
       isSearchEnabled: isSearchEnabled ?? this.isSearchEnabled,
       searchSuggestion: searchSuggestion ?? this.searchSuggestion,
       searchSuggestion: searchSuggestion ?? this.searchSuggestion,
-      userSuggestedSearchTerms: userSuggestedSearchTerms ?? this.userSuggestedSearchTerms,
+      userSuggestedSearchTerms:
+          userSuggestedSearchTerms ?? this.userSuggestedSearchTerms,
     );
     );
   }
   }
 
 
@@ -43,13 +44,15 @@ class SearchPageState {
       searchTerm: map['searchTerm'] ?? '',
       searchTerm: map['searchTerm'] ?? '',
       isSearchEnabled: map['isSearchEnabled'] ?? false,
       isSearchEnabled: map['isSearchEnabled'] ?? false,
       searchSuggestion: List<String>.from(map['searchSuggestion']),
       searchSuggestion: List<String>.from(map['searchSuggestion']),
-      userSuggestedSearchTerms: List<String>.from(map['userSuggestedSearchTerms']),
+      userSuggestedSearchTerms:
+          List<String>.from(map['userSuggestedSearchTerms']),
     );
     );
   }
   }
 
 
   String toJson() => json.encode(toMap());
   String toJson() => json.encode(toMap());
 
 
-  factory SearchPageState.fromJson(String source) => SearchPageState.fromMap(json.decode(source));
+  factory SearchPageState.fromJson(String source) =>
+      SearchPageState.fromMap(json.decode(source));
 
 
   @override
   @override
   String toString() {
   String toString() {

+ 8 - 3
mobile/lib/modules/search/models/search_result_page_state.model.dart

@@ -44,13 +44,15 @@ class SearchResultPageState {
       isLoading: map['isLoading'] ?? false,
       isLoading: map['isLoading'] ?? false,
       isSuccess: map['isSuccess'] ?? false,
       isSuccess: map['isSuccess'] ?? false,
       isError: map['isError'] ?? false,
       isError: map['isError'] ?? false,
-      searchResult: List<ImmichAsset>.from(map['searchResult']?.map((x) => ImmichAsset.fromMap(x))),
+      searchResult: List<ImmichAsset>.from(
+          map['searchResult']?.map((x) => ImmichAsset.fromMap(x))),
     );
     );
   }
   }
 
 
   String toJson() => json.encode(toMap());
   String toJson() => json.encode(toMap());
 
 
-  factory SearchResultPageState.fromJson(String source) => SearchResultPageState.fromMap(json.decode(source));
+  factory SearchResultPageState.fromJson(String source) =>
+      SearchResultPageState.fromMap(json.decode(source));
 
 
   @override
   @override
   String toString() {
   String toString() {
@@ -71,6 +73,9 @@ class SearchResultPageState {
 
 
   @override
   @override
   int get hashCode {
   int get hashCode {
-    return isLoading.hashCode ^ isSuccess.hashCode ^ isError.hashCode ^ searchResult.hashCode;
+    return isLoading.hashCode ^
+        isSuccess.hashCode ^
+        isError.hashCode ^
+        searchResult.hashCode;
   }
   }
 }
 }

+ 3 - 1
mobile/lib/modules/search/ui/search_bar.dart

@@ -4,7 +4,9 @@ import 'package:hooks_riverpod/hooks_riverpod.dart';
 import 'package:immich_mobile/modules/search/providers/search_page_state.provider.dart';
 import 'package:immich_mobile/modules/search/providers/search_page_state.provider.dart';
 
 
 class SearchBar extends HookConsumerWidget with PreferredSizeWidget {
 class SearchBar extends HookConsumerWidget with PreferredSizeWidget {
-  SearchBar({Key? key, required this.searchFocusNode, required this.onSubmitted}) : super(key: key);
+  SearchBar(
+      {Key? key, required this.searchFocusNode, required this.onSubmitted})
+      : super(key: key);
 
 
   final FocusNode searchFocusNode;
   final FocusNode searchFocusNode;
   final Function(String) onSubmitted;
   final Function(String) onSubmitted;

+ 7 - 3
mobile/lib/modules/search/ui/search_suggestion_list.dart

@@ -3,16 +3,20 @@ import 'package:hooks_riverpod/hooks_riverpod.dart';
 import 'package:immich_mobile/modules/search/providers/search_page_state.provider.dart';
 import 'package:immich_mobile/modules/search/providers/search_page_state.provider.dart';
 
 
 class SearchSuggestionList extends ConsumerWidget {
 class SearchSuggestionList extends ConsumerWidget {
-  const SearchSuggestionList({Key? key, required this.onSubmitted}) : super(key: key);
+  const SearchSuggestionList({Key? key, required this.onSubmitted})
+      : super(key: key);
 
 
   final Function(String) onSubmitted;
   final Function(String) onSubmitted;
   @override
   @override
   Widget build(BuildContext context, WidgetRef ref) {
   Widget build(BuildContext context, WidgetRef ref) {
     final searchTerm = ref.watch(searchPageStateProvider).searchTerm;
     final searchTerm = ref.watch(searchPageStateProvider).searchTerm;
-    final searchSuggestion = ref.watch(searchPageStateProvider).searchSuggestion;
+    final searchSuggestion =
+        ref.watch(searchPageStateProvider).searchSuggestion;
 
 
     return Container(
     return Container(
-      color: searchTerm.isEmpty ? Colors.black.withOpacity(0.5) : Theme.of(context).scaffoldBackgroundColor,
+      color: searchTerm.isEmpty
+          ? Colors.black.withOpacity(0.5)
+          : Theme.of(context).scaffoldBackgroundColor,
       child: CustomScrollView(
       child: CustomScrollView(
         slivers: [
         slivers: [
           SliverFillRemaining(
           SliverFillRemaining(

+ 8 - 2
mobile/lib/modules/search/ui/thumbnail_with_info.dart

@@ -5,7 +5,11 @@ import 'package:immich_mobile/constants/hive_box.dart';
 import 'package:immich_mobile/utils/capitalize_first_letter.dart';
 import 'package:immich_mobile/utils/capitalize_first_letter.dart';
 
 
 class ThumbnailWithInfo extends StatelessWidget {
 class ThumbnailWithInfo extends StatelessWidget {
-  const ThumbnailWithInfo({Key? key, required this.textInfo, required this.imageUrl, required this.onTap})
+  const ThumbnailWithInfo(
+      {Key? key,
+      required this.textInfo,
+      required this.imageUrl,
+      required this.onTap})
       : super(key: key);
       : super(key: key);
 
 
   final String textInfo;
   final String textInfo;
@@ -39,7 +43,9 @@ class ThumbnailWithInfo extends StatelessWidget {
                     height: 250,
                     height: 250,
                     fit: BoxFit.cover,
                     fit: BoxFit.cover,
                     imageUrl: imageUrl,
                     imageUrl: imageUrl,
-                    httpHeaders: {"Authorization": "Bearer ${box.get(accessTokenKey)}"},
+                    httpHeaders: {
+                      "Authorization": "Bearer ${box.get(accessTokenKey)}"
+                    },
                   ),
                   ),
                 ),
                 ),
               ),
               ),

+ 7 - 3
mobile/lib/modules/sharing/models/album_viewer_page_state.model.dart

@@ -36,16 +36,20 @@ class AlbumViewerPageState {
 
 
   String toJson() => json.encode(toMap());
   String toJson() => json.encode(toMap());
 
 
-  factory AlbumViewerPageState.fromJson(String source) => AlbumViewerPageState.fromMap(json.decode(source));
+  factory AlbumViewerPageState.fromJson(String source) =>
+      AlbumViewerPageState.fromMap(json.decode(source));
 
 
   @override
   @override
-  String toString() => 'AlbumViewerPageState(isEditAlbum: $isEditAlbum, editTitleText: $editTitleText)';
+  String toString() =>
+      'AlbumViewerPageState(isEditAlbum: $isEditAlbum, editTitleText: $editTitleText)';
 
 
   @override
   @override
   bool operator ==(Object other) {
   bool operator ==(Object other) {
     if (identical(this, other)) return true;
     if (identical(this, other)) return true;
 
 
-    return other is AlbumViewerPageState && other.isEditAlbum == isEditAlbum && other.editTitleText == editTitleText;
+    return other is AlbumViewerPageState &&
+        other.isEditAlbum == isEditAlbum &&
+        other.editTitleText == editTitleText;
   }
   }
 
 
   @override
   @override

+ 18 - 8
mobile/lib/modules/sharing/models/asset_selection_page_result.model.dart

@@ -22,7 +22,8 @@ class AssetSelectionPageResult {
   }) {
   }) {
     return AssetSelectionPageResult(
     return AssetSelectionPageResult(
       selectedNewAsset: selectedNewAsset ?? this.selectedNewAsset,
       selectedNewAsset: selectedNewAsset ?? this.selectedNewAsset,
-      selectedAdditionalAsset: selectedAdditionalAsset ?? this.selectedAdditionalAsset,
+      selectedAdditionalAsset:
+          selectedAdditionalAsset ?? this.selectedAdditionalAsset,
       isAlbumExist: isAlbumExist ?? this.isAlbumExist,
       isAlbumExist: isAlbumExist ?? this.isAlbumExist,
     );
     );
   }
   }
@@ -30,8 +31,12 @@ class AssetSelectionPageResult {
   Map<String, dynamic> toMap() {
   Map<String, dynamic> toMap() {
     final result = <String, dynamic>{};
     final result = <String, dynamic>{};
 
 
-    result.addAll({'selectedNewAsset': selectedNewAsset.map((x) => x.toMap()).toList()});
-    result.addAll({'selectedAdditionalAsset': selectedAdditionalAsset.map((x) => x.toMap()).toList()});
+    result.addAll(
+        {'selectedNewAsset': selectedNewAsset.map((x) => x.toMap()).toList()});
+    result.addAll({
+      'selectedAdditionalAsset':
+          selectedAdditionalAsset.map((x) => x.toMap()).toList()
+    });
     result.addAll({'isAlbumExist': isAlbumExist});
     result.addAll({'isAlbumExist': isAlbumExist});
 
 
     return result;
     return result;
@@ -39,16 +44,18 @@ class AssetSelectionPageResult {
 
 
   factory AssetSelectionPageResult.fromMap(Map<String, dynamic> map) {
   factory AssetSelectionPageResult.fromMap(Map<String, dynamic> map) {
     return AssetSelectionPageResult(
     return AssetSelectionPageResult(
-      selectedNewAsset: Set<ImmichAsset>.from(map['selectedNewAsset']?.map((x) => ImmichAsset.fromMap(x))),
-      selectedAdditionalAsset:
-          Set<ImmichAsset>.from(map['selectedAdditionalAsset']?.map((x) => ImmichAsset.fromMap(x))),
+      selectedNewAsset: Set<ImmichAsset>.from(
+          map['selectedNewAsset']?.map((x) => ImmichAsset.fromMap(x))),
+      selectedAdditionalAsset: Set<ImmichAsset>.from(
+          map['selectedAdditionalAsset']?.map((x) => ImmichAsset.fromMap(x))),
       isAlbumExist: map['isAlbumExist'] ?? false,
       isAlbumExist: map['isAlbumExist'] ?? false,
     );
     );
   }
   }
 
 
   String toJson() => json.encode(toMap());
   String toJson() => json.encode(toMap());
 
 
-  factory AssetSelectionPageResult.fromJson(String source) => AssetSelectionPageResult.fromMap(json.decode(source));
+  factory AssetSelectionPageResult.fromJson(String source) =>
+      AssetSelectionPageResult.fromMap(json.decode(source));
 
 
   @override
   @override
   String toString() =>
   String toString() =>
@@ -66,5 +73,8 @@ class AssetSelectionPageResult {
   }
   }
 
 
   @override
   @override
-  int get hashCode => selectedNewAsset.hashCode ^ selectedAdditionalAsset.hashCode ^ isAlbumExist.hashCode;
+  int get hashCode =>
+      selectedNewAsset.hashCode ^
+      selectedAdditionalAsset.hashCode ^
+      isAlbumExist.hashCode;
 }
 }

+ 32 - 16
mobile/lib/modules/sharing/models/asset_selection_state.model.dart

@@ -32,9 +32,12 @@ class AssetSelectionState {
   }) {
   }) {
     return AssetSelectionState(
     return AssetSelectionState(
       selectedMonths: selectedMonths ?? this.selectedMonths,
       selectedMonths: selectedMonths ?? this.selectedMonths,
-      selectedNewAssetsForAlbum: selectedNewAssetsForAlbum ?? this.selectedNewAssetsForAlbum,
-      selectedAdditionalAssetsForAlbum: selectedAdditionalAssetsForAlbum ?? this.selectedAdditionalAssetsForAlbum,
-      selectedAssetsInAlbumViewer: selectedAssetsInAlbumViewer ?? this.selectedAssetsInAlbumViewer,
+      selectedNewAssetsForAlbum:
+          selectedNewAssetsForAlbum ?? this.selectedNewAssetsForAlbum,
+      selectedAdditionalAssetsForAlbum: selectedAdditionalAssetsForAlbum ??
+          this.selectedAdditionalAssetsForAlbum,
+      selectedAssetsInAlbumViewer:
+          selectedAssetsInAlbumViewer ?? this.selectedAssetsInAlbumViewer,
       isMultiselectEnable: isMultiselectEnable ?? this.isMultiselectEnable,
       isMultiselectEnable: isMultiselectEnable ?? this.isMultiselectEnable,
       isAlbumExist: isAlbumExist ?? this.isAlbumExist,
       isAlbumExist: isAlbumExist ?? this.isAlbumExist,
     );
     );
@@ -44,10 +47,18 @@ class AssetSelectionState {
     final result = <String, dynamic>{};
     final result = <String, dynamic>{};
 
 
     result.addAll({'selectedMonths': selectedMonths.toList()});
     result.addAll({'selectedMonths': selectedMonths.toList()});
-    result.addAll({'selectedNewAssetsForAlbum': selectedNewAssetsForAlbum.map((x) => x.toMap()).toList()});
-    result
-        .addAll({'selectedAdditionalAssetsForAlbum': selectedAdditionalAssetsForAlbum.map((x) => x.toMap()).toList()});
-    result.addAll({'selectedAssetsInAlbumViewer': selectedAssetsInAlbumViewer.map((x) => x.toMap()).toList()});
+    result.addAll({
+      'selectedNewAssetsForAlbum':
+          selectedNewAssetsForAlbum.map((x) => x.toMap()).toList()
+    });
+    result.addAll({
+      'selectedAdditionalAssetsForAlbum':
+          selectedAdditionalAssetsForAlbum.map((x) => x.toMap()).toList()
+    });
+    result.addAll({
+      'selectedAssetsInAlbumViewer':
+          selectedAssetsInAlbumViewer.map((x) => x.toMap()).toList()
+    });
     result.addAll({'isMultiselectEnable': isMultiselectEnable});
     result.addAll({'isMultiselectEnable': isMultiselectEnable});
     result.addAll({'isAlbumExist': isAlbumExist});
     result.addAll({'isAlbumExist': isAlbumExist});
 
 
@@ -57,12 +68,14 @@ class AssetSelectionState {
   factory AssetSelectionState.fromMap(Map<String, dynamic> map) {
   factory AssetSelectionState.fromMap(Map<String, dynamic> map) {
     return AssetSelectionState(
     return AssetSelectionState(
       selectedMonths: Set<String>.from(map['selectedMonths']),
       selectedMonths: Set<String>.from(map['selectedMonths']),
-      selectedNewAssetsForAlbum:
-          Set<ImmichAsset>.from(map['selectedNewAssetsForAlbum']?.map((x) => ImmichAsset.fromMap(x))),
-      selectedAdditionalAssetsForAlbum:
-          Set<ImmichAsset>.from(map['selectedAdditionalAssetsForAlbum']?.map((x) => ImmichAsset.fromMap(x))),
-      selectedAssetsInAlbumViewer:
-          Set<ImmichAsset>.from(map['selectedAssetsInAlbumViewer']?.map((x) => ImmichAsset.fromMap(x))),
+      selectedNewAssetsForAlbum: Set<ImmichAsset>.from(
+          map['selectedNewAssetsForAlbum']?.map((x) => ImmichAsset.fromMap(x))),
+      selectedAdditionalAssetsForAlbum: Set<ImmichAsset>.from(
+          map['selectedAdditionalAssetsForAlbum']
+              ?.map((x) => ImmichAsset.fromMap(x))),
+      selectedAssetsInAlbumViewer: Set<ImmichAsset>.from(
+          map['selectedAssetsInAlbumViewer']
+              ?.map((x) => ImmichAsset.fromMap(x))),
       isMultiselectEnable: map['isMultiselectEnable'] ?? false,
       isMultiselectEnable: map['isMultiselectEnable'] ?? false,
       isAlbumExist: map['isAlbumExist'] ?? false,
       isAlbumExist: map['isAlbumExist'] ?? false,
     );
     );
@@ -70,7 +83,8 @@ class AssetSelectionState {
 
 
   String toJson() => json.encode(toMap());
   String toJson() => json.encode(toMap());
 
 
-  factory AssetSelectionState.fromJson(String source) => AssetSelectionState.fromMap(json.decode(source));
+  factory AssetSelectionState.fromJson(String source) =>
+      AssetSelectionState.fromMap(json.decode(source));
 
 
   @override
   @override
   String toString() {
   String toString() {
@@ -85,8 +99,10 @@ class AssetSelectionState {
     return other is AssetSelectionState &&
     return other is AssetSelectionState &&
         setEquals(other.selectedMonths, selectedMonths) &&
         setEquals(other.selectedMonths, selectedMonths) &&
         setEquals(other.selectedNewAssetsForAlbum, selectedNewAssetsForAlbum) &&
         setEquals(other.selectedNewAssetsForAlbum, selectedNewAssetsForAlbum) &&
-        setEquals(other.selectedAdditionalAssetsForAlbum, selectedAdditionalAssetsForAlbum) &&
-        setEquals(other.selectedAssetsInAlbumViewer, selectedAssetsInAlbumViewer) &&
+        setEquals(other.selectedAdditionalAssetsForAlbum,
+            selectedAdditionalAssetsForAlbum) &&
+        setEquals(
+            other.selectedAssetsInAlbumViewer, selectedAssetsInAlbumViewer) &&
         other.isMultiselectEnable == isMultiselectEnable &&
         other.isMultiselectEnable == isMultiselectEnable &&
         other.isAlbumExist == isAlbumExist;
         other.isAlbumExist == isAlbumExist;
   }
   }

+ 8 - 4
mobile/lib/modules/sharing/models/shared_album.model.dart

@@ -38,7 +38,8 @@ class SharedAlbum {
       ownerId: ownerId ?? this.ownerId,
       ownerId: ownerId ?? this.ownerId,
       albumName: albumName ?? this.albumName,
       albumName: albumName ?? this.albumName,
       createdAt: createdAt ?? this.createdAt,
       createdAt: createdAt ?? this.createdAt,
-      albumThumbnailAssetId: albumThumbnailAssetId ?? this.albumThumbnailAssetId,
+      albumThumbnailAssetId:
+          albumThumbnailAssetId ?? this.albumThumbnailAssetId,
       sharedUsers: sharedUsers ?? this.sharedUsers,
       sharedUsers: sharedUsers ?? this.sharedUsers,
       assets: assets ?? this.assets,
       assets: assets ?? this.assets,
     );
     );
@@ -69,16 +70,19 @@ class SharedAlbum {
       albumName: map['albumName'] ?? '',
       albumName: map['albumName'] ?? '',
       createdAt: map['createdAt'] ?? '',
       createdAt: map['createdAt'] ?? '',
       albumThumbnailAssetId: map['albumThumbnailAssetId'],
       albumThumbnailAssetId: map['albumThumbnailAssetId'],
-      sharedUsers: List<User>.from(map['sharedUsers']?.map((x) => User.fromMap(x))),
+      sharedUsers:
+          List<User>.from(map['sharedUsers']?.map((x) => User.fromMap(x))),
       assets: map['assets'] != null
       assets: map['assets'] != null
-          ? List<ImmichAsset>.from(map['assets']?.map((x) => ImmichAsset.fromMap(x)))
+          ? List<ImmichAsset>.from(
+              map['assets']?.map((x) => ImmichAsset.fromMap(x)))
           : null,
           : null,
     );
     );
   }
   }
 
 
   String toJson() => json.encode(toMap());
   String toJson() => json.encode(toMap());
 
 
-  factory SharedAlbum.fromJson(String source) => SharedAlbum.fromMap(json.decode(source));
+  factory SharedAlbum.fromJson(String source) =>
+      SharedAlbum.fromMap(json.decode(source));
 
 
   @override
   @override
   String toString() {
   String toString() {

+ 2 - 1
mobile/lib/modules/sharing/providers/album_title.provider.dart

@@ -12,4 +12,5 @@ class AlbumTitleNotifier extends StateNotifier<String> {
   }
   }
 }
 }
 
 
-final albumTitleProvider = StateNotifierProvider<AlbumTitleNotifier, String>((ref) => AlbumTitleNotifier());
+final albumTitleProvider = StateNotifierProvider<AlbumTitleNotifier, String>(
+    (ref) => AlbumTitleNotifier());

+ 25 - 8
mobile/lib/modules/sharing/providers/asset_selection.provider.dart

@@ -18,35 +18,48 @@ class AssetSelectionNotifier extends StateNotifier<AssetSelectionState> {
     state = state.copyWith(isAlbumExist: isAlbumExist);
     state = state.copyWith(isAlbumExist: isAlbumExist);
   }
   }
 
 
-  void removeAssetsInMonth(String removedMonth, List<ImmichAsset> assetsInMonth) {
+  void removeAssetsInMonth(
+      String removedMonth, List<ImmichAsset> assetsInMonth) {
     Set<ImmichAsset> currentAssetList = state.selectedNewAssetsForAlbum;
     Set<ImmichAsset> currentAssetList = state.selectedNewAssetsForAlbum;
     Set<String> currentMonthList = state.selectedMonths;
     Set<String> currentMonthList = state.selectedMonths;
 
 
-    currentMonthList.removeWhere((selectedMonth) => selectedMonth == removedMonth);
+    currentMonthList
+        .removeWhere((selectedMonth) => selectedMonth == removedMonth);
 
 
     for (ImmichAsset asset in assetsInMonth) {
     for (ImmichAsset asset in assetsInMonth) {
       currentAssetList.removeWhere((e) => e.id == asset.id);
       currentAssetList.removeWhere((e) => e.id == asset.id);
     }
     }
 
 
-    state = state.copyWith(selectedNewAssetsForAlbum: currentAssetList, selectedMonths: currentMonthList);
+    state = state.copyWith(
+        selectedNewAssetsForAlbum: currentAssetList,
+        selectedMonths: currentMonthList);
   }
   }
 
 
   void addAdditionalAssets(List<ImmichAsset> assets) {
   void addAdditionalAssets(List<ImmichAsset> assets) {
     state = state.copyWith(
     state = state.copyWith(
-      selectedAdditionalAssetsForAlbum: {...state.selectedAdditionalAssetsForAlbum, ...assets},
+      selectedAdditionalAssetsForAlbum: {
+        ...state.selectedAdditionalAssetsForAlbum,
+        ...assets
+      },
     );
     );
   }
   }
 
 
   void addAllAssetsInMonth(String month, List<ImmichAsset> assetsInMonth) {
   void addAllAssetsInMonth(String month, List<ImmichAsset> assetsInMonth) {
     state = state.copyWith(
     state = state.copyWith(
       selectedMonths: {...state.selectedMonths, month},
       selectedMonths: {...state.selectedMonths, month},
-      selectedNewAssetsForAlbum: {...state.selectedNewAssetsForAlbum, ...assetsInMonth},
+      selectedNewAssetsForAlbum: {
+        ...state.selectedNewAssetsForAlbum,
+        ...assetsInMonth
+      },
     );
     );
   }
   }
 
 
   void addNewAssets(List<ImmichAsset> assets) {
   void addNewAssets(List<ImmichAsset> assets) {
     state = state.copyWith(
     state = state.copyWith(
-      selectedNewAssetsForAlbum: {...state.selectedNewAssetsForAlbum, ...assets},
+      selectedNewAssetsForAlbum: {
+        ...state.selectedNewAssetsForAlbum,
+        ...assets
+      },
     );
     );
   }
   }
 
 
@@ -93,7 +106,10 @@ class AssetSelectionNotifier extends StateNotifier<AssetSelectionState> {
 
 
   void addAssetsInAlbumViewer(List<ImmichAsset> assets) {
   void addAssetsInAlbumViewer(List<ImmichAsset> assets) {
     state = state.copyWith(
     state = state.copyWith(
-      selectedAssetsInAlbumViewer: {...state.selectedAssetsInAlbumViewer, ...assets},
+      selectedAssetsInAlbumViewer: {
+        ...state.selectedAssetsInAlbumViewer,
+        ...assets
+      },
     );
     );
   }
   }
 
 
@@ -108,6 +124,7 @@ class AssetSelectionNotifier extends StateNotifier<AssetSelectionState> {
   }
   }
 }
 }
 
 
-final assetSelectionProvider = StateNotifierProvider<AssetSelectionNotifier, AssetSelectionState>((ref) {
+final assetSelectionProvider =
+    StateNotifierProvider<AssetSelectionNotifier, AssetSelectionState>((ref) {
   return AssetSelectionNotifier();
   return AssetSelectionNotifier();
 });
 });

+ 7 - 2
mobile/lib/modules/sharing/ui/album_action_outlined_button.dart

@@ -5,7 +5,11 @@ class AlbumActionOutlinedButton extends StatelessWidget {
   final String labelText;
   final String labelText;
   final IconData iconData;
   final IconData iconData;
 
 
-  const AlbumActionOutlinedButton({Key? key, this.onPressed, required this.labelText, required this.iconData})
+  const AlbumActionOutlinedButton(
+      {Key? key,
+      this.onPressed,
+      required this.labelText,
+      required this.iconData})
       : super(key: key);
       : super(key: key);
 
 
   @override
   @override
@@ -26,7 +30,8 @@ class AlbumActionOutlinedButton extends StatelessWidget {
         icon: Icon(iconData, size: 15),
         icon: Icon(iconData, size: 15),
         label: Text(
         label: Text(
           labelText,
           labelText,
-          style: const TextStyle(fontSize: 12, fontWeight: FontWeight.bold, color: Colors.black87),
+          style: const TextStyle(
+              fontSize: 12, fontWeight: FontWeight.bold, color: Colors.black87),
         ),
         ),
         onPressed: onPressed,
         onPressed: onPressed,
       ),
       ),

+ 2 - 1
mobile/lib/modules/sharing/ui/album_title_text_field.dart

@@ -29,7 +29,8 @@ class AlbumTitleTextField extends ConsumerWidget {
         ref.watch(albumTitleProvider.notifier).setAlbumTitle(v);
         ref.watch(albumTitleProvider.notifier).setAlbumTitle(v);
       },
       },
       focusNode: albumTitleTextFieldFocusNode,
       focusNode: albumTitleTextFieldFocusNode,
-      style: TextStyle(fontSize: 28, color: Colors.grey[700], fontWeight: FontWeight.bold),
+      style: TextStyle(
+          fontSize: 28, color: Colors.grey[700], fontWeight: FontWeight.bold),
       controller: albumTitleController,
       controller: albumTitleController,
       onTap: () {
       onTap: () {
         isAlbumTitleTextFieldFocus.value = true;
         isAlbumTitleTextFieldFocus.value = true;

+ 8 - 3
mobile/lib/modules/sharing/ui/album_viewer_editable_title.dart

@@ -7,11 +7,14 @@ import 'package:immich_mobile/modules/sharing/providers/album_viewer.provider.da
 class AlbumViewerEditableTitle extends HookConsumerWidget {
 class AlbumViewerEditableTitle extends HookConsumerWidget {
   final SharedAlbum albumInfo;
   final SharedAlbum albumInfo;
   final FocusNode titleFocusNode;
   final FocusNode titleFocusNode;
-  const AlbumViewerEditableTitle({Key? key, required this.albumInfo, required this.titleFocusNode}) : super(key: key);
+  const AlbumViewerEditableTitle(
+      {Key? key, required this.albumInfo, required this.titleFocusNode})
+      : super(key: key);
 
 
   @override
   @override
   Widget build(BuildContext context, WidgetRef ref) {
   Widget build(BuildContext context, WidgetRef ref) {
-    final titleTextEditController = useTextEditingController(text: albumInfo.albumName);
+    final titleTextEditController =
+        useTextEditingController(text: albumInfo.albumName);
 
 
     void onFocusModeChange() {
     void onFocusModeChange() {
       if (!titleFocusNode.hasFocus && titleTextEditController.text.isEmpty) {
       if (!titleFocusNode.hasFocus && titleTextEditController.text.isEmpty) {
@@ -40,7 +43,9 @@ class AlbumViewerEditableTitle extends HookConsumerWidget {
       onTap: () {
       onTap: () {
         FocusScope.of(context).requestFocus(titleFocusNode);
         FocusScope.of(context).requestFocus(titleFocusNode);
 
 
-        ref.watch(albumViewerProvider.notifier).setEditTitleText(albumInfo.albumName);
+        ref
+            .watch(albumViewerProvider.notifier)
+            .setEditTitleText(albumInfo.albumName);
         ref.watch(albumViewerProvider.notifier).enableEditAlbum();
         ref.watch(albumViewerProvider.notifier).enableEditAlbum();
 
 
         if (titleTextEditController.text == 'Untitled') {
         if (titleTextEditController.text == 'Untitled') {

+ 20 - 8
mobile/lib/modules/sharing/ui/album_viewer_thumbnail.dart

@@ -22,8 +22,10 @@ class AlbumViewerThumbnail extends HookConsumerWidget {
     var thumbnailRequestUrl =
     var thumbnailRequestUrl =
         '${box.get(serverEndpointKey)}/asset/file?aid=${asset.deviceAssetId}&did=${asset.deviceId}&isThumb=true';
         '${box.get(serverEndpointKey)}/asset/file?aid=${asset.deviceAssetId}&did=${asset.deviceId}&isThumb=true';
     var deviceId = ref.watch(authenticationProvider).deviceId;
     var deviceId = ref.watch(authenticationProvider).deviceId;
-    final selectedAssetsInAlbumViewer = ref.watch(assetSelectionProvider).selectedAssetsInAlbumViewer;
-    final isMultiSelectionEnable = ref.watch(assetSelectionProvider).isMultiselectEnable;
+    final selectedAssetsInAlbumViewer =
+        ref.watch(assetSelectionProvider).selectedAssetsInAlbumViewer;
+    final isMultiSelectionEnable =
+        ref.watch(assetSelectionProvider).isMultiselectEnable;
 
 
     _viewAsset() {
     _viewAsset() {
       if (asset.type == 'IMAGE') {
       if (asset.type == 'IMAGE') {
@@ -39,7 +41,8 @@ class AlbumViewerThumbnail extends HookConsumerWidget {
       } else {
       } else {
         AutoRouter.of(context).push(
         AutoRouter.of(context).push(
           VideoViewerRoute(
           VideoViewerRoute(
-              videoUrl: '${box.get(serverEndpointKey)}/asset/file?aid=${asset.deviceAssetId}&did=${asset.deviceId}',
+              videoUrl:
+                  '${box.get(serverEndpointKey)}/asset/file?aid=${asset.deviceAssetId}&did=${asset.deviceId}',
               asset: asset),
               asset: asset),
         );
         );
       }
       }
@@ -58,7 +61,9 @@ class AlbumViewerThumbnail extends HookConsumerWidget {
 
 
     _enableMultiSelection() {
     _enableMultiSelection() {
       ref.watch(assetSelectionProvider.notifier).enableMultiselection();
       ref.watch(assetSelectionProvider.notifier).enableMultiselection();
-      ref.watch(assetSelectionProvider.notifier).addAssetsInAlbumViewer([asset]);
+      ref
+          .watch(assetSelectionProvider.notifier)
+          .addAssetsInAlbumViewer([asset]);
     }
     }
 
 
     _disableMultiSelection() {
     _disableMultiSelection() {
@@ -96,7 +101,9 @@ class AlbumViewerThumbnail extends HookConsumerWidget {
         right: 10,
         right: 10,
         bottom: 5,
         bottom: 5,
         child: Icon(
         child: Icon(
-          (deviceId != asset.deviceId) ? Icons.cloud_done_outlined : Icons.photo_library_rounded,
+          (deviceId != asset.deviceId)
+              ? Icons.cloud_done_outlined
+              : Icons.photo_library_rounded,
           color: Colors.white,
           color: Colors.white,
           size: 18,
           size: 18,
         ),
         ),
@@ -136,7 +143,8 @@ class AlbumViewerThumbnail extends HookConsumerWidget {
           imageUrl: thumbnailRequestUrl,
           imageUrl: thumbnailRequestUrl,
           httpHeaders: {"Authorization": "Bearer ${box.get(accessTokenKey)}"},
           httpHeaders: {"Authorization": "Bearer ${box.get(accessTokenKey)}"},
           fadeInDuration: const Duration(milliseconds: 250),
           fadeInDuration: const Duration(milliseconds: 250),
-          progressIndicatorBuilder: (context, url, downloadProgress) => Transform.scale(
+          progressIndicatorBuilder: (context, url, downloadProgress) =>
+              Transform.scale(
             scale: 0.2,
             scale: 0.2,
             child: CircularProgressIndicator(value: downloadProgress.progress),
             child: CircularProgressIndicator(value: downloadProgress.progress),
           ),
           ),
@@ -152,13 +160,17 @@ class AlbumViewerThumbnail extends HookConsumerWidget {
 
 
     _handleSelectionGesture() {
     _handleSelectionGesture() {
       if (selectedAssetsInAlbumViewer.contains(asset)) {
       if (selectedAssetsInAlbumViewer.contains(asset)) {
-        ref.watch(assetSelectionProvider.notifier).removeAssetsInAlbumViewer([asset]);
+        ref
+            .watch(assetSelectionProvider.notifier)
+            .removeAssetsInAlbumViewer([asset]);
 
 
         if (selectedAssetsInAlbumViewer.isEmpty) {
         if (selectedAssetsInAlbumViewer.isEmpty) {
           _disableMultiSelection();
           _disableMultiSelection();
         }
         }
       } else {
       } else {
-        ref.watch(assetSelectionProvider.notifier).addAssetsInAlbumViewer([asset]);
+        ref
+            .watch(assetSelectionProvider.notifier)
+            .addAssetsInAlbumViewer([asset]);
       }
       }
     }
     }
 
 

+ 2 - 1
mobile/lib/modules/sharing/ui/asset_grid_by_month.dart

@@ -5,7 +5,8 @@ import 'package:immich_mobile/shared/models/immich_asset.model.dart';
 
 
 class AssetGridByMonth extends HookConsumerWidget {
 class AssetGridByMonth extends HookConsumerWidget {
   final List<ImmichAsset> assetGroup;
   final List<ImmichAsset> assetGroup;
-  const AssetGridByMonth({Key? key, required this.assetGroup}) : super(key: key);
+  const AssetGridByMonth({Key? key, required this.assetGroup})
+      : super(key: key);
   @override
   @override
   Widget build(BuildContext context, WidgetRef ref) {
   Widget build(BuildContext context, WidgetRef ref) {
     return SliverGrid(
     return SliverGrid(

+ 25 - 9
mobile/lib/modules/sharing/ui/month_group_title.dart

@@ -8,12 +8,15 @@ class MonthGroupTitle extends HookConsumerWidget {
   final String month;
   final String month;
   final List<ImmichAsset> assetGroup;
   final List<ImmichAsset> assetGroup;
 
 
-  const MonthGroupTitle({Key? key, required this.month, required this.assetGroup}) : super(key: key);
+  const MonthGroupTitle(
+      {Key? key, required this.month, required this.assetGroup})
+      : super(key: key);
 
 
   @override
   @override
   Widget build(BuildContext context, WidgetRef ref) {
   Widget build(BuildContext context, WidgetRef ref) {
     final selectedDateGroup = ref.watch(assetSelectionProvider).selectedMonths;
     final selectedDateGroup = ref.watch(assetSelectionProvider).selectedMonths;
-    final selectedAssets = ref.watch(assetSelectionProvider).selectedNewAssetsForAlbum;
+    final selectedAssets =
+        ref.watch(assetSelectionProvider).selectedNewAssetsForAlbum;
     final isAlbumExist = ref.watch(assetSelectionProvider).isAlbumExist;
     final isAlbumExist = ref.watch(assetSelectionProvider).isAlbumExist;
 
 
     _handleTitleIconClick() {
     _handleTitleIconClick() {
@@ -21,10 +24,16 @@ class MonthGroupTitle extends HookConsumerWidget {
 
 
       if (isAlbumExist) {
       if (isAlbumExist) {
         if (selectedDateGroup.contains(month)) {
         if (selectedDateGroup.contains(month)) {
-          ref.watch(assetSelectionProvider.notifier).removeAssetsInMonth(month, []);
-          ref.watch(assetSelectionProvider.notifier).removeSelectedAdditionalAssets(assetGroup);
+          ref
+              .watch(assetSelectionProvider.notifier)
+              .removeAssetsInMonth(month, []);
+          ref
+              .watch(assetSelectionProvider.notifier)
+              .removeSelectedAdditionalAssets(assetGroup);
         } else {
         } else {
-          ref.watch(assetSelectionProvider.notifier).addAllAssetsInMonth(month, []);
+          ref
+              .watch(assetSelectionProvider.notifier)
+              .addAllAssetsInMonth(month, []);
 
 
           // Deep clone assetGroup
           // Deep clone assetGroup
           var assetGroupWithNewItems = [...assetGroup];
           var assetGroupWithNewItems = [...assetGroup];
@@ -33,13 +42,19 @@ class MonthGroupTitle extends HookConsumerWidget {
             assetGroupWithNewItems.removeWhere((a) => a.id == selectedAsset.id);
             assetGroupWithNewItems.removeWhere((a) => a.id == selectedAsset.id);
           }
           }
 
 
-          ref.watch(assetSelectionProvider.notifier).addAdditionalAssets(assetGroupWithNewItems);
+          ref
+              .watch(assetSelectionProvider.notifier)
+              .addAdditionalAssets(assetGroupWithNewItems);
         }
         }
       } else {
       } else {
         if (selectedDateGroup.contains(month)) {
         if (selectedDateGroup.contains(month)) {
-          ref.watch(assetSelectionProvider.notifier).removeAssetsInMonth(month, assetGroup);
+          ref
+              .watch(assetSelectionProvider.notifier)
+              .removeAssetsInMonth(month, assetGroup);
         } else {
         } else {
-          ref.watch(assetSelectionProvider.notifier).addAllAssetsInMonth(month, assetGroup);
+          ref
+              .watch(assetSelectionProvider.notifier)
+              .addAllAssetsInMonth(month, assetGroup);
         }
         }
       }
       }
     }
     }
@@ -59,7 +74,8 @@ class MonthGroupTitle extends HookConsumerWidget {
 
 
     return SliverToBoxAdapter(
     return SliverToBoxAdapter(
       child: Padding(
       child: Padding(
-        padding: const EdgeInsets.only(top: 29.0, bottom: 29.0, left: 14.0, right: 8.0),
+        padding: const EdgeInsets.only(
+            top: 29.0, bottom: 29.0, left: 14.0, right: 8.0),
         child: Row(
         child: Row(
           children: [
           children: [
             GestureDetector(
             GestureDetector(

+ 22 - 9
mobile/lib/modules/sharing/ui/selection_thumbnail_image.dart

@@ -10,7 +10,8 @@ import 'package:immich_mobile/shared/models/immich_asset.model.dart';
 class SelectionThumbnailImage extends HookConsumerWidget {
 class SelectionThumbnailImage extends HookConsumerWidget {
   final ImmichAsset asset;
   final ImmichAsset asset;
 
 
-  const SelectionThumbnailImage({Key? key, required this.asset}) : super(key: key);
+  const SelectionThumbnailImage({Key? key, required this.asset})
+      : super(key: key);
 
 
   @override
   @override
   Widget build(BuildContext context, WidgetRef ref) {
   Widget build(BuildContext context, WidgetRef ref) {
@@ -18,8 +19,10 @@ class SelectionThumbnailImage extends HookConsumerWidget {
     var box = Hive.box(userInfoBox);
     var box = Hive.box(userInfoBox);
     var thumbnailRequestUrl =
     var thumbnailRequestUrl =
         '${box.get(serverEndpointKey)}/asset/file?aid=${asset.deviceAssetId}&did=${asset.deviceId}&isThumb=true';
         '${box.get(serverEndpointKey)}/asset/file?aid=${asset.deviceAssetId}&did=${asset.deviceId}&isThumb=true';
-    var selectedAsset = ref.watch(assetSelectionProvider).selectedNewAssetsForAlbum;
-    var newAssetsForAlbum = ref.watch(assetSelectionProvider).selectedAdditionalAssetsForAlbum;
+    var selectedAsset =
+        ref.watch(assetSelectionProvider).selectedNewAssetsForAlbum;
+    var newAssetsForAlbum =
+        ref.watch(assetSelectionProvider).selectedAdditionalAssetsForAlbum;
     var isAlbumExist = ref.watch(assetSelectionProvider).isAlbumExist;
     var isAlbumExist = ref.watch(assetSelectionProvider).isAlbumExist;
 
 
     Widget _buildSelectionIcon(ImmichAsset asset) {
     Widget _buildSelectionIcon(ImmichAsset asset) {
@@ -72,15 +75,21 @@ class SelectionThumbnailImage extends HookConsumerWidget {
           // Operation for existing album
           // Operation for existing album
           if (!selectedAsset.contains(asset)) {
           if (!selectedAsset.contains(asset)) {
             if (newAssetsForAlbum.contains(asset)) {
             if (newAssetsForAlbum.contains(asset)) {
-              ref.watch(assetSelectionProvider.notifier).removeSelectedAdditionalAssets([asset]);
+              ref
+                  .watch(assetSelectionProvider.notifier)
+                  .removeSelectedAdditionalAssets([asset]);
             } else {
             } else {
-              ref.watch(assetSelectionProvider.notifier).addAdditionalAssets([asset]);
+              ref
+                  .watch(assetSelectionProvider.notifier)
+                  .addAdditionalAssets([asset]);
             }
             }
           }
           }
         } else {
         } else {
           // Operation for new album
           // Operation for new album
           if (selectedAsset.contains(asset)) {
           if (selectedAsset.contains(asset)) {
-            ref.watch(assetSelectionProvider.notifier).removeSelectedNewAssets([asset]);
+            ref
+                .watch(assetSelectionProvider.notifier)
+                .removeSelectedNewAssets([asset]);
           } else {
           } else {
             ref.watch(assetSelectionProvider.notifier).addNewAssets([asset]);
             ref.watch(assetSelectionProvider.notifier).addNewAssets([asset]);
           }
           }
@@ -97,11 +106,15 @@ class SelectionThumbnailImage extends HookConsumerWidget {
               memCacheHeight: asset.type == 'IMAGE' ? 150 : 150,
               memCacheHeight: asset.type == 'IMAGE' ? 150 : 150,
               fit: BoxFit.cover,
               fit: BoxFit.cover,
               imageUrl: thumbnailRequestUrl,
               imageUrl: thumbnailRequestUrl,
-              httpHeaders: {"Authorization": "Bearer ${box.get(accessTokenKey)}"},
+              httpHeaders: {
+                "Authorization": "Bearer ${box.get(accessTokenKey)}"
+              },
               fadeInDuration: const Duration(milliseconds: 250),
               fadeInDuration: const Duration(milliseconds: 250),
-              progressIndicatorBuilder: (context, url, downloadProgress) => Transform.scale(
+              progressIndicatorBuilder: (context, url, downloadProgress) =>
+                  Transform.scale(
                 scale: 0.2,
                 scale: 0.2,
-                child: CircularProgressIndicator(value: downloadProgress.progress),
+                child:
+                    CircularProgressIndicator(value: downloadProgress.progress),
               ),
               ),
               errorWidget: (context, url, error) {
               errorWidget: (context, url, error) {
                 return Icon(
                 return Icon(

+ 6 - 3
mobile/lib/modules/sharing/ui/shared_album_thumbnail_image.dart

@@ -9,7 +9,8 @@ import 'package:immich_mobile/shared/models/immich_asset.model.dart';
 class SharedAlbumThumbnailImage extends HookConsumerWidget {
 class SharedAlbumThumbnailImage extends HookConsumerWidget {
   final ImmichAsset asset;
   final ImmichAsset asset;
 
 
-  const SharedAlbumThumbnailImage({Key? key, required this.asset}) : super(key: key);
+  const SharedAlbumThumbnailImage({Key? key, required this.asset})
+      : super(key: key);
 
 
   @override
   @override
   Widget build(BuildContext context, WidgetRef ref) {
   Widget build(BuildContext context, WidgetRef ref) {
@@ -34,9 +35,11 @@ class SharedAlbumThumbnailImage extends HookConsumerWidget {
             imageUrl: thumbnailRequestUrl,
             imageUrl: thumbnailRequestUrl,
             httpHeaders: {"Authorization": "Bearer ${box.get(accessTokenKey)}"},
             httpHeaders: {"Authorization": "Bearer ${box.get(accessTokenKey)}"},
             fadeInDuration: const Duration(milliseconds: 250),
             fadeInDuration: const Duration(milliseconds: 250),
-            progressIndicatorBuilder: (context, url, downloadProgress) => Transform.scale(
+            progressIndicatorBuilder: (context, url, downloadProgress) =>
+                Transform.scale(
               scale: 0.2,
               scale: 0.2,
-              child: CircularProgressIndicator(value: downloadProgress.progress),
+              child:
+                  CircularProgressIndicator(value: downloadProgress.progress),
             ),
             ),
             errorWidget: (context, url, error) {
             errorWidget: (context, url, error) {
               return Icon(
               return Icon(

+ 10 - 5
mobile/lib/modules/sharing/ui/sharing_sliver_appbar.dart

@@ -37,11 +37,13 @@ class SharingSliverAppBar extends StatelessWidget {
                   padding: const EdgeInsets.only(right: 4.0),
                   padding: const EdgeInsets.only(right: 4.0),
                   child: TextButton.icon(
                   child: TextButton.icon(
                     style: ButtonStyle(
                     style: ButtonStyle(
-                      backgroundColor: MaterialStateProperty.all(Theme.of(context).primaryColor.withAlpha(20)),
+                      backgroundColor: MaterialStateProperty.all(
+                          Theme.of(context).primaryColor.withAlpha(20)),
                       // foregroundColor: MaterialStateProperty.all(Colors.white),
                       // foregroundColor: MaterialStateProperty.all(Colors.white),
                     ),
                     ),
                     onPressed: () {
                     onPressed: () {
-                      AutoRouter.of(context).push(const CreateSharedAlbumRoute());
+                      AutoRouter.of(context)
+                          .push(const CreateSharedAlbumRoute());
                     },
                     },
                     icon: const Icon(
                     icon: const Icon(
                       Icons.photo_album_outlined,
                       Icons.photo_album_outlined,
@@ -49,7 +51,8 @@ class SharingSliverAppBar extends StatelessWidget {
                     ),
                     ),
                     label: const Text(
                     label: const Text(
                       "Create shared album",
                       "Create shared album",
-                      style: TextStyle(fontWeight: FontWeight.bold, fontSize: 12),
+                      style:
+                          TextStyle(fontWeight: FontWeight.bold, fontSize: 12),
                     ),
                     ),
                   ),
                   ),
                 ),
                 ),
@@ -59,7 +62,8 @@ class SharingSliverAppBar extends StatelessWidget {
                   padding: const EdgeInsets.only(left: 4.0),
                   padding: const EdgeInsets.only(left: 4.0),
                   child: TextButton.icon(
                   child: TextButton.icon(
                     style: ButtonStyle(
                     style: ButtonStyle(
-                      backgroundColor: MaterialStateProperty.all(Theme.of(context).primaryColor.withAlpha(20)),
+                      backgroundColor: MaterialStateProperty.all(
+                          Theme.of(context).primaryColor.withAlpha(20)),
                       // foregroundColor: MaterialStateProperty.all(Colors.white),
                       // foregroundColor: MaterialStateProperty.all(Colors.white),
                     ),
                     ),
                     onPressed: null,
                     onPressed: null,
@@ -69,7 +73,8 @@ class SharingSliverAppBar extends StatelessWidget {
                     ),
                     ),
                     label: const Text(
                     label: const Text(
                       "Share with partner",
                       "Share with partner",
-                      style: TextStyle(fontWeight: FontWeight.bold, fontSize: 12),
+                      style:
+                          TextStyle(fontWeight: FontWeight.bold, fontSize: 12),
                     ),
                     ),
                   ),
                   ),
                 ),
                 ),

+ 30 - 12
mobile/lib/modules/sharing/views/select_additional_user_for_sharing_page.dart

@@ -10,15 +10,18 @@ import 'package:immich_mobile/shared/ui/immich_loading_indicator.dart';
 class SelectAdditionalUserForSharingPage extends HookConsumerWidget {
 class SelectAdditionalUserForSharingPage extends HookConsumerWidget {
   final SharedAlbum albumInfo;
   final SharedAlbum albumInfo;
 
 
-  const SelectAdditionalUserForSharingPage({Key? key, required this.albumInfo}) : super(key: key);
+  const SelectAdditionalUserForSharingPage({Key? key, required this.albumInfo})
+      : super(key: key);
 
 
   @override
   @override
   Widget build(BuildContext context, WidgetRef ref) {
   Widget build(BuildContext context, WidgetRef ref) {
-    AsyncValue<List<User>> suggestedShareUsers = ref.watch(suggestedSharedUsersProvider);
+    AsyncValue<List<User>> suggestedShareUsers =
+        ref.watch(suggestedSharedUsersProvider);
     final sharedUsersList = useState<Set<User>>({});
     final sharedUsersList = useState<Set<User>>({});
 
 
     _addNewUsersHandler() {
     _addNewUsersHandler() {
-      AutoRouter.of(context).pop(sharedUsersList.value.map((e) => e.id).toList());
+      AutoRouter.of(context)
+          .pop(sharedUsersList.value.map((e) => e.id).toList());
     }
     }
 
 
     _buildTileIcon(User user) {
     _buildTileIcon(User user) {
@@ -32,7 +35,8 @@ class SelectAdditionalUserForSharingPage extends HookConsumerWidget {
         );
         );
       } else {
       } else {
         return CircleAvatar(
         return CircleAvatar(
-          backgroundImage: const AssetImage('assets/immich-logo-no-outline.png'),
+          backgroundImage:
+              const AssetImage('assets/immich-logo-no-outline.png'),
           backgroundColor: Theme.of(context).primaryColor.withAlpha(50),
           backgroundColor: Theme.of(context).primaryColor.withAlpha(50),
         );
         );
       }
       }
@@ -49,7 +53,10 @@ class SelectAdditionalUserForSharingPage extends HookConsumerWidget {
               backgroundColor: Theme.of(context).primaryColor.withOpacity(0.15),
               backgroundColor: Theme.of(context).primaryColor.withOpacity(0.15),
               label: Text(
               label: Text(
                 user.email,
                 user.email,
-                style: const TextStyle(fontSize: 12, color: Colors.black87, fontWeight: FontWeight.bold),
+                style: const TextStyle(
+                    fontSize: 12,
+                    color: Colors.black87,
+                    fontWeight: FontWeight.bold),
               ),
               ),
             ),
             ),
           ),
           ),
@@ -65,7 +72,10 @@ class SelectAdditionalUserForSharingPage extends HookConsumerWidget {
             padding: EdgeInsets.all(16.0),
             padding: EdgeInsets.all(16.0),
             child: Text(
             child: Text(
               'Suggestions',
               'Suggestions',
-              style: TextStyle(fontSize: 14, color: Colors.grey, fontWeight: FontWeight.bold),
+              style: TextStyle(
+                  fontSize: 14,
+                  color: Colors.grey,
+                  fontWeight: FontWeight.bold),
             ),
             ),
           ),
           ),
           ListView.builder(
           ListView.builder(
@@ -75,14 +85,20 @@ class SelectAdditionalUserForSharingPage extends HookConsumerWidget {
                 leading: _buildTileIcon(users[index]),
                 leading: _buildTileIcon(users[index]),
                 title: Text(
                 title: Text(
                   users[index].email,
                   users[index].email,
-                  style: const TextStyle(fontSize: 14, fontWeight: FontWeight.bold),
+                  style: const TextStyle(
+                      fontSize: 14, fontWeight: FontWeight.bold),
                 ),
                 ),
                 onTap: () {
                 onTap: () {
                   if (sharedUsersList.value.contains(users[index])) {
                   if (sharedUsersList.value.contains(users[index])) {
-                    sharedUsersList.value =
-                        sharedUsersList.value.where((selectedUser) => selectedUser.id != users[index].id).toSet();
+                    sharedUsersList.value = sharedUsersList.value
+                        .where((selectedUser) =>
+                            selectedUser.id != users[index].id)
+                        .toSet();
                   } else {
                   } else {
-                    sharedUsersList.value = {...sharedUsersList.value, users[index]};
+                    sharedUsersList.value = {
+                      ...sharedUsersList.value,
+                      users[index]
+                    };
                   }
                   }
                 },
                 },
               );
               );
@@ -109,7 +125,8 @@ class SelectAdditionalUserForSharingPage extends HookConsumerWidget {
         ),
         ),
         actions: [
         actions: [
           TextButton(
           TextButton(
-            onPressed: sharedUsersList.value.isEmpty ? null : _addNewUsersHandler,
+            onPressed:
+                sharedUsersList.value.isEmpty ? null : _addNewUsersHandler,
             child: const Text(
             child: const Text(
               "Add",
               "Add",
               style: TextStyle(fontSize: 14, fontWeight: FontWeight.bold),
               style: TextStyle(fontSize: 14, fontWeight: FontWeight.bold),
@@ -120,7 +137,8 @@ class SelectAdditionalUserForSharingPage extends HookConsumerWidget {
       body: suggestedShareUsers.when(
       body: suggestedShareUsers.when(
         data: (users) {
         data: (users) {
           for (var sharedUsers in albumInfo.sharedUsers) {
           for (var sharedUsers in albumInfo.sharedUsers) {
-            users.removeWhere((u) => u.id == sharedUsers.id || u.id == albumInfo.ownerId);
+            users.removeWhere(
+                (u) => u.id == sharedUsers.id || u.id == albumInfo.ownerId);
           }
           }
 
 
           return _buildUserList(users);
           return _buildUserList(users);

+ 16 - 6
mobile/lib/modules/sharing/views/sharing_page.dart

@@ -29,12 +29,14 @@ class SharingPage extends HookConsumerWidget {
       return SliverList(
       return SliverList(
         delegate: SliverChildBuilderDelegate(
         delegate: SliverChildBuilderDelegate(
           (BuildContext context, int index) {
           (BuildContext context, int index) {
-            String thumbnailUrl = sharedAlbums[index].albumThumbnailAssetId != null
+            String thumbnailUrl = sharedAlbums[index].albumThumbnailAssetId !=
+                    null
                 ? "$thumbnailRequestUrl/${sharedAlbums[index].albumThumbnailAssetId}"
                 ? "$thumbnailRequestUrl/${sharedAlbums[index].albumThumbnailAssetId}"
                 : "https://images.unsplash.com/photo-1612178537253-bccd437b730e?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxzZWFyY2h8NXx8Ymxhbmt8ZW58MHx8MHx8&auto=format&fit=crop&w=700&q=60";
                 : "https://images.unsplash.com/photo-1612178537253-bccd437b730e?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxzZWFyY2h8NXx8Ymxhbmt8ZW58MHx8MHx8&auto=format&fit=crop&w=700&q=60";
 
 
             return ListTile(
             return ListTile(
-              contentPadding: const EdgeInsets.symmetric(vertical: 12, horizontal: 12),
+              contentPadding:
+                  const EdgeInsets.symmetric(vertical: 12, horizontal: 12),
               leading: ClipRRect(
               leading: ClipRRect(
                 borderRadius: BorderRadius.circular(8),
                 borderRadius: BorderRadius.circular(8),
                 child: FadeInImage(
                 child: FadeInImage(
@@ -44,7 +46,9 @@ class SharingPage extends HookConsumerWidget {
                   placeholder: MemoryImage(kTransparentImage),
                   placeholder: MemoryImage(kTransparentImage),
                   image: NetworkImage(
                   image: NetworkImage(
                     thumbnailUrl,
                     thumbnailUrl,
-                    headers: {"Authorization": "Bearer ${box.get(accessTokenKey)}"},
+                    headers: {
+                      "Authorization": "Bearer ${box.get(accessTokenKey)}"
+                    },
                   ),
                   ),
                   fadeInDuration: const Duration(milliseconds: 200),
                   fadeInDuration: const Duration(milliseconds: 200),
                   fadeOutDuration: const Duration(milliseconds: 200),
                   fadeOutDuration: const Duration(milliseconds: 200),
@@ -54,10 +58,14 @@ class SharingPage extends HookConsumerWidget {
                 sharedAlbums[index].albumName,
                 sharedAlbums[index].albumName,
                 maxLines: 1,
                 maxLines: 1,
                 overflow: TextOverflow.ellipsis,
                 overflow: TextOverflow.ellipsis,
-                style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold, color: Colors.grey.shade800),
+                style: TextStyle(
+                    fontSize: 16,
+                    fontWeight: FontWeight.bold,
+                    color: Colors.grey.shade800),
               ),
               ),
               onTap: () {
               onTap: () {
-                AutoRouter.of(context).push(AlbumViewerRoute(albumId: sharedAlbums[index].id));
+                AutoRouter.of(context)
+                    .push(AlbumViewerRoute(albumId: sharedAlbums[index].id));
               },
               },
             );
             );
           },
           },
@@ -134,7 +142,9 @@ class SharingPage extends HookConsumerWidget {
               ),
               ),
             ),
             ),
           ),
           ),
-          sharedAlbums.isNotEmpty ? _buildAlbumList() : _buildEmptyListIndication()
+          sharedAlbums.isNotEmpty
+              ? _buildAlbumList()
+              : _buildEmptyListIndication()
         ],
         ],
       ),
       ),
     );
     );

+ 2 - 1
mobile/lib/shared/models/exif.model.dart

@@ -149,7 +149,8 @@ class ImmichExif {
 
 
   String toJson() => json.encode(toMap());
   String toJson() => json.encode(toMap());
 
 
-  factory ImmichExif.fromJson(String source) => ImmichExif.fromMap(json.decode(source));
+  factory ImmichExif.fromJson(String source) =>
+      ImmichExif.fromMap(json.decode(source));
 
 
   @override
   @override
   String toString() {
   String toString() {

+ 2 - 1
mobile/lib/shared/models/immich_asset.model.dart

@@ -95,7 +95,8 @@ class ImmichAsset extends Equatable {
 
 
   String toJson() => json.encode(toMap());
   String toJson() => json.encode(toMap());
 
 
-  factory ImmichAsset.fromJson(String source) => ImmichAsset.fromMap(json.decode(source));
+  factory ImmichAsset.fromJson(String source) =>
+      ImmichAsset.fromMap(json.decode(source));
 
 
   @override
   @override
   String toString() {
   String toString() {

+ 4 - 2
mobile/lib/shared/models/immich_asset_with_exif.model.dart

@@ -85,13 +85,15 @@ class ImmichAssetWithExif {
       originalPath: map['originalPath'] ?? '',
       originalPath: map['originalPath'] ?? '',
       isFavorite: map['isFavorite'] ?? false,
       isFavorite: map['isFavorite'] ?? false,
       duration: map['duration'],
       duration: map['duration'],
-      exifInfo: map['exifInfo'] != null ? ImmichExif.fromMap(map['exifInfo']) : null,
+      exifInfo:
+          map['exifInfo'] != null ? ImmichExif.fromMap(map['exifInfo']) : null,
     );
     );
   }
   }
 
 
   String toJson() => json.encode(toMap());
   String toJson() => json.encode(toMap());
 
 
-  factory ImmichAssetWithExif.fromJson(String source) => ImmichAssetWithExif.fromMap(json.decode(source));
+  factory ImmichAssetWithExif.fromJson(String source) =>
+      ImmichAssetWithExif.fromMap(json.decode(source));
 
 
   @override
   @override
   String toString() {
   String toString() {

+ 7 - 3
mobile/lib/shared/models/mapbox_info.model.dart

@@ -34,16 +34,20 @@ class MapboxInfo {
 
 
   String toJson() => json.encode(toMap());
   String toJson() => json.encode(toMap());
 
 
-  factory MapboxInfo.fromJson(String source) => MapboxInfo.fromMap(json.decode(source));
+  factory MapboxInfo.fromJson(String source) =>
+      MapboxInfo.fromMap(json.decode(source));
 
 
   @override
   @override
-  String toString() => 'MapboxInfo(isEnable: $isEnable, mapboxSecret: $mapboxSecret)';
+  String toString() =>
+      'MapboxInfo(isEnable: $isEnable, mapboxSecret: $mapboxSecret)';
 
 
   @override
   @override
   bool operator ==(Object other) {
   bool operator ==(Object other) {
     if (identical(this, other)) return true;
     if (identical(this, other)) return true;
 
 
-    return other is MapboxInfo && other.isEnable == isEnable && other.mapboxSecret == mapboxSecret;
+    return other is MapboxInfo &&
+        other.isEnable == isEnable &&
+        other.mapboxSecret == mapboxSecret;
   }
   }
 
 
   @override
   @override

+ 2 - 1
mobile/lib/shared/models/server_info.model.dart

@@ -64,7 +64,8 @@ class ServerInfo {
 
 
   String toJson() => json.encode(toMap());
   String toJson() => json.encode(toMap());
 
 
-  factory ServerInfo.fromJson(String source) => ServerInfo.fromMap(json.decode(source));
+  factory ServerInfo.fromJson(String source) =>
+      ServerInfo.fromMap(json.decode(source));
 
 
   @override
   @override
   String toString() {
   String toString() {

+ 4 - 2
mobile/lib/shared/models/server_info_state.model.dart

@@ -26,7 +26,8 @@ class ServerInfoState {
       mapboxInfo: mapboxInfo ?? this.mapboxInfo,
       mapboxInfo: mapboxInfo ?? this.mapboxInfo,
       serverVersion: serverVersion ?? this.serverVersion,
       serverVersion: serverVersion ?? this.serverVersion,
       isVersionMismatch: isVersionMismatch ?? this.isVersionMismatch,
       isVersionMismatch: isVersionMismatch ?? this.isVersionMismatch,
-      versionMismatchErrorMessage: versionMismatchErrorMessage ?? this.versionMismatchErrorMessage,
+      versionMismatchErrorMessage:
+          versionMismatchErrorMessage ?? this.versionMismatchErrorMessage,
     );
     );
   }
   }
 
 
@@ -50,7 +51,8 @@ class ServerInfoState {
 
 
   String toJson() => json.encode(toMap());
   String toJson() => json.encode(toMap());
 
 
-  factory ServerInfoState.fromJson(String source) => ServerInfoState.fromMap(json.decode(source));
+  factory ServerInfoState.fromJson(String source) =>
+      ServerInfoState.fromMap(json.decode(source));
 
 
   @override
   @override
   String toString() {
   String toString() {

+ 2 - 1
mobile/lib/shared/models/server_version.model.dart

@@ -47,7 +47,8 @@ class ServerVersion {
 
 
   String toJson() => json.encode(toMap());
   String toJson() => json.encode(toMap());
 
 
-  factory ServerVersion.fromJson(String source) => ServerVersion.fromMap(json.decode(source));
+  factory ServerVersion.fromJson(String source) =>
+      ServerVersion.fromMap(json.decode(source));
 
 
   @override
   @override
   String toString() {
   String toString() {

+ 7 - 3
mobile/lib/shared/models/upload_profile_image_repsonse.model.dart

@@ -36,16 +36,20 @@ class UploadProfileImageResponse {
 
 
   String toJson() => json.encode(toMap());
   String toJson() => json.encode(toMap());
 
 
-  factory UploadProfileImageResponse.fromJson(String source) => UploadProfileImageResponse.fromMap(json.decode(source));
+  factory UploadProfileImageResponse.fromJson(String source) =>
+      UploadProfileImageResponse.fromMap(json.decode(source));
 
 
   @override
   @override
-  String toString() => 'UploadProfileImageReponse(userId: $userId, profileImagePath: $profileImagePath)';
+  String toString() =>
+      'UploadProfileImageReponse(userId: $userId, profileImagePath: $profileImagePath)';
 
 
   @override
   @override
   bool operator ==(Object other) {
   bool operator ==(Object other) {
     if (identical(this, other)) return true;
     if (identical(this, other)) return true;
 
 
-    return other is UploadProfileImageResponse && other.userId == userId && other.profileImagePath == profileImagePath;
+    return other is UploadProfileImageResponse &&
+        other.userId == userId &&
+        other.profileImagePath == profileImagePath;
   }
   }
 
 
   @override
   @override

+ 7 - 6
mobile/lib/shared/models/user.model.dart

@@ -56,18 +56,19 @@ class User {
   factory User.fromJson(String source) => User.fromMap(json.decode(source));
   factory User.fromJson(String source) => User.fromMap(json.decode(source));
 
 
   @override
   @override
-  String toString() => 'UserInfo(id: $id, email: $email, createdAt: $createdAt)';
+  String toString() =>
+      'UserInfo(id: $id, email: $email, createdAt: $createdAt)';
 
 
   @override
   @override
   bool operator ==(Object other) {
   bool operator ==(Object other) {
     if (identical(this, other)) return true;
     if (identical(this, other)) return true;
 
 
     return other is User &&
     return other is User &&
-      other.id == id &&
-      other.email == email &&
-      other.createdAt == createdAt &&
-      other.firstName == firstName &&
-      other.lastName == lastName;
+        other.id == id &&
+        other.email == email &&
+        other.createdAt == createdAt &&
+        other.firstName == firstName &&
+        other.lastName == lastName;
   }
   }
 
 
   @override
   @override

+ 1 - 1
mobile/lib/shared/providers/asset.provider.dart

@@ -45,7 +45,7 @@ class AssetNotifier extends StateNotifier<List<ImmichAsset>> {
         }
         }
       }
       }
     }
     }
-    
+
     try {
     try {
       await PhotoManager.editor.deleteWithIds(deleteIdList);
       await PhotoManager.editor.deleteWithIds(deleteIdList);
     } catch (e) {
     } catch (e) {

+ 9 - 4
mobile/lib/shared/providers/websocket.provider.dart

@@ -29,13 +29,16 @@ class WebscoketState {
   }
   }
 
 
   @override
   @override
-  String toString() => 'WebscoketState(socket: $socket, isConnected: $isConnected)';
+  String toString() =>
+      'WebscoketState(socket: $socket, isConnected: $isConnected)';
 
 
   @override
   @override
   bool operator ==(Object other) {
   bool operator ==(Object other) {
     if (identical(this, other)) return true;
     if (identical(this, other)) return true;
 
 
-    return other is WebscoketState && other.socket == socket && other.isConnected == isConnected;
+    return other is WebscoketState &&
+        other.socket == socket &&
+        other.isConnected == isConnected;
   }
   }
 
 
   @override
   @override
@@ -43,7 +46,8 @@ class WebscoketState {
 }
 }
 
 
 class WebsocketNotifier extends StateNotifier<WebscoketState> {
 class WebsocketNotifier extends StateNotifier<WebscoketState> {
-  WebsocketNotifier(this.ref) : super(WebscoketState(socket: null, isConnected: false)) {
+  WebsocketNotifier(this.ref)
+      : super(WebscoketState(socket: null, isConnected: false)) {
     debugPrint("Init websocket instance");
     debugPrint("Init websocket instance");
   }
   }
 
 
@@ -122,6 +126,7 @@ class WebsocketNotifier extends StateNotifier<WebscoketState> {
   }
   }
 }
 }
 
 
-final websocketProvider = StateNotifierProvider<WebsocketNotifier, WebscoketState>((ref) {
+final websocketProvider =
+    StateNotifierProvider<WebsocketNotifier, WebscoketState>((ref) {
   return WebsocketNotifier(ref);
   return WebsocketNotifier(ref);
 });
 });

+ 7 - 3
mobile/lib/shared/ui/immich_sliver_persistent_app_bar_delegate.dart

@@ -2,7 +2,8 @@ import 'dart:math';
 
 
 import 'package:flutter/material.dart';
 import 'package:flutter/material.dart';
 
 
-class ImmichSliverPersistentAppBarDelegate extends SliverPersistentHeaderDelegate {
+class ImmichSliverPersistentAppBarDelegate
+    extends SliverPersistentHeaderDelegate {
   final double minHeight;
   final double minHeight;
   final double maxHeight;
   final double maxHeight;
   final Widget child;
   final Widget child;
@@ -20,12 +21,15 @@ class ImmichSliverPersistentAppBarDelegate extends SliverPersistentHeaderDelegat
   double get maxExtent => max(maxHeight, minHeight);
   double get maxExtent => max(maxHeight, minHeight);
 
 
   @override
   @override
-  Widget build(BuildContext context, double shrinkOffset, bool overlapsContent) {
+  Widget build(
+      BuildContext context, double shrinkOffset, bool overlapsContent) {
     return SizedBox.expand(child: child);
     return SizedBox.expand(child: child);
   }
   }
 
 
   @override
   @override
   bool shouldRebuild(ImmichSliverPersistentAppBarDelegate oldDelegate) {
   bool shouldRebuild(ImmichSliverPersistentAppBarDelegate oldDelegate) {
-    return maxHeight != oldDelegate.maxHeight || minHeight != oldDelegate.minHeight || child != oldDelegate.child;
+    return maxHeight != oldDelegate.maxHeight ||
+        minHeight != oldDelegate.minHeight ||
+        child != oldDelegate.child;
   }
   }
 }
 }

+ 4 - 2
mobile/lib/shared/views/splash_screen.dart

@@ -15,12 +15,14 @@ class SplashScreenPage extends HookConsumerWidget {
 
 
   @override
   @override
   Widget build(BuildContext context, WidgetRef ref) {
   Widget build(BuildContext context, WidgetRef ref) {
-    HiveSavedLoginInfo? loginInfo = Hive.box<HiveSavedLoginInfo>(hiveLoginInfoBox).get(savedLoginInfoKey);
+    HiveSavedLoginInfo? loginInfo =
+        Hive.box<HiveSavedLoginInfo>(hiveLoginInfoBox).get(savedLoginInfoKey);
 
 
     void performLoggingIn() async {
     void performLoggingIn() async {
       var isAuthenticated = await ref
       var isAuthenticated = await ref
           .read(authenticationProvider.notifier)
           .read(authenticationProvider.notifier)
-          .login(loginInfo!.email, loginInfo.password, loginInfo.serverUrl, true);
+          .login(
+              loginInfo!.email, loginInfo.password, loginInfo.serverUrl, true);
 
 
       if (isAuthenticated) {
       if (isAuthenticated) {
         // Resume backup (if enable) then navigate
         // Resume backup (if enable) then navigate

+ 12 - 6
mobile/lib/shared/views/tab_controller_page.dart

@@ -9,7 +9,8 @@ class TabControllerPage extends ConsumerWidget {
 
 
   @override
   @override
   Widget build(BuildContext context, WidgetRef ref) {
   Widget build(BuildContext context, WidgetRef ref) {
-    var isMultiSelectEnable = ref.watch(homePageStateProvider).isMultiSelectEnable;
+    var isMultiSelectEnable =
+        ref.watch(homePageStateProvider).isMultiSelectEnable;
 
 
     return AutoTabsRouter(
     return AutoTabsRouter(
       routes: [
       routes: [
@@ -32,16 +33,21 @@ class TabControllerPage extends ConsumerWidget {
             bottomNavigationBar: isMultiSelectEnable
             bottomNavigationBar: isMultiSelectEnable
                 ? null
                 ? null
                 : BottomNavigationBar(
                 : BottomNavigationBar(
-                    selectedLabelStyle: const TextStyle(fontSize: 15, fontWeight: FontWeight.w600),
-                    unselectedLabelStyle: const TextStyle(fontSize: 15, fontWeight: FontWeight.w600),
+                    selectedLabelStyle: const TextStyle(
+                        fontSize: 15, fontWeight: FontWeight.w600),
+                    unselectedLabelStyle: const TextStyle(
+                        fontSize: 15, fontWeight: FontWeight.w600),
                     currentIndex: tabsRouter.activeIndex,
                     currentIndex: tabsRouter.activeIndex,
                     onTap: (index) {
                     onTap: (index) {
                       tabsRouter.setActiveIndex(index);
                       tabsRouter.setActiveIndex(index);
                     },
                     },
                     items: const [
                     items: const [
-                      BottomNavigationBarItem(label: 'Photos', icon: Icon(Icons.photo)),
-                      BottomNavigationBarItem(label: 'Search', icon: Icon(Icons.search)),
-                      BottomNavigationBarItem(label: 'Sharing', icon: Icon(Icons.group_outlined)),
+                      BottomNavigationBarItem(
+                          label: 'Photos', icon: Icon(Icons.photo)),
+                      BottomNavigationBarItem(
+                          label: 'Search', icon: Icon(Icons.search)),
+                      BottomNavigationBarItem(
+                          label: 'Sharing', icon: Icon(Icons.group_outlined)),
                     ],
                     ],
                   ),
                   ),
           ),
           ),