浏览代码

Refactor mobile to use OpenApi generated SDK (#336)

Alex 3 年之前
父节点
当前提交
ae7e582ec8
共有 100 个文件被更改,包括 1545 次插入2797 次删除
  1. 10 1
      README.md
  2. 9 1
      mobile/analysis_options.yaml
  3. 3 1
      mobile/assets/i18n/en-US.json
  4. 13 8
      mobile/lib/main.dart
  5. 11 7
      mobile/lib/modules/asset_viewer/providers/image_viewer_page_state.provider.dart
  6. 17 15
      mobile/lib/modules/asset_viewer/services/image_viewer.service.dart
  7. 21 12
      mobile/lib/modules/asset_viewer/ui/exif_bottom_sheet.dart
  8. 41 29
      mobile/lib/modules/asset_viewer/ui/remote_photo_view.dart
  9. 15 14
      mobile/lib/modules/asset_viewer/ui/top_control_app_bar.dart
  10. 19 16
      mobile/lib/modules/asset_viewer/views/image_viewer_page.dart
  11. 14 10
      mobile/lib/modules/asset_viewer/views/video_viewer_page.dart
  12. 7 5
      mobile/lib/modules/backup/models/backup_state.model.dart
  13. 0 48
      mobile/lib/modules/backup/models/check_duplicate_asset_response.model.dart
  14. 39 30
      mobile/lib/modules/backup/providers/backup.provider.dart
  15. 53 57
      mobile/lib/modules/backup/services/backup.service.dart
  16. 23 11
      mobile/lib/modules/backup/ui/album_info_card.dart
  17. 6 6
      mobile/lib/modules/backup/ui/backup_info_card.dart
  18. 16 10
      mobile/lib/modules/backup/views/album_preview_page.dart
  19. 31 19
      mobile/lib/modules/backup/views/backup_album_selection_page.dart
  20. 73 55
      mobile/lib/modules/backup/views/backup_controller_page.dart
  21. 12 10
      mobile/lib/modules/backup/views/failed_backup_status_page.dart
  22. 0 55
      mobile/lib/modules/home/models/delete_asset_response.model.dart
  23. 3 47
      mobile/lib/modules/home/models/get_all_asset_response.model.dart
  24. 3 27
      mobile/lib/modules/home/models/home_page_state.model.dart
  25. 17 12
      mobile/lib/modules/home/providers/home_page_state.provider.dart
  26. 11 7
      mobile/lib/modules/home/providers/upload_profile_image.provider.dart
  27. 23 92
      mobile/lib/modules/home/services/asset.service.dart
  28. 9 7
      mobile/lib/modules/home/ui/control_bottom_app_bar.dart
  29. 10 6
      mobile/lib/modules/home/ui/daily_title_text.dart
  30. 12 9
      mobile/lib/modules/home/ui/disable_multi_select_button.dart
  31. 68 48
      mobile/lib/modules/home/ui/draggable_scrollbar.dart
  32. 3 3
      mobile/lib/modules/home/ui/image_grid.dart
  33. 6 3
      mobile/lib/modules/home/ui/immich_sliver_appbar.dart
  34. 2 2
      mobile/lib/modules/home/ui/monthly_title_text.dart
  35. 27 16
      mobile/lib/modules/home/ui/profile_drawer.dart
  36. 16 12
      mobile/lib/modules/home/ui/thumbnail_image.dart
  37. 9 6
      mobile/lib/modules/home/views/home_page.dart
  38. 5 47
      mobile/lib/modules/login/models/authentication_state.model.dart
  39. 6 5
      mobile/lib/modules/login/models/hive_saved_login_info.model.dart
  40. 78 75
      mobile/lib/modules/login/providers/authentication.provider.dart
  41. 27 26
      mobile/lib/modules/login/ui/change_password_form.dart
  42. 71 61
      mobile/lib/modules/login/ui/login_form.dart
  43. 0 84
      mobile/lib/modules/search/models/curated_location.model.dart
  44. 0 85
      mobile/lib/modules/search/models/curated_object.model.dart
  45. 7 6
      mobile/lib/modules/search/models/search_result_page_state.model.dart
  46. 3 4
      mobile/lib/modules/search/providers/search_page_state.provider.dart
  47. 25 11
      mobile/lib/modules/search/providers/search_result_page.provider.dart
  48. 22 47
      mobile/lib/modules/search/services/search.service.dart
  49. 9 6
      mobile/lib/modules/search/ui/search_bar.dart
  50. 6 7
      mobile/lib/modules/search/ui/thumbnail_with_info.dart
  51. 23 18
      mobile/lib/modules/search/views/search_page.dart
  52. 28 20
      mobile/lib/modules/search/views/search_result_page.dart
  53. 5 36
      mobile/lib/modules/sharing/models/asset_selection_page_result.model.dart
  54. 14 55
      mobile/lib/modules/sharing/models/asset_selection_state.model.dart
  55. 0 117
      mobile/lib/modules/sharing/models/shared_album.model.dart
  56. 2 1
      mobile/lib/modules/sharing/providers/album_title.provider.dart
  57. 4 1
      mobile/lib/modules/sharing/providers/album_viewer.provider.dart
  58. 32 27
      mobile/lib/modules/sharing/providers/asset_selection.provider.dart
  59. 11 7
      mobile/lib/modules/sharing/providers/shared_album.provider.dart
  60. 3 3
      mobile/lib/modules/sharing/providers/suggested_shared_users.provider.dart
  61. 65 78
      mobile/lib/modules/sharing/services/shared_album.service.dart
  62. 10 7
      mobile/lib/modules/sharing/ui/album_action_outlined_button.dart
  63. 4 1
      mobile/lib/modules/sharing/ui/album_title_text_field.dart
  64. 5 5
      mobile/lib/modules/sharing/ui/album_viewer_appbar.dart
  65. 16 11
      mobile/lib/modules/sharing/ui/album_viewer_editable_title.dart
  66. 15 17
      mobile/lib/modules/sharing/ui/album_viewer_thumbnail.dart
  67. 2 2
      mobile/lib/modules/sharing/ui/asset_grid_by_month.dart
  68. 12 6
      mobile/lib/modules/sharing/ui/month_group_title.dart
  69. 7 7
      mobile/lib/modules/sharing/ui/selection_thumbnail_image.dart
  70. 4 4
      mobile/lib/modules/sharing/ui/shared_album_thumbnail_image.dart
  71. 4 2
      mobile/lib/modules/sharing/ui/sharing_sliver_appbar.dart
  72. 46 29
      mobile/lib/modules/sharing/views/album_viewer_page.dart
  73. 73 65
      mobile/lib/modules/sharing/views/create_shared_album_page.dart
  74. 24 19
      mobile/lib/modules/sharing/views/select_additional_user_for_sharing_page.dart
  75. 35 24
      mobile/lib/modules/sharing/views/select_user_for_sharing_page.dart
  76. 15 11
      mobile/lib/modules/sharing/views/sharing_page.dart
  77. 13 9
      mobile/lib/routing/auth_guard.dart
  78. 8 3
      mobile/lib/routing/router.dart
  79. 6 6
      mobile/lib/routing/router.gr.dart
  80. 3 1
      mobile/lib/routing/tab_navigation_observer.dart
  81. 0 100
      mobile/lib/shared/models/device_info.model.dart
  82. 0 212
      mobile/lib/shared/models/exif.model.dart
  83. 0 110
      mobile/lib/shared/models/immich_asset.model.dart
  84. 0 135
      mobile/lib/shared/models/immich_asset_with_exif.model.dart
  85. 0 55
      mobile/lib/shared/models/mapbox_info.model.dart
  86. 0 99
      mobile/lib/shared/models/server_info.model.dart
  87. 5 37
      mobile/lib/shared/models/server_info_state.model.dart
  88. 0 73
      mobile/lib/shared/models/server_version.model.dart
  89. 0 76
      mobile/lib/shared/models/user.model.dart
  90. 24 16
      mobile/lib/shared/providers/asset.provider.dart
  91. 2 1
      mobile/lib/shared/providers/release_info.provider.dart
  92. 12 7
      mobile/lib/shared/providers/server_info.provider.dart
  93. 11 5
      mobile/lib/shared/providers/websocket.provider.dart
  94. 30 0
      mobile/lib/shared/services/api.service.dart
  95. 4 3
      mobile/lib/shared/services/device_info.service.dart
  96. 0 21
      mobile/lib/shared/services/local_storage.service.dart
  97. 5 4
      mobile/lib/shared/services/network.service.dart
  98. 19 19
      mobile/lib/shared/services/server_info.service.dart
  99. 29 50
      mobile/lib/shared/services/user.service.dart
  100. 4 1
      mobile/lib/shared/ui/immich_sliver_persistent_app_bar_delegate.dart

+ 10 - 1
README.md

@@ -161,7 +161,7 @@ To *update* docker-compose with newest image (if you have started the docker-com
 docker-compose -f ./docker/docker-compose.yml pull && docker-compose -f ./docker/docker-compose.yml up
 docker-compose -f ./docker/docker-compose.yml pull && docker-compose -f ./docker/docker-compose.yml up
 ```
 ```
 
 
-The server will be running at `http://your-ip:2283/api` through `Nginx`
+The server will be running at `http://your-ip:2283/api`
 
 
 ## Step 3: Register User
 ## Step 3: Register User
 
 
@@ -225,6 +225,15 @@ make dev # required Makefile installed on the system.
 
 
 All servers and web container are hot reload for quick feedback loop.
 All servers and web container are hot reload for quick feedback loop.
 
 
+## Note for developers
+### 1 - OpenAPI
+OpenAPI is used to generate the client (Typescript, Dart) SDK. `openapi-generator-cli` can be installed [here](https://openapi-generator.tech/docs/installation/). When you add a new or modify an existing endpoint, you must run the generate command below to update the client SDK.
+
+```bash
+npm run api:generate # Run from server directory
+```
+You can find the generated client SDK in the [`web/src/api`](web/src/api) for Typescript SDK and [`mobile/openapi`](mobile/openapi) for Dart SDK.
+
 # Support
 # Support
 
 
 If you like the app, find it helpful, and want to support me to offset the cost of publishing to AppStores, you can sponsor the project with [**Github Sponsor**](https://github.com/sponsors/alextran1502), or a one time donation with the Buy Me a coffee link below.
 If you like the app, find it helpful, and want to support me to offset the cost of publishing to AppStores, you can sponsor the project with [**Github Sponsor**](https://github.com/sponsors/alextran1502), or a one time donation with the Buy Me a coffee link below.

+ 9 - 1
mobile/analysis_options.yaml

@@ -21,10 +21,18 @@ linter:
   # or a specific dart file by using the `// ignore: name_of_lint` and
   # or a specific dart file by using the `// ignore: name_of_lint` and
   # `// ignore_for_file: name_of_lint` syntax on the line or in the file
   # `// ignore_for_file: name_of_lint` syntax on the line or in the file
   # producing the lint.
   # producing the lint.
+
   rules:
   rules:
     # avoid_print: false  # Uncomment to disable the `avoid_print` rule
     # avoid_print: false  # Uncomment to disable the `avoid_print` rule
-    # prefer_single_quotes: true  # Uncomment to enable the `prefer_single_quotes` rule
+    # prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule
     use_build_context_synchronously: false
     use_build_context_synchronously: false
+    require_trailing_commas: true
+    unrelated_type_equality_checks: true
 
 
 # Additional information about this file can be found at
 # Additional information about this file can be found at
 # https://dart.dev/guides/language/analysis-options
 # https://dart.dev/guides/language/analysis-options
+analyzer:
+  exclude:
+    - openapi/
+    - openapi/test/
+    - lib/generated_plugin_registrant.dart

+ 3 - 1
mobile/assets/i18n/en-US.json

@@ -71,6 +71,7 @@
   "login_form_label_password": "Password",
   "login_form_label_password": "Password",
   "login_form_password_hint": "password",
   "login_form_password_hint": "password",
   "login_form_save_login": "Stay logged in",
   "login_form_save_login": "Stay logged in",
+  "login_form_failed_login": "Error logging you in, check server url, email and password",
   "monthly_title_text_date_format": "MMMM y",
   "monthly_title_text_date_format": "MMMM y",
   "profile_drawer_client_server_up_to_date": "Client and Server are up-to-date",
   "profile_drawer_client_server_up_to_date": "Client and Server are up-to-date",
   "profile_drawer_sign_out": "Sign Out",
   "profile_drawer_sign_out": "Sign Out",
@@ -81,6 +82,7 @@
   "search_result_page_new_search_hint": "New Search",
   "search_result_page_new_search_hint": "New Search",
   "select_additional_user_for_sharing_page_suggestions": "Suggestions",
   "select_additional_user_for_sharing_page_suggestions": "Suggestions",
   "select_user_for_sharing_page_err_album": "Failed to create album",
   "select_user_for_sharing_page_err_album": "Failed to create album",
+  "select_user_for_sharing_page_share_suggestions": "Suggestions",
   "share_add": "Add",
   "share_add": "Add",
   "share_add_photos": "Add photos",
   "share_add_photos": "Add photos",
   "share_add_title": "Add a title",
   "share_add_title": "Add a title",
@@ -100,4 +102,4 @@
   "version_announcement_overlay_text_2": "please take your time to visit the ",
   "version_announcement_overlay_text_2": "please take your time to visit the ",
   "version_announcement_overlay_text_3": " and ensure your docker-compose and .env setup is up-to-date to prevent any misconfigurations, especially if you use WatchTower or any mechanism that handles updating your server application automatically.",
   "version_announcement_overlay_text_3": " and ensure your docker-compose and .env setup is up-to-date to prevent any misconfigurations, especially if you use WatchTower or any mechanism that handles updating your server application automatically.",
   "version_announcement_overlay_title": "New Server Version Available \uD83C\uDF89"
   "version_announcement_overlay_title": "New Server Version Available \uD83C\uDF89"
-}
+}

+ 13 - 8
mobile/lib/main.dart

@@ -46,12 +46,15 @@ void main() async {
     Locale('de', 'DE')
     Locale('de', 'DE')
   ];
   ];
 
 
-  runApp(EasyLocalization(
+  runApp(
+    EasyLocalization(
       supportedLocales: locales,
       supportedLocales: locales,
       path: 'assets/i18n',
       path: 'assets/i18n',
       useFallbackTranslations: true,
       useFallbackTranslations: true,
       fallbackLocale: locales.first,
       fallbackLocale: locales.first,
-      child: const ProviderScope(child: ImmichApp())));
+      child: const ProviderScope(child: ImmichApp()),
+    ),
+  );
 }
 }
 
 
 class ImmichApp extends ConsumerStatefulWidget {
 class ImmichApp extends ConsumerStatefulWidget {
@@ -111,6 +114,7 @@ class ImmichAppState extends ConsumerState<ImmichApp>
   @override
   @override
   initState() {
   initState() {
     super.initState();
     super.initState();
+
     initApp().then((_) => debugPrint("App Init Completed"));
     initApp().then((_) => debugPrint("App Init Completed"));
   }
   }
 
 
@@ -120,10 +124,9 @@ class ImmichAppState extends ConsumerState<ImmichApp>
     super.dispose();
     super.dispose();
   }
   }
 
 
-  final _immichRouter = AppRouter();
-
   @override
   @override
   Widget build(BuildContext context) {
   Widget build(BuildContext context) {
+    var router = ref.watch(appRouterProvider);
     ref.watch(releaseInfoProvider.notifier).checkGithubReleaseInfo();
     ref.watch(releaseInfoProvider.notifier).checkGithubReleaseInfo();
 
 
     return MaterialApp(
     return MaterialApp(
@@ -142,7 +145,8 @@ class ImmichAppState extends ConsumerState<ImmichApp>
               primarySwatch: Colors.indigo,
               primarySwatch: Colors.indigo,
               fontFamily: 'WorkSans',
               fontFamily: 'WorkSans',
               snackBarTheme: const SnackBarThemeData(
               snackBarTheme: const SnackBarThemeData(
-                  contentTextStyle: TextStyle(fontFamily: 'WorkSans')),
+                contentTextStyle: TextStyle(fontFamily: 'WorkSans'),
+              ),
               scaffoldBackgroundColor: immichBackgroundColor,
               scaffoldBackgroundColor: immichBackgroundColor,
               appBarTheme: const AppBarTheme(
               appBarTheme: const AppBarTheme(
                 backgroundColor: immichBackgroundColor,
                 backgroundColor: immichBackgroundColor,
@@ -152,9 +156,10 @@ class ImmichAppState extends ConsumerState<ImmichApp>
                 systemOverlayStyle: SystemUiOverlayStyle.dark,
                 systemOverlayStyle: SystemUiOverlayStyle.dark,
               ),
               ),
             ),
             ),
-            routeInformationParser: _immichRouter.defaultRouteParser(),
-            routerDelegate: _immichRouter.delegate(
-                navigatorObservers: () => [TabNavigationObserver(ref: ref)]),
+            routeInformationParser: router.defaultRouteParser(),
+            routerDelegate: router.delegate(
+              navigatorObservers: () => [TabNavigationObserver(ref: ref)],
+            ),
           ),
           ),
           const ImmichLoadingOverlay(),
           const ImmichLoadingOverlay(),
           const VersionAnnouncementOverlay(),
           const VersionAnnouncementOverlay(),

+ 11 - 7
mobile/lib/modules/asset_viewer/providers/image_viewer_page_state.provider.dart

@@ -3,17 +3,20 @@ import 'package:fluttertoast/fluttertoast.dart';
 import 'package:hooks_riverpod/hooks_riverpod.dart';
 import 'package:hooks_riverpod/hooks_riverpod.dart';
 import 'package:immich_mobile/modules/asset_viewer/models/image_viewer_page_state.model.dart';
 import 'package:immich_mobile/modules/asset_viewer/models/image_viewer_page_state.model.dart';
 import 'package:immich_mobile/modules/asset_viewer/services/image_viewer.service.dart';
 import 'package:immich_mobile/modules/asset_viewer/services/image_viewer.service.dart';
-import 'package:immich_mobile/shared/models/immich_asset.model.dart';
 import 'package:immich_mobile/shared/ui/immich_toast.dart';
 import 'package:immich_mobile/shared/ui/immich_toast.dart';
+import 'package:openapi/api.dart';
 
 
 class ImageViewerStateNotifier extends StateNotifier<ImageViewerPageState> {
 class ImageViewerStateNotifier extends StateNotifier<ImageViewerPageState> {
-  final ImageViewerService _imageViewerService = ImageViewerService();
+  final ImageViewerService _imageViewerService;
 
 
-  ImageViewerStateNotifier()
-      : super(ImageViewerPageState(
-            downloadAssetStatus: DownloadAssetStatus.idle));
+  ImageViewerStateNotifier(this._imageViewerService)
+      : super(
+          ImageViewerPageState(
+            downloadAssetStatus: DownloadAssetStatus.idle,
+          ),
+        );
 
 
-  void downloadAsset(ImmichAsset asset, BuildContext context) async {
+  void downloadAsset(AssetResponseDto asset, BuildContext context) async {
     state = state.copyWith(downloadAssetStatus: DownloadAssetStatus.loading);
     state = state.copyWith(downloadAssetStatus: DownloadAssetStatus.loading);
 
 
     bool isSuccess = await _imageViewerService.downloadAssetToDevice(asset);
     bool isSuccess = await _imageViewerService.downloadAssetToDevice(asset);
@@ -43,4 +46,5 @@ class ImageViewerStateNotifier extends StateNotifier<ImageViewerPageState> {
 
 
 final imageViewerStateProvider =
 final imageViewerStateProvider =
     StateNotifierProvider<ImageViewerStateNotifier, ImageViewerPageState>(
     StateNotifierProvider<ImageViewerStateNotifier, ImageViewerPageState>(
-        ((ref) => ImageViewerStateNotifier()));
+  ((ref) => ImageViewerStateNotifier(ref.watch(imageViewerServiceProvider))),
+);

+ 17 - 15
mobile/lib/modules/asset_viewer/services/image_viewer.service.dart

@@ -1,33 +1,35 @@
 import 'dart:io';
 import 'dart:io';
 
 
 import 'package:flutter/foundation.dart';
 import 'package:flutter/foundation.dart';
-import 'package:hive_flutter/hive_flutter.dart';
-import 'package:immich_mobile/constants/hive_box.dart';
-import 'package:immich_mobile/shared/models/immich_asset.model.dart';
+import 'package:hooks_riverpod/hooks_riverpod.dart';
+import 'package:immich_mobile/shared/services/api.service.dart';
+import 'package:openapi/api.dart';
 import 'package:path/path.dart' as p;
 import 'package:path/path.dart' as p;
-import 'package:http/http.dart' as http;
 
 
 import 'package:photo_manager/photo_manager.dart';
 import 'package:photo_manager/photo_manager.dart';
 import 'package:path_provider/path_provider.dart';
 import 'package:path_provider/path_provider.dart';
 
 
+final imageViewerServiceProvider =
+    Provider((ref) => ImageViewerService(ref.watch(apiServiceProvider)));
+
 class ImageViewerService {
 class ImageViewerService {
-  Future<bool> downloadAssetToDevice(ImmichAsset asset) async {
+  final ApiService _apiService;
+  ImageViewerService(this._apiService);
+
+  Future<bool> downloadAssetToDevice(AssetResponseDto asset) async {
     try {
     try {
       String fileName = p.basename(asset.originalPath);
       String fileName = p.basename(asset.originalPath);
-      var savedEndpoint = Hive.box(userInfoBox).get(serverEndpointKey);
-      Uri filePath = Uri.parse(
-          "$savedEndpoint/asset/download?aid=${asset.deviceAssetId}&did=${asset.deviceId}&isThumb=false");
-
-      var res = await http.get(
-        filePath,
-        headers: {
-          "Authorization": "Bearer ${Hive.box(userInfoBox).get(accessTokenKey)}"
-        },
+
+      var res = await _apiService.assetApi.downloadFileWithHttpInfo(
+        asset.deviceAssetId,
+        asset.deviceId,
+        isThumb: false,
+        isWeb: false,
       );
       );
 
 
       final AssetEntity? entity;
       final AssetEntity? entity;
 
 
-      if (asset.type == 'IMAGE') {
+      if (asset.type == AssetTypeEnum.IMAGE) {
         entity = await PhotoManager.editor.saveImage(
         entity = await PhotoManager.editor.saveImage(
           res.bodyBytes,
           res.bodyBytes,
           title: p.basename(asset.originalPath),
           title: p.basename(asset.originalPath),

+ 21 - 12
mobile/lib/modules/asset_viewer/ui/exif_bottom_sheet.dart

@@ -2,13 +2,12 @@ import 'package:easy_localization/easy_localization.dart';
 import 'package:flutter/material.dart';
 import 'package:flutter/material.dart';
 import 'package:flutter_map/flutter_map.dart';
 import 'package:flutter_map/flutter_map.dart';
 import 'package:hooks_riverpod/hooks_riverpod.dart';
 import 'package:hooks_riverpod/hooks_riverpod.dart';
-import 'package:immich_mobile/shared/models/immich_asset_with_exif.model.dart';
-import 'package:intl/intl.dart';
+import 'package:openapi/api.dart';
 import 'package:path/path.dart' as p;
 import 'package:path/path.dart' as p;
 import 'package:latlong2/latlong.dart';
 import 'package:latlong2/latlong.dart';
 
 
 class ExifBottomSheet extends ConsumerWidget {
 class ExifBottomSheet extends ConsumerWidget {
-  final ImmichAssetWithExif assetDetail;
+  final AssetResponseDto assetDetail;
 
 
   const ExifBottomSheet({Key? key, required this.assetDetail})
   const ExifBottomSheet({Key? key, required this.assetDetail})
       : super(key: key);
       : super(key: key);
@@ -26,8 +25,10 @@ class ExifBottomSheet extends ConsumerWidget {
           ),
           ),
           child: FlutterMap(
           child: FlutterMap(
             options: MapOptions(
             options: MapOptions(
-              center: LatLng(assetDetail.exifInfo!.latitude!,
-                  assetDetail.exifInfo!.longitude!),
+              center: LatLng(
+                assetDetail.exifInfo?.latitude?.toDouble() ?? 0,
+                assetDetail.exifInfo?.longitude?.toDouble() ?? 0,
+              ),
               zoom: 16.0,
               zoom: 16.0,
             ),
             ),
             layers: [
             layers: [
@@ -46,10 +47,13 @@ class ExifBottomSheet extends ConsumerWidget {
                 markers: [
                 markers: [
                   Marker(
                   Marker(
                     anchorPos: AnchorPos.align(AnchorAlign.top),
                     anchorPos: AnchorPos.align(AnchorAlign.top),
-                    point: LatLng(assetDetail.exifInfo!.latitude!,
-                        assetDetail.exifInfo!.longitude!),
+                    point: LatLng(
+                      assetDetail.exifInfo?.latitude?.toDouble() ?? 0,
+                      assetDetail.exifInfo?.longitude?.toDouble() ?? 0,
+                    ),
                     builder: (ctx) => const Image(
                     builder: (ctx) => const Image(
-                        image: AssetImage('assets/location-pin.png')),
+                      image: AssetImage('assets/location-pin.png'),
+                    ),
                   ),
                   ),
                 ],
                 ],
               ),
               ),
@@ -63,7 +67,10 @@ class ExifBottomSheet extends ConsumerWidget {
       return Text(
       return Text(
         "${assetDetail.exifInfo!.city}, ${assetDetail.exifInfo!.state}",
         "${assetDetail.exifInfo!.city}, ${assetDetail.exifInfo!.state}",
         style: TextStyle(
         style: TextStyle(
-            fontSize: 12, color: Colors.grey[200], fontWeight: FontWeight.bold),
+          fontSize: 12,
+          color: Colors.grey[200],
+          fontWeight: FontWeight.bold,
+        ),
       );
       );
     }
     }
 
 
@@ -74,7 +81,7 @@ class ExifBottomSheet extends ConsumerWidget {
           if (assetDetail.exifInfo?.dateTimeOriginal != null)
           if (assetDetail.exifInfo?.dateTimeOriginal != null)
             Text(
             Text(
               DateFormat('date_format'.tr()).format(
               DateFormat('date_format'.tr()).format(
-                DateTime.parse(assetDetail.exifInfo!.dateTimeOriginal!),
+                assetDetail.exifInfo!.dateTimeOriginal!,
               ),
               ),
               style: TextStyle(
               style: TextStyle(
                 color: Colors.grey[400],
                 color: Colors.grey[400],
@@ -151,7 +158,8 @@ class ExifBottomSheet extends ConsumerWidget {
                     ),
                     ),
                     subtitle: assetDetail.exifInfo?.exifImageHeight != null
                     subtitle: assetDetail.exifInfo?.exifImageHeight != null
                         ? Text(
                         ? Text(
-                            "${assetDetail.exifInfo?.exifImageHeight} x ${assetDetail.exifInfo?.exifImageWidth}  ${assetDetail.exifInfo?.fileSizeInByte!}B ")
+                            "${assetDetail.exifInfo?.exifImageHeight} x ${assetDetail.exifInfo?.exifImageWidth}  ${assetDetail.exifInfo?.fileSizeInByte!}B ",
+                          )
                         : null,
                         : null,
                   ),
                   ),
                   if (assetDetail.exifInfo?.make != null)
                   if (assetDetail.exifInfo?.make != null)
@@ -166,7 +174,8 @@ class ExifBottomSheet extends ConsumerWidget {
                         style: const TextStyle(fontWeight: FontWeight.bold),
                         style: const TextStyle(fontWeight: FontWeight.bold),
                       ),
                       ),
                       subtitle: Text(
                       subtitle: Text(
-                          "ƒ/${assetDetail.exifInfo?.fNumber}   1/${(1 / (assetDetail.exifInfo?.exposureTime ?? 1)).toStringAsFixed(0)}   ${assetDetail.exifInfo?.focalLength}mm   ISO${assetDetail.exifInfo?.iso} "),
+                        "ƒ/${assetDetail.exifInfo?.fNumber}   1/${(1 / (assetDetail.exifInfo?.exposureTime ?? 1)).toStringAsFixed(0)}   ${assetDetail.exifInfo?.focalLength}mm   ISO${assetDetail.exifInfo?.iso} ",
+                      ),
                     ),
                     ),
                 ],
                 ],
               ),
               ),

+ 41 - 29
mobile/lib/modules/asset_viewer/ui/remote_photo_view.dart

@@ -17,16 +17,20 @@ class _RemotePhotoViewState extends State<RemotePhotoView> {
     bool allowMoving = _status == _RemoteImageStatus.full;
     bool allowMoving = _status == _RemoteImageStatus.full;
 
 
     return PhotoView(
     return PhotoView(
-        imageProvider: _imageProvider,
-        minScale: PhotoViewComputedScale.contained,
-        maxScale: allowMoving ? 1.0 : PhotoViewComputedScale.contained,
-        enablePanAlways: true,
-        scaleStateChangedCallback: _scaleStateChanged,
-        onScaleEnd: _onScaleListener);
+      imageProvider: _imageProvider,
+      minScale: PhotoViewComputedScale.contained,
+      maxScale: allowMoving ? 1.0 : PhotoViewComputedScale.contained,
+      enablePanAlways: true,
+      scaleStateChangedCallback: _scaleStateChanged,
+      onScaleEnd: _onScaleListener,
+    );
   }
   }
 
 
-  void _onScaleListener(BuildContext context, ScaleEndDetails details,
-      PhotoViewControllerValue controllerValue) {
+  void _onScaleListener(
+    BuildContext context,
+    ScaleEndDetails details,
+    PhotoViewControllerValue controllerValue,
+  ) {
     // Disable swipe events when zoomed in
     // Disable swipe events when zoomed in
     if (_zoomedIn) return;
     if (_zoomedIn) return;
 
 
@@ -42,12 +46,17 @@ class _RemotePhotoViewState extends State<RemotePhotoView> {
   }
   }
 
 
   CachedNetworkImageProvider _authorizedImageProvider(String url) {
   CachedNetworkImageProvider _authorizedImageProvider(String url) {
-    return CachedNetworkImageProvider(url,
-        headers: {"Authorization": widget.authToken}, cacheKey: url);
+    return CachedNetworkImageProvider(
+      url,
+      headers: {"Authorization": widget.authToken},
+      cacheKey: url,
+    );
   }
   }
 
 
   void _performStateTransition(
   void _performStateTransition(
-      _RemoteImageStatus newStatus, CachedNetworkImageProvider provider) {
+    _RemoteImageStatus newStatus,
+    CachedNetworkImageProvider provider,
+  ) {
     // Transition to same status is forbidden
     // Transition to same status is forbidden
     if (_status == newStatus) return;
     if (_status == newStatus) return;
     // Transition full -> thumbnail is forbidden
     // Transition full -> thumbnail is forbidden
@@ -67,19 +76,22 @@ class _RemotePhotoViewState extends State<RemotePhotoView> {
         _authorizedImageProvider(widget.thumbnailUrl);
         _authorizedImageProvider(widget.thumbnailUrl);
     _imageProvider = thumbnailProvider;
     _imageProvider = thumbnailProvider;
 
 
-    thumbnailProvider
-        .resolve(const ImageConfiguration())
-        .addListener(ImageStreamListener((ImageInfo imageInfo, _) {
-      _performStateTransition(_RemoteImageStatus.thumbnail, thumbnailProvider);
-    }));
+    thumbnailProvider.resolve(const ImageConfiguration()).addListener(
+      ImageStreamListener((ImageInfo imageInfo, _) {
+        _performStateTransition(
+          _RemoteImageStatus.thumbnail,
+          thumbnailProvider,
+        );
+      }),
+    );
 
 
     CachedNetworkImageProvider fullProvider =
     CachedNetworkImageProvider fullProvider =
         _authorizedImageProvider(widget.imageUrl);
         _authorizedImageProvider(widget.imageUrl);
-    fullProvider
-        .resolve(const ImageConfiguration())
-        .addListener(ImageStreamListener((ImageInfo imageInfo, _) {
-      _performStateTransition(_RemoteImageStatus.full, fullProvider);
-    }));
+    fullProvider.resolve(const ImageConfiguration()).addListener(
+      ImageStreamListener((ImageInfo imageInfo, _) {
+        _performStateTransition(_RemoteImageStatus.full, fullProvider);
+      }),
+    );
   }
   }
 
 
   @override
   @override
@@ -90,14 +102,14 @@ class _RemotePhotoViewState extends State<RemotePhotoView> {
 }
 }
 
 
 class RemotePhotoView extends StatefulWidget {
 class RemotePhotoView extends StatefulWidget {
-  const RemotePhotoView(
-      {Key? key,
-      required this.thumbnailUrl,
-      required this.imageUrl,
-      required this.authToken,
-      required this.onSwipeDown,
-      required this.onSwipeUp})
-      : super(key: key);
+  const RemotePhotoView({
+    Key? key,
+    required this.thumbnailUrl,
+    required this.imageUrl,
+    required this.authToken,
+    required this.onSwipeDown,
+    required this.onSwipeUp,
+  }) : super(key: key);
 
 
   final String thumbnailUrl;
   final String thumbnailUrl;
   final String imageUrl;
   final String imageUrl;

+ 15 - 14
mobile/lib/modules/asset_viewer/ui/top_control_app_bar.dart

@@ -3,17 +3,17 @@ import 'dart:developer';
 import 'package:auto_route/auto_route.dart';
 import 'package:auto_route/auto_route.dart';
 import 'package:flutter/material.dart';
 import 'package:flutter/material.dart';
 import 'package:hooks_riverpod/hooks_riverpod.dart';
 import 'package:hooks_riverpod/hooks_riverpod.dart';
-import 'package:immich_mobile/shared/models/immich_asset.model.dart';
+import 'package:openapi/api.dart';
 
 
 class TopControlAppBar extends ConsumerWidget with PreferredSizeWidget {
 class TopControlAppBar extends ConsumerWidget with PreferredSizeWidget {
-  const TopControlAppBar(
-      {Key? key,
-      required this.asset,
-      required this.onMoreInfoPressed,
-      required this.onDownloadPressed})
-      : super(key: key);
+  const TopControlAppBar({
+    Key? key,
+    required this.asset,
+    required this.onMoreInfoPressed,
+    required this.onDownloadPressed,
+  }) : super(key: key);
 
 
-  final ImmichAsset asset;
+  final AssetResponseDto asset;
   final Function onMoreInfoPressed;
   final Function onMoreInfoPressed;
   final Function onDownloadPressed;
   final Function onDownloadPressed;
 
 
@@ -54,12 +54,13 @@ class TopControlAppBar extends ConsumerWidget with PreferredSizeWidget {
               : const Icon(Icons.favorite_border_rounded),
               : const Icon(Icons.favorite_border_rounded),
         ),
         ),
         IconButton(
         IconButton(
-            iconSize: iconSize,
-            splashRadius: iconSize,
-            onPressed: () {
-              onMoreInfoPressed();
-            },
-            icon: const Icon(Icons.more_horiz_rounded))
+          iconSize: iconSize,
+          splashRadius: iconSize,
+          onPressed: () {
+            onMoreInfoPressed();
+          },
+          icon: const Icon(Icons.more_horiz_rounded),
+        )
       ],
       ],
     );
     );
   }
   }

+ 19 - 16
mobile/lib/modules/asset_viewer/views/image_viewer_page.dart

@@ -11,17 +11,16 @@ import 'package:immich_mobile/modules/asset_viewer/ui/exif_bottom_sheet.dart';
 import 'package:immich_mobile/modules/asset_viewer/ui/remote_photo_view.dart';
 import 'package:immich_mobile/modules/asset_viewer/ui/remote_photo_view.dart';
 import 'package:immich_mobile/modules/asset_viewer/ui/top_control_app_bar.dart';
 import 'package:immich_mobile/modules/asset_viewer/ui/top_control_app_bar.dart';
 import 'package:immich_mobile/modules/home/services/asset.service.dart';
 import 'package:immich_mobile/modules/home/services/asset.service.dart';
-import 'package:immich_mobile/shared/models/immich_asset.model.dart';
-import 'package:immich_mobile/shared/models/immich_asset_with_exif.model.dart';
+import 'package:openapi/api.dart';
 
 
 // ignore: must_be_immutable
 // ignore: must_be_immutable
 class ImageViewerPage extends HookConsumerWidget {
 class ImageViewerPage extends HookConsumerWidget {
   final String imageUrl;
   final String imageUrl;
   final String heroTag;
   final String heroTag;
   final String thumbnailUrl;
   final String thumbnailUrl;
-  final ImmichAsset asset;
+  final AssetResponseDto asset;
 
 
-  ImmichAssetWithExif? assetDetail;
+  AssetResponseDto? assetDetail;
 
 
   ImageViewerPage({
   ImageViewerPage({
     Key? key,
     Key? key,
@@ -54,10 +53,13 @@ class ImageViewerPage extends HookConsumerWidget {
       );
       );
     }
     }
 
 
-    useEffect(() {
-      getAssetExif();
-      return null;
-    }, []);
+    useEffect(
+      () {
+        getAssetExif();
+        return null;
+      },
+      [],
+    );
 
 
     return Scaffold(
     return Scaffold(
       backgroundColor: Colors.black,
       backgroundColor: Colors.black,
@@ -75,14 +77,15 @@ class ImageViewerPage extends HookConsumerWidget {
           children: [
           children: [
             Center(
             Center(
               child: Hero(
               child: Hero(
-                  tag: heroTag,
-                  child: RemotePhotoView(
-                    thumbnailUrl: thumbnailUrl,
-                    imageUrl: imageUrl,
-                    authToken: "Bearer ${box.get(accessTokenKey)}",
-                    onSwipeDown: () => AutoRouter.of(context).pop(),
-                    onSwipeUp: () => showInfo(),
-                  )),
+                tag: heroTag,
+                child: RemotePhotoView(
+                  thumbnailUrl: thumbnailUrl,
+                  imageUrl: imageUrl,
+                  authToken: "Bearer ${box.get(accessTokenKey)}",
+                  onSwipeDown: () => AutoRouter.of(context).pop(),
+                  onSwipeUp: () => showInfo(),
+                ),
+              ),
             ),
             ),
             if (downloadAssetStatus == DownloadAssetStatus.loading)
             if (downloadAssetStatus == DownloadAssetStatus.loading)
               const Center(
               const Center(

+ 14 - 10
mobile/lib/modules/asset_viewer/views/video_viewer_page.dart

@@ -12,15 +12,14 @@ import 'package:immich_mobile/modules/asset_viewer/ui/download_loading_indicator
 import 'package:immich_mobile/modules/asset_viewer/ui/exif_bottom_sheet.dart';
 import 'package:immich_mobile/modules/asset_viewer/ui/exif_bottom_sheet.dart';
 import 'package:immich_mobile/modules/asset_viewer/ui/top_control_app_bar.dart';
 import 'package:immich_mobile/modules/asset_viewer/ui/top_control_app_bar.dart';
 import 'package:immich_mobile/modules/home/services/asset.service.dart';
 import 'package:immich_mobile/modules/home/services/asset.service.dart';
-import 'package:immich_mobile/shared/models/immich_asset.model.dart';
-import 'package:immich_mobile/shared/models/immich_asset_with_exif.model.dart';
+import 'package:openapi/api.dart';
 import 'package:video_player/video_player.dart';
 import 'package:video_player/video_player.dart';
 
 
 // ignore: must_be_immutable
 // ignore: must_be_immutable
 class VideoViewerPage extends HookConsumerWidget {
 class VideoViewerPage extends HookConsumerWidget {
   final String videoUrl;
   final String videoUrl;
-  final ImmichAsset asset;
-  ImmichAssetWithExif? assetDetail;
+  final AssetResponseDto asset;
+  AssetResponseDto? assetDetail;
 
 
   VideoViewerPage({Key? key, required this.videoUrl, required this.asset})
   VideoViewerPage({Key? key, required this.videoUrl, required this.asset})
       : super(key: key);
       : super(key: key);
@@ -49,10 +48,13 @@ class VideoViewerPage extends HookConsumerWidget {
           await ref.watch(assetServiceProvider).getAssetById(asset.id);
           await ref.watch(assetServiceProvider).getAssetById(asset.id);
     }
     }
 
 
-    useEffect(() {
-      getAssetExif();
-      return null;
-    }, []);
+    useEffect(
+      () {
+        getAssetExif();
+        return null;
+      },
+      [],
+    );
 
 
     return Scaffold(
     return Scaffold(
       backgroundColor: Colors.black,
       backgroundColor: Colors.black,
@@ -116,8 +118,10 @@ class _VideoThumbnailPlayerState extends State<VideoThumbnailPlayer> {
 
 
   Future<void> initializePlayer() async {
   Future<void> initializePlayer() async {
     try {
     try {
-      videoPlayerController = VideoPlayerController.network(widget.url,
-          httpHeaders: {"Authorization": "Bearer ${widget.jwtToken}"});
+      videoPlayerController = VideoPlayerController.network(
+        widget.url,
+        httpHeaders: {"Authorization": "Bearer ${widget.jwtToken}"},
+      );
 
 
       await videoPlayerController.initialize();
       await videoPlayerController.initialize();
       _createChewieController();
       _createChewieController();

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

@@ -1,10 +1,10 @@
 import 'package:cancellation_token_http/http.dart';
 import 'package:cancellation_token_http/http.dart';
 import 'package:collection/collection.dart';
 import 'package:collection/collection.dart';
+import 'package:openapi/api.dart';
 import 'package:photo_manager/photo_manager.dart';
 import 'package:photo_manager/photo_manager.dart';
 
 
 import 'package:immich_mobile/modules/backup/models/available_album.model.dart';
 import 'package:immich_mobile/modules/backup/models/available_album.model.dart';
 import 'package:immich_mobile/modules/backup/models/current_upload_asset.model.dart';
 import 'package:immich_mobile/modules/backup/models/current_upload_asset.model.dart';
-import 'package:immich_mobile/shared/models/server_info.model.dart';
 
 
 enum BackUpProgressEnum { idle, inProgress, done }
 enum BackUpProgressEnum { idle, inProgress, done }
 
 
@@ -14,7 +14,7 @@ class BackUpState {
   final List<String> allAssetsInDatabase;
   final List<String> allAssetsInDatabase;
   final double progressInPercentage;
   final double progressInPercentage;
   final CancellationToken cancelToken;
   final CancellationToken cancelToken;
-  final ServerInfo serverInfo;
+  final ServerInfoResponseDto serverInfo;
 
 
   /// All available albums on the device
   /// All available albums on the device
   final List<AvailableAlbum> availableAlbums;
   final List<AvailableAlbum> availableAlbums;
@@ -49,7 +49,7 @@ class BackUpState {
     List<String>? allAssetsInDatabase,
     List<String>? allAssetsInDatabase,
     double? progressInPercentage,
     double? progressInPercentage,
     CancellationToken? cancelToken,
     CancellationToken? cancelToken,
-    ServerInfo? serverInfo,
+    ServerInfoResponseDto? serverInfo,
     List<AvailableAlbum>? availableAlbums,
     List<AvailableAlbum>? availableAlbums,
     Set<AssetPathEntity>? selectedBackupAlbums,
     Set<AssetPathEntity>? selectedBackupAlbums,
     Set<AssetPathEntity>? excludedBackupAlbums,
     Set<AssetPathEntity>? excludedBackupAlbums,
@@ -93,8 +93,10 @@ class BackUpState {
         collectionEquals(other.selectedBackupAlbums, selectedBackupAlbums) &&
         collectionEquals(other.selectedBackupAlbums, selectedBackupAlbums) &&
         collectionEquals(other.excludedBackupAlbums, excludedBackupAlbums) &&
         collectionEquals(other.excludedBackupAlbums, excludedBackupAlbums) &&
         collectionEquals(other.allUniqueAssets, allUniqueAssets) &&
         collectionEquals(other.allUniqueAssets, allUniqueAssets) &&
-        collectionEquals(other.selectedAlbumsBackupAssetsIds,
-            selectedAlbumsBackupAssetsIds) &&
+        collectionEquals(
+          other.selectedAlbumsBackupAssetsIds,
+          selectedAlbumsBackupAssetsIds,
+        ) &&
         other.currentUploadAsset == currentUploadAsset;
         other.currentUploadAsset == currentUploadAsset;
   }
   }
 
 

+ 0 - 48
mobile/lib/modules/backup/models/check_duplicate_asset_response.model.dart

@@ -1,48 +0,0 @@
-import 'dart:convert';
-
-class CheckDuplicateAssetResponse {
-  final bool isExist;
-  CheckDuplicateAssetResponse({
-    required this.isExist,
-  });
-
-  CheckDuplicateAssetResponse copyWith({
-    bool? isExist,
-  }) {
-    return CheckDuplicateAssetResponse(
-      isExist: isExist ?? this.isExist,
-    );
-  }
-
-  Map<String, dynamic> toMap() {
-    final result = <String, dynamic>{};
-
-    result.addAll({'isExist': isExist});
-
-    return result;
-  }
-
-  factory CheckDuplicateAssetResponse.fromMap(Map<String, dynamic> map) {
-    return CheckDuplicateAssetResponse(
-      isExist: map['isExist'] ?? false,
-    );
-  }
-
-  String toJson() => json.encode(toMap());
-
-  factory CheckDuplicateAssetResponse.fromJson(String source) =>
-      CheckDuplicateAssetResponse.fromMap(json.decode(source));
-
-  @override
-  String toString() => 'CheckDuplicateAssetResponse(isExist: $isExist)';
-
-  @override
-  bool operator ==(Object other) {
-    if (identical(this, other)) return true;
-
-    return other is CheckDuplicateAssetResponse && other.isExist == isExist;
-  }
-
-  @override
-  int get hashCode => isExist.hashCode;
-}

+ 39 - 30
mobile/lib/modules/backup/providers/backup.provider.dart

@@ -12,8 +12,8 @@ import 'package:immich_mobile/modules/backup/providers/error_backup_list.provide
 import 'package:immich_mobile/modules/backup/services/backup.service.dart';
 import 'package:immich_mobile/modules/backup/services/backup.service.dart';
 import 'package:immich_mobile/modules/login/models/authentication_state.model.dart';
 import 'package:immich_mobile/modules/login/models/authentication_state.model.dart';
 import 'package:immich_mobile/modules/login/providers/authentication.provider.dart';
 import 'package:immich_mobile/modules/login/providers/authentication.provider.dart';
-import 'package:immich_mobile/shared/models/server_info.model.dart';
 import 'package:immich_mobile/shared/services/server_info.service.dart';
 import 'package:immich_mobile/shared/services/server_info.service.dart';
+import 'package:openapi/api.dart';
 import 'package:photo_manager/photo_manager.dart';
 import 'package:photo_manager/photo_manager.dart';
 
 
 class BackupNotifier extends StateNotifier<BackUpState> {
 class BackupNotifier extends StateNotifier<BackUpState> {
@@ -28,12 +28,12 @@ class BackupNotifier extends StateNotifier<BackUpState> {
             allAssetsInDatabase: const [],
             allAssetsInDatabase: const [],
             progressInPercentage: 0,
             progressInPercentage: 0,
             cancelToken: CancellationToken(),
             cancelToken: CancellationToken(),
-            serverInfo: ServerInfo(
+            serverInfo: ServerInfoResponseDto(
               diskAvailable: "0",
               diskAvailable: "0",
               diskAvailableRaw: 0,
               diskAvailableRaw: 0,
               diskSize: "0",
               diskSize: "0",
               diskSizeRaw: 0,
               diskSizeRaw: 0,
-              diskUsagePercentage: 0.0,
+              diskUsagePercentage: 0,
               diskUse: "0",
               diskUse: "0",
               diskUseRaw: 0,
               diskUseRaw: 0,
             ),
             ),
@@ -113,7 +113,9 @@ class BackupNotifier extends StateNotifier<BackUpState> {
     // 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(
-        hasAll: true, type: RequestType.common);
+      hasAll: true,
+      type: RequestType.common,
+    );
 
 
     for (AssetPathEntity album in albums) {
     for (AssetPathEntity album in albums) {
       AvailableAlbum availableAlbum = AvailableAlbum(albumEntity: album);
       AvailableAlbum availableAlbum = AvailableAlbum(albumEntity: album);
@@ -156,7 +158,10 @@ class BackupNotifier extends StateNotifier<BackUpState> {
 
 
       // Get album that contains all assets
       // Get album that contains all assets
       var list = await PhotoManager.getAssetPathList(
       var list = await PhotoManager.getAssetPathList(
-          hasAll: true, onlyAll: true, type: RequestType.common);
+        hasAll: true,
+        onlyAll: true,
+        type: RequestType.common,
+      );
       AssetPathEntity albumHasAllAssets = list.first;
       AssetPathEntity albumHasAllAssets = list.first;
 
 
       backupAlbumInfoBox.put(
       backupAlbumInfoBox.put(
@@ -175,13 +180,15 @@ class BackupNotifier extends StateNotifier<BackUpState> {
       for (var selectedAlbumId in backupAlbumInfo!.selectedAlbumIds) {
       for (var selectedAlbumId in backupAlbumInfo!.selectedAlbumIds) {
         var albumAsset = await AssetPathEntity.fromId(selectedAlbumId);
         var albumAsset = await AssetPathEntity.fromId(selectedAlbumId);
         state = state.copyWith(
         state = state.copyWith(
-            selectedBackupAlbums: {...state.selectedBackupAlbums, albumAsset});
+          selectedBackupAlbums: {...state.selectedBackupAlbums, albumAsset},
+        );
       }
       }
 
 
       for (var excludedAlbumId in backupAlbumInfo.excludedAlbumsIds) {
       for (var excludedAlbumId in backupAlbumInfo.excludedAlbumsIds) {
         var albumAsset = await AssetPathEntity.fromId(excludedAlbumId);
         var albumAsset = await AssetPathEntity.fromId(excludedAlbumId);
         state = state.copyWith(
         state = state.copyWith(
-            excludedBackupAlbums: {...state.excludedBackupAlbums, albumAsset});
+          excludedBackupAlbums: {...state.excludedBackupAlbums, albumAsset},
+        );
       }
       }
     } catch (e) {
     } catch (e) {
       debugPrint("[ERROR] Failed to generate album from id $e");
       debugPrint("[ERROR] Failed to generate album from id $e");
@@ -211,8 +218,11 @@ class BackupNotifier extends StateNotifier<BackUpState> {
 
 
     Set<AssetEntity> allUniqueAssets =
     Set<AssetEntity> allUniqueAssets =
         assetsFromSelectedAlbums.difference(assetsFromExcludedAlbums);
         assetsFromSelectedAlbums.difference(assetsFromExcludedAlbums);
-    List<String> allAssetsInDatabase =
-        await _backupService.getDeviceBackupAsset();
+    var allAssetsInDatabase = await _backupService.getDeviceBackupAsset();
+
+    if (allAssetsInDatabase == null) {
+      return;
+    }
 
 
     // Find asset that were backup from selected albums
     // Find asset that were backup from selected albums
     Set<String> selectedAlbumsBackupAssets =
     Set<String> selectedAlbumsBackupAssets =
@@ -328,23 +338,27 @@ class BackupNotifier extends StateNotifier<BackUpState> {
   void cancelBackup() {
   void cancelBackup() {
     state.cancelToken.cancel();
     state.cancelToken.cancel();
     state = state.copyWith(
     state = state.copyWith(
-        backupProgress: BackUpProgressEnum.idle, progressInPercentage: 0.0);
+      backupProgress: BackUpProgressEnum.idle,
+      progressInPercentage: 0.0,
+    );
   }
   }
 
 
   void _onAssetUploaded(String deviceAssetId, String deviceId) {
   void _onAssetUploaded(String deviceAssetId, String deviceId) {
-    state = state.copyWith(selectedAlbumsBackupAssetsIds: {
-      ...state.selectedAlbumsBackupAssetsIds,
-      deviceAssetId
-    }, allAssetsInDatabase: [
-      ...state.allAssetsInDatabase,
-      deviceAssetId
-    ]);
+    state = state.copyWith(
+      selectedAlbumsBackupAssetsIds: {
+        ...state.selectedAlbumsBackupAssetsIds,
+        deviceAssetId
+      },
+      allAssetsInDatabase: [...state.allAssetsInDatabase, deviceAssetId],
+    );
 
 
     if (state.allUniqueAssets.length -
     if (state.allUniqueAssets.length -
             state.selectedAlbumsBackupAssetsIds.length ==
             state.selectedAlbumsBackupAssetsIds.length ==
         0) {
         0) {
       state = state.copyWith(
       state = state.copyWith(
-          backupProgress: BackUpProgressEnum.done, progressInPercentage: 0.0);
+        backupProgress: BackUpProgressEnum.done,
+        progressInPercentage: 0.0,
+      );
     }
     }
 
 
     _updateServerInfo();
     _updateServerInfo();
@@ -352,24 +366,19 @@ class BackupNotifier extends StateNotifier<BackUpState> {
 
 
   void _onUploadProgress(int sent, int total) {
   void _onUploadProgress(int sent, int total) {
     state = state.copyWith(
     state = state.copyWith(
-        progressInPercentage: (sent.toDouble() / total.toDouble() * 100));
+      progressInPercentage: (sent.toDouble() / total.toDouble() * 100),
+    );
   }
   }
 
 
   Future<void> _updateServerInfo() async {
   Future<void> _updateServerInfo() async {
     var serverInfo = await _serverInfoService.getServerInfo();
     var serverInfo = await _serverInfoService.getServerInfo();
 
 
     // Update server info
     // Update server info
-    state = state.copyWith(
-      serverInfo: ServerInfo(
-        diskSize: serverInfo.diskSize,
-        diskUse: serverInfo.diskUse,
-        diskAvailable: serverInfo.diskAvailable,
-        diskSizeRaw: serverInfo.diskSizeRaw,
-        diskUseRaw: serverInfo.diskUseRaw,
-        diskAvailableRaw: serverInfo.diskAvailableRaw,
-        diskUsagePercentage: serverInfo.diskUsagePercentage,
-      ),
-    );
+    if (serverInfo != null) {
+      state = state.copyWith(
+        serverInfo: serverInfo,
+      );
+    }
   }
   }
 
 
   void resumeBackup() {
   void resumeBackup() {

+ 53 - 57
mobile/lib/modules/backup/services/backup.service.dart

@@ -2,59 +2,38 @@ import 'dart:async';
 import 'dart:convert';
 import 'dart:convert';
 import 'dart:io';
 import 'dart:io';
 
 
-import 'package:dio/dio.dart';
 import 'package:flutter/material.dart';
 import 'package:flutter/material.dart';
 import 'package:hive/hive.dart';
 import 'package:hive/hive.dart';
 import 'package:hooks_riverpod/hooks_riverpod.dart';
 import 'package:hooks_riverpod/hooks_riverpod.dart';
 import 'package:immich_mobile/constants/hive_box.dart';
 import 'package:immich_mobile/constants/hive_box.dart';
-import 'package:immich_mobile/modules/backup/models/check_duplicate_asset_response.model.dart';
 import 'package:immich_mobile/modules/backup/models/current_upload_asset.model.dart';
 import 'package:immich_mobile/modules/backup/models/current_upload_asset.model.dart';
 import 'package:immich_mobile/modules/backup/models/error_upload_asset.model.dart';
 import 'package:immich_mobile/modules/backup/models/error_upload_asset.model.dart';
-import 'package:immich_mobile/shared/services/network.service.dart';
-import 'package:immich_mobile/shared/models/device_info.model.dart';
+import 'package:immich_mobile/shared/services/api.service.dart';
 import 'package:immich_mobile/utils/files_helper.dart';
 import 'package:immich_mobile/utils/files_helper.dart';
+import 'package:openapi/api.dart';
 import 'package:photo_manager/photo_manager.dart';
 import 'package:photo_manager/photo_manager.dart';
 import 'package:http_parser/http_parser.dart';
 import 'package:http_parser/http_parser.dart';
 import 'package:path/path.dart' as p;
 import 'package:path/path.dart' as p;
 import 'package:cancellation_token_http/http.dart' as http;
 import 'package:cancellation_token_http/http.dart' as http;
 
 
-final backupServiceProvider =
-    Provider((ref) => BackupService(ref.watch(networkServiceProvider)));
+final backupServiceProvider = Provider(
+  (ref) => BackupService(
+    ref.watch(apiServiceProvider),
+  ),
+);
 
 
 class BackupService {
 class BackupService {
-  final NetworkService _networkService;
+  final ApiService _apiService;
+  BackupService(this._apiService);
 
 
-  BackupService(this._networkService);
-
-  Future<List<String>> getDeviceBackupAsset() async {
-    String deviceId = Hive.box(userInfoBox).get(deviceIdKey);
-
-    Response response =
-        await _networkService.getRequest(url: "asset/$deviceId");
-    List<dynamic> result = jsonDecode(response.toString());
-
-    return result.cast<String>();
-  }
-
-  Future<bool> checkDuplicateAsset(String deviceAssetId) async {
+  Future<List<String>?> getDeviceBackupAsset() async {
     String deviceId = Hive.box(userInfoBox).get(deviceIdKey);
     String deviceId = Hive.box(userInfoBox).get(deviceIdKey);
 
 
     try {
     try {
-      Response response =
-          await _networkService.postRequest(url: "asset/check", data: {
-        "deviceId": deviceId,
-        "deviceAssetId": deviceAssetId,
-      });
-
-      if (response.statusCode == 200) {
-        var result = CheckDuplicateAssetResponse.fromJson(response.toString());
-
-        return result.isExist;
-      } else {
-        return false;
-      }
+      return await _apiService.assetApi.getUserAssetsByDeviceId(deviceId);
     } catch (e) {
     } catch (e) {
-      return false;
+      debugPrint('Error [getDeviceBackupAsset] ${e.toString()}');
+      return null;
     }
     }
   }
   }
 
 
@@ -99,9 +78,11 @@ class BackupService {
           var box = Hive.box(userInfoBox);
           var box = Hive.box(userInfoBox);
 
 
           var req = MultipartRequest(
           var req = MultipartRequest(
-              'POST', Uri.parse('$savedEndpoint/asset/upload'),
-              onProgress: ((bytes, totalBytes) =>
-                  uploadProgressCb(bytes, totalBytes)));
+            'POST',
+            Uri.parse('$savedEndpoint/asset/upload'),
+            onProgress: ((bytes, totalBytes) =>
+                uploadProgressCb(bytes, totalBytes)),
+          );
           req.headers["Authorization"] = "Bearer ${box.get(accessTokenKey)}";
           req.headers["Authorization"] = "Bearer ${box.get(accessTokenKey)}";
 
 
           req.fields['deviceAssetId'] = entity.id;
           req.fields['deviceAssetId'] = entity.id;
@@ -133,16 +114,19 @@ class BackupService {
             var error = jsonDecode(data);
             var error = jsonDecode(data);
 
 
             debugPrint(
             debugPrint(
-                "Error(${error['statusCode']}) uploading ${entity.id} | $originalFileName | Created on ${entity.createDateTime} | ${error['error']}");
-
-            errorCb(ErrorUploadAsset(
-              asset: entity,
-              id: entity.id,
-              createdAt: entity.createDateTime,
-              fileName: originalFileName,
-              fileType: _getAssetType(entity.type),
-              errorMessage: error['error'],
-            ));
+              "Error(${error['statusCode']}) uploading ${entity.id} | $originalFileName | Created on ${entity.createDateTime} | ${error['error']}",
+            );
+
+            errorCb(
+              ErrorUploadAsset(
+                asset: entity,
+                id: entity.id,
+                createdAt: entity.createDateTime,
+                fileName: originalFileName,
+                fileType: _getAssetType(entity.type),
+                errorMessage: error['error'],
+              ),
+            );
             continue;
             continue;
           }
           }
         }
         }
@@ -160,8 +144,6 @@ class BackupService {
     }
     }
   }
   }
 
 
-  void sendBackupRequest(AssetEntity entity) {}
-
   String _getAssetType(AssetType assetType) {
   String _getAssetType(AssetType assetType) {
     switch (assetType) {
     switch (assetType) {
       case AssetType.audio:
       case AssetType.audio:
@@ -175,15 +157,29 @@ class BackupService {
     }
     }
   }
   }
 
 
-  Future<DeviceInfoRemote> setAutoBackup(
-      bool status, String deviceId, String deviceType) async {
-    var res = await _networkService.patchRequest(url: 'device-info', data: {
-      "isAutoBackup": status,
-      "deviceId": deviceId,
-      "deviceType": deviceType,
-    });
+  Future<DeviceInfoResponseDto> setAutoBackup(
+    bool status,
+    String deviceId,
+    DeviceTypeEnum deviceType,
+  ) async {
+    try {
+      var updatedDeviceInfo = await _apiService.deviceInfoApi.updateDeviceInfo(
+        UpdateDeviceInfoDto(
+          deviceId: deviceId,
+          deviceType: deviceType,
+          isAutoBackup: status,
+        ),
+      );
+
+      if (updatedDeviceInfo == null) {
+        throw Exception("Error updating device info");
+      }
 
 
-    return DeviceInfoRemote.fromJson(res.toString());
+      return updatedDeviceInfo;
+    } catch (e) {
+      debugPrint("Error setAutoBackup: ${e.toString()}");
+      throw Error();
+    }
   }
   }
 }
 }
 
 

+ 23 - 11
mobile/lib/modules/backup/ui/album_info_card.dart

@@ -26,7 +26,9 @@ class AlbumInfoCard extends HookConsumerWidget {
         ref.watch(backupProvider).excludedBackupAlbums.contains(albumInfo);
         ref.watch(backupProvider).excludedBackupAlbums.contains(albumInfo);
 
 
     ColorFilter selectedFilter = ColorFilter.mode(
     ColorFilter selectedFilter = ColorFilter.mode(
-        Theme.of(context).primaryColor.withAlpha(100), BlendMode.darken);
+      Theme.of(context).primaryColor.withAlpha(100),
+      BlendMode.darken,
+    );
     ColorFilter excludedFilter =
     ColorFilter excludedFilter =
         ColorFilter.mode(Colors.red.withAlpha(75), BlendMode.darken);
         ColorFilter.mode(Colors.red.withAlpha(75), BlendMode.darken);
     ColorFilter unselectedFilter =
     ColorFilter unselectedFilter =
@@ -40,7 +42,10 @@ class AlbumInfoCard extends HookConsumerWidget {
           label: const Text(
           label: const Text(
             "album_info_card_backup_album_included",
             "album_info_card_backup_album_included",
             style: TextStyle(
             style: TextStyle(
-                fontSize: 10, color: Colors.white, fontWeight: FontWeight.bold),
+              fontSize: 10,
+              color: Colors.white,
+              fontWeight: FontWeight.bold,
+            ),
           ).tr(),
           ).tr(),
           backgroundColor: Theme.of(context).primaryColor,
           backgroundColor: Theme.of(context).primaryColor,
         );
         );
@@ -51,7 +56,10 @@ class AlbumInfoCard extends HookConsumerWidget {
           label: const Text(
           label: const Text(
             "album_info_card_backup_album_excluded",
             "album_info_card_backup_album_excluded",
             style: TextStyle(
             style: TextStyle(
-                fontSize: 10, color: Colors.white, fontWeight: FontWeight.bold),
+              fontSize: 10,
+              color: Colors.white,
+              fontWeight: FontWeight.bold,
+            ),
           ).tr(),
           ).tr(),
           backgroundColor: Colors.red[300],
           backgroundColor: Colors.red[300],
         );
         );
@@ -138,15 +146,16 @@ class AlbumInfoCard extends HookConsumerWidget {
                   height: 200,
                   height: 200,
                   decoration: BoxDecoration(
                   decoration: BoxDecoration(
                     borderRadius: const BorderRadius.only(
                     borderRadius: const BorderRadius.only(
-                        topLeft: Radius.circular(12),
-                        topRight: Radius.circular(12)),
+                      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(
                           : const AssetImage(
-                                  'assets/immich-logo-no-outline.png')
-                              as ImageProvider,
+                              'assets/immich-logo-no-outline.png',
+                            ) as ImageProvider,
                       fit: BoxFit.cover,
                       fit: BoxFit.cover,
                     ),
                     ),
                   ),
                   ),
@@ -174,9 +183,10 @@ 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),
@@ -186,7 +196,9 @@ class AlbumInfoCard extends HookConsumerWidget {
                                       ? " (${'backup_all'.tr()})"
                                       ? " (${'backup_all'.tr()})"
                                       : ""),
                                       : ""),
                               style: TextStyle(
                               style: TextStyle(
-                                  fontSize: 12, color: Colors.grey[600]),
+                                fontSize: 12,
+                                color: Colors.grey[600],
+                              ),
                             ),
                             ),
                           )
                           )
                         ],
                         ],

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

@@ -5,12 +5,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) {

+ 16 - 10
mobile/lib/modules/backup/views/album_preview_page.dart

@@ -20,10 +20,13 @@ class AlbumPreviewPage extends HookConsumerWidget {
           await album.getAssetListRange(start: 0, end: album.assetCount);
           await album.getAssetListRange(start: 0, end: album.assetCount);
     }
     }
 
 
-    useEffect(() {
-      _getAssetsInAlbum();
-      return null;
-    }, []);
+    useEffect(
+      () {
+        _getAssetsInAlbum();
+        return null;
+      },
+      [],
+    );
 
 
     return Scaffold(
     return Scaffold(
       appBar: AppBar(
       appBar: AppBar(
@@ -39,9 +42,10 @@ class AlbumPreviewPage extends HookConsumerWidget {
               child: Text(
               child: Text(
                 "ID ${album.id}",
                 "ID ${album.id}",
                 style: TextStyle(
                 style: TextStyle(
-                    fontSize: 10,
-                    color: Colors.grey[600],
-                    fontWeight: FontWeight.bold),
+                  fontSize: 10,
+                  color: Colors.grey[600],
+                  fontWeight: FontWeight.bold,
+                ),
               ),
               ),
             ),
             ),
           ],
           ],
@@ -59,9 +63,11 @@ 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,

+ 31 - 19
mobile/lib/modules/backup/views/backup_album_selection_page.dart

@@ -17,10 +17,13 @@ class BackupAlbumSelectionPage extends HookConsumerWidget {
     final selectedBackupAlbums = ref.watch(backupProvider).selectedBackupAlbums;
     final selectedBackupAlbums = ref.watch(backupProvider).selectedBackupAlbums;
     final excludedBackupAlbums = ref.watch(backupProvider).excludedBackupAlbums;
     final excludedBackupAlbums = ref.watch(backupProvider).excludedBackupAlbums;
 
 
-    useEffect(() {
-      ref.read(backupProvider.notifier).getBackupInfo();
-      return null;
-    }, []);
+    useEffect(
+      () {
+        ref.read(backupProvider.notifier).getBackupInfo();
+        return null;
+      },
+      [],
+    );
 
 
     _buildAlbumSelectionList() {
     _buildAlbumSelectionList() {
       if (availableAlbums.isEmpty) {
       if (availableAlbums.isEmpty) {
@@ -42,8 +45,9 @@ class BackupAlbumSelectionPage extends HookConsumerWidget {
                   ? const EdgeInsets.only(left: 16.00)
                   ? const EdgeInsets.only(left: 16.00)
                   : const EdgeInsets.all(0),
                   : const EdgeInsets.all(0),
               child: AlbumInfoCard(
               child: AlbumInfoCard(
-                  imageData: thumbnailData,
-                  albumInfo: availableAlbums[index].albumEntity),
+                imageData: thumbnailData,
+                albumInfo: availableAlbums[index].albumEntity,
+              ),
             );
             );
           }),
           }),
         ),
         ),
@@ -73,13 +77,15 @@ class BackupAlbumSelectionPage extends HookConsumerWidget {
             child: Chip(
             child: Chip(
               visualDensity: VisualDensity.compact,
               visualDensity: VisualDensity.compact,
               shape: RoundedRectangleBorder(
               shape: RoundedRectangleBorder(
-                  borderRadius: BorderRadius.circular(5)),
+                borderRadius: BorderRadius.circular(5),
+              ),
               label: Text(
               label: Text(
                 album.name,
                 album.name,
                 style: const TextStyle(
                 style: const TextStyle(
-                    fontSize: 10,
-                    color: Colors.white,
-                    fontWeight: FontWeight.bold),
+                  fontSize: 10,
+                  color: Colors.white,
+                  fontWeight: FontWeight.bold,
+                ),
               ),
               ),
               backgroundColor: Theme.of(context).primaryColor,
               backgroundColor: Theme.of(context).primaryColor,
               deleteIconColor: Colors.white,
               deleteIconColor: Colors.white,
@@ -109,13 +115,15 @@ class BackupAlbumSelectionPage extends HookConsumerWidget {
             child: Chip(
             child: Chip(
               visualDensity: VisualDensity.compact,
               visualDensity: VisualDensity.compact,
               shape: RoundedRectangleBorder(
               shape: RoundedRectangleBorder(
-                  borderRadius: BorderRadius.circular(5)),
+                borderRadius: BorderRadius.circular(5),
+              ),
               label: Text(
               label: Text(
                 album.name,
                 album.name,
                 style: const TextStyle(
                 style: const TextStyle(
-                    fontSize: 10,
-                    color: Colors.white,
-                    fontWeight: FontWeight.bold),
+                  fontSize: 10,
+                  color: Colors.white,
+                  fontWeight: FontWeight.bold,
+                ),
               ),
               ),
               backgroundColor: Colors.red[300],
               backgroundColor: Colors.red[300],
               deleteIconColor: Colors.white,
               deleteIconColor: Colors.white,
@@ -185,9 +193,10 @@ class BackupAlbumSelectionPage extends HookConsumerWidget {
                     title: Text(
                     title: Text(
                       "backup_album_selection_page_total_assets",
                       "backup_album_selection_page_total_assets",
                       style: TextStyle(
                       style: TextStyle(
-                          fontWeight: FontWeight.bold,
-                          fontSize: 14,
-                          color: Colors.grey[700]),
+                        fontWeight: FontWeight.bold,
+                        fontSize: 14,
+                        color: Colors.grey[700],
+                      ),
                     ).tr(),
                     ).tr(),
                     trailing: Text(
                     trailing: Text(
                       ref
                       ref
@@ -234,7 +243,8 @@ class BackupAlbumSelectionPage extends HookConsumerWidget {
                   builder: (BuildContext context) {
                   builder: (BuildContext context) {
                     return AlertDialog(
                     return AlertDialog(
                       shape: RoundedRectangleBorder(
                       shape: RoundedRectangleBorder(
-                          borderRadius: BorderRadius.circular(12)),
+                        borderRadius: BorderRadius.circular(12),
+                      ),
                       elevation: 5,
                       elevation: 5,
                       title: Text(
                       title: Text(
                         'backup_album_selection_page_selection_info',
                         'backup_album_selection_page_selection_info',
@@ -250,7 +260,9 @@ class BackupAlbumSelectionPage extends HookConsumerWidget {
                             Text(
                             Text(
                               'backup_album_selection_page_assets_scatter',
                               'backup_album_selection_page_assets_scatter',
                               style: TextStyle(
                               style: TextStyle(
-                                  fontSize: 14, color: Colors.grey[700]),
+                                fontSize: 14,
+                                color: Colors.grey[700],
+                              ),
                             ).tr(),
                             ).tr(),
                           ],
                           ],
                         ),
                         ),

+ 73 - 55
mobile/lib/modules/backup/views/backup_controller_page.dart

@@ -11,7 +11,6 @@ import 'package:immich_mobile/modules/backup/providers/backup.provider.dart';
 import 'package:immich_mobile/routing/router.dart';
 import 'package:immich_mobile/routing/router.dart';
 import 'package:immich_mobile/shared/providers/websocket.provider.dart';
 import 'package:immich_mobile/shared/providers/websocket.provider.dart';
 import 'package:immich_mobile/modules/backup/ui/backup_info_card.dart';
 import 'package:immich_mobile/modules/backup/ui/backup_info_card.dart';
-import 'package:intl/intl.dart';
 import 'package:percent_indicator/linear_percent_indicator.dart';
 import 'package:percent_indicator/linear_percent_indicator.dart';
 
 
 class BackupControllerPage extends HookConsumerWidget {
 class BackupControllerPage extends HookConsumerWidget {
@@ -27,16 +26,19 @@ class BackupControllerPage extends HookConsumerWidget {
         ? false
         ? false
         : true;
         : true;
 
 
-    useEffect(() {
-      if (backupState.backupProgress != BackUpProgressEnum.inProgress) {
-        ref.watch(backupProvider.notifier).getBackupInfo();
-      }
+    useEffect(
+      () {
+        if (backupState.backupProgress != BackUpProgressEnum.inProgress) {
+          ref.watch(backupProvider.notifier).getBackupInfo();
+        }
 
 
-      ref
-          .watch(websocketProvider.notifier)
-          .stopListenToEvent('on_upload_success');
-      return null;
-    }, []);
+        ref
+            .watch(websocketProvider.notifier)
+            .stopListenToEvent('on_upload_success');
+        return null;
+      },
+      [],
+    );
 
 
     Widget _buildStorageInformation() {
     Widget _buildStorageInformation() {
       return ListTile(
       return ListTile(
@@ -68,10 +70,11 @@ class BackupControllerPage extends HookConsumerWidget {
               Padding(
               Padding(
                 padding: const EdgeInsets.only(top: 12.0),
                 padding: const EdgeInsets.only(top: 12.0),
                 child: const Text('backup_controller_page_storage_format').tr(
                 child: const Text('backup_controller_page_storage_format').tr(
-                    args: [
-                      backupState.serverInfo.diskUse,
-                      backupState.serverInfo.diskSize
-                    ]),
+                  args: [
+                    backupState.serverInfo.diskUse,
+                    backupState.serverInfo.diskSize
+                  ],
+                ),
               ),
               ),
             ],
             ],
           ),
           ),
@@ -129,8 +132,10 @@ class BackupControllerPage extends HookConsumerWidget {
                           .setAutoBackup(true);
                           .setAutoBackup(true);
                     }
                     }
                   },
                   },
-                  child: Text(backupBtnText,
-                      style: const TextStyle(fontWeight: FontWeight.bold)),
+                  child: Text(
+                    backupBtnText,
+                    style: const TextStyle(fontWeight: FontWeight.bold),
+                  ),
                 ),
                 ),
               )
               )
             ],
             ],
@@ -157,9 +162,10 @@ class BackupControllerPage extends HookConsumerWidget {
           child: Text(
           child: Text(
             text.trim().substring(0, text.length - 2),
             text.trim().substring(0, text.length - 2),
             style: TextStyle(
             style: TextStyle(
-                color: Theme.of(context).primaryColor,
-                fontSize: 12,
-                fontWeight: FontWeight.bold),
+              color: Theme.of(context).primaryColor,
+              fontSize: 12,
+              fontWeight: FontWeight.bold,
+            ),
           ),
           ),
         );
         );
       } else {
       } else {
@@ -168,9 +174,10 @@ class BackupControllerPage extends HookConsumerWidget {
           child: Text(
           child: Text(
             "backup_controller_page_none_selected".tr(),
             "backup_controller_page_none_selected".tr(),
             style: TextStyle(
             style: TextStyle(
-                color: Theme.of(context).primaryColor,
-                fontSize: 12,
-                fontWeight: FontWeight.bold),
+              color: Theme.of(context).primaryColor,
+              fontSize: 12,
+              fontWeight: FontWeight.bold,
+            ),
           ),
           ),
         );
         );
       }
       }
@@ -190,9 +197,10 @@ class BackupControllerPage extends HookConsumerWidget {
           child: Text(
           child: Text(
             text.trim().substring(0, text.length - 2),
             text.trim().substring(0, text.length - 2),
             style: TextStyle(
             style: TextStyle(
-                color: Colors.red[300],
-                fontSize: 12,
-                fontWeight: FontWeight.bold),
+              color: Colors.red[300],
+              fontSize: 12,
+              fontWeight: FontWeight.bold,
+            ),
           ),
           ),
         );
         );
       } else {
       } else {
@@ -213,9 +221,10 @@ class BackupControllerPage extends HookConsumerWidget {
         borderOnForeground: false,
         borderOnForeground: false,
         child: ListTile(
         child: ListTile(
           minVerticalPadding: 15,
           minVerticalPadding: 15,
-          title: const Text("backup_controller_page_albums",
-                  style: TextStyle(fontWeight: FontWeight.bold, fontSize: 20))
-              .tr(),
+          title: const Text(
+            "backup_controller_page_albums",
+            style: TextStyle(fontWeight: FontWeight.bold, fontSize: 20),
+          ).tr(),
           subtitle: Padding(
           subtitle: Padding(
             padding: const EdgeInsets.only(top: 8.0),
             padding: const EdgeInsets.only(top: 8.0),
             child: Column(
             child: Column(
@@ -284,9 +293,9 @@ class BackupControllerPage extends HookConsumerWidget {
                     fontWeight: FontWeight.bold,
                     fontWeight: FontWeight.bold,
                     fontSize: 11,
                     fontSize: 11,
                   ),
                   ),
-                ).tr(args: [
-                  ref.watch(errorBackupListProvider).length.toString()
-                ]),
+                ).tr(
+                  args: [ref.watch(errorBackupListProvider).length.toString()],
+                ),
                 backgroundColor: Colors.white,
                 backgroundColor: Colors.white,
                 onPressed: () {
                 onPressed: () {
                   AutoRouter.of(context).push(const FailedBackupStatusRoute());
                   AutoRouter.of(context).push(const FailedBackupStatusRoute());
@@ -331,12 +340,16 @@ class BackupControllerPage extends HookConsumerWidget {
                           child: const Text(
                           child: const Text(
                             'backup_controller_page_filename',
                             'backup_controller_page_filename',
                             style: TextStyle(
                             style: TextStyle(
-                                fontWeight: FontWeight.bold, fontSize: 10.0),
-                          ).tr(args: [
-                            backupState.currentUploadAsset.fileName,
-                            backupState.currentUploadAsset.fileType
-                                .toLowerCase()
-                          ]),
+                              fontWeight: FontWeight.bold,
+                              fontSize: 10.0,
+                            ),
+                          ).tr(
+                            args: [
+                              backupState.currentUploadAsset.fileName,
+                              backupState.currentUploadAsset.fileType
+                                  .toLowerCase()
+                            ],
+                          ),
                         ),
                         ),
                       ),
                       ),
                     ],
                     ],
@@ -352,16 +365,20 @@ class BackupControllerPage extends HookConsumerWidget {
                           padding: const EdgeInsets.all(6.0),
                           padding: const EdgeInsets.all(6.0),
                           child: const Text(
                           child: const Text(
                             "backup_controller_page_created",
                             "backup_controller_page_created",
-                            style: const TextStyle(
-                                fontWeight: FontWeight.bold, fontSize: 10.0),
-                          ).tr(args: [
-                            DateFormat.yMMMMd('en_US').format(
-                              DateTime.parse(
-                                backupState.currentUploadAsset.createdAt
-                                    .toString(),
-                              ),
-                            )
-                          ]),
+                            style: TextStyle(
+                              fontWeight: FontWeight.bold,
+                              fontSize: 10.0,
+                            ),
+                          ).tr(
+                            args: [
+                              DateFormat.yMMMMd('en_US').format(
+                                DateTime.parse(
+                                  backupState.currentUploadAsset.createdAt
+                                      .toString(),
+                                ),
+                              )
+                            ],
+                          ),
                         ),
                         ),
                       ),
                       ),
                     ],
                     ],
@@ -406,14 +423,15 @@ class BackupControllerPage extends HookConsumerWidget {
           style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold),
           style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold),
         ).tr(),
         ).tr(),
         leading: IconButton(
         leading: IconButton(
-            onPressed: () {
-              ref.watch(websocketProvider.notifier).listenUploadEvent();
-              AutoRouter.of(context).pop(true);
-            },
-            splashRadius: 24,
-            icon: const Icon(
-              Icons.arrow_back_ios_rounded,
-            )),
+          onPressed: () {
+            ref.watch(websocketProvider.notifier).listenUploadEvent();
+            AutoRouter.of(context).pop(true);
+          },
+          splashRadius: 24,
+          icon: const Icon(
+            Icons.arrow_back_ios_rounded,
+          ),
+        ),
       ),
       ),
       body: Padding(
       body: Padding(
         padding: const EdgeInsets.only(left: 16.0, right: 16, bottom: 32),
         padding: const EdgeInsets.only(left: 16.0, right: 16, bottom: 32),

+ 12 - 10
mobile/lib/modules/backup/views/failed_backup_status_page.dart

@@ -19,13 +19,14 @@ class FailedBackupStatusPage extends HookConsumerWidget {
           style: const TextStyle(fontSize: 16, fontWeight: FontWeight.bold),
           style: const TextStyle(fontSize: 16, fontWeight: FontWeight.bold),
         ),
         ),
         leading: IconButton(
         leading: IconButton(
-            onPressed: () {
-              AutoRouter.of(context).pop(true);
-            },
-            splashRadius: 24,
-            icon: const Icon(
-              Icons.arrow_back_ios_rounded,
-            )),
+          onPressed: () {
+            AutoRouter.of(context).pop(true);
+          },
+          splashRadius: 24,
+          icon: const Icon(
+            Icons.arrow_back_ios_rounded,
+          ),
+        ),
       ),
       ),
       body: ListView.builder(
       body: ListView.builder(
         shrinkWrap: true,
         shrinkWrap: true,
@@ -92,9 +93,10 @@ class FailedBackupStatusPage extends HookConsumerWidget {
                                   ),
                                   ),
                                 ),
                                 ),
                                 style: TextStyle(
                                 style: TextStyle(
-                                    fontSize: 12,
-                                    fontWeight: FontWeight.w600,
-                                    color: Colors.grey[700]),
+                                  fontSize: 12,
+                                  fontWeight: FontWeight.w600,
+                                  color: Colors.grey[700],
+                                ),
                               ),
                               ),
                               Icon(
                               Icon(
                                 Icons.error,
                                 Icons.error,

+ 0 - 55
mobile/lib/modules/home/models/delete_asset_response.model.dart

@@ -1,55 +0,0 @@
-import 'dart:convert';
-
-class DeleteAssetResponse {
-  final String id;
-  final String status;
-
-  DeleteAssetResponse({
-    required this.id,
-    required this.status,
-  });
-
-  DeleteAssetResponse copyWith({
-    String? id,
-    String? status,
-  }) {
-    return DeleteAssetResponse(
-      id: id ?? this.id,
-      status: status ?? this.status,
-    );
-  }
-
-  Map<String, dynamic> toMap() {
-    return {
-      'id': id,
-      'status': status,
-    };
-  }
-
-  factory DeleteAssetResponse.fromMap(Map<String, dynamic> map) {
-    return DeleteAssetResponse(
-      id: map['id'] ?? '',
-      status: map['status'] ?? '',
-    );
-  }
-
-  String toJson() => json.encode(toMap());
-
-  factory DeleteAssetResponse.fromJson(String source) =>
-      DeleteAssetResponse.fromMap(json.decode(source));
-
-  @override
-  String toString() => 'DeleteAssetResponse(id: $id, status: $status)';
-
-  @override
-  bool operator ==(Object other) {
-    if (identical(this, other)) return true;
-
-    return other is DeleteAssetResponse &&
-        other.id == id &&
-        other.status == status;
-  }
-
-  @override
-  int get hashCode => id.hashCode ^ status.hashCode;
-}

+ 3 - 47
mobile/lib/modules/home/models/get_all_asset_response.model.dart

@@ -1,11 +1,9 @@
-import 'dart:convert';
-
 import 'package:flutter/foundation.dart';
 import 'package:flutter/foundation.dart';
-import 'package:immich_mobile/shared/models/immich_asset.model.dart';
+import 'package:openapi/api.dart';
 
 
 class ImmichAssetGroupByDate {
 class ImmichAssetGroupByDate {
   final String date;
   final String date;
-  List<ImmichAsset> assets;
+  List<AssetResponseDto> assets;
   ImmichAssetGroupByDate({
   ImmichAssetGroupByDate({
     required this.date,
     required this.date,
     required this.assets,
     required this.assets,
@@ -13,7 +11,7 @@ class ImmichAssetGroupByDate {
 
 
   ImmichAssetGroupByDate copyWith({
   ImmichAssetGroupByDate copyWith({
     String? date,
     String? date,
-    List<ImmichAsset>? assets,
+    List<AssetResponseDto>? assets,
   }) {
   }) {
     return ImmichAssetGroupByDate(
     return ImmichAssetGroupByDate(
       date: date ?? this.date,
       date: date ?? this.date,
@@ -21,26 +19,6 @@ class ImmichAssetGroupByDate {
     );
     );
   }
   }
 
 
-  Map<String, dynamic> toMap() {
-    return {
-      'date': date,
-      'assets': assets.map((x) => x.toMap()).toList(),
-    };
-  }
-
-  factory ImmichAssetGroupByDate.fromMap(Map<String, dynamic> map) {
-    return ImmichAssetGroupByDate(
-      date: map['date'] ?? '',
-      assets: List<ImmichAsset>.from(
-          map['assets']?.map((x) => ImmichAsset.fromMap(x))),
-    );
-  }
-
-  String toJson() => json.encode(toMap());
-
-  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)';
 
 
@@ -79,28 +57,6 @@ class GetAllAssetResponse {
     );
     );
   }
   }
 
 
-  Map<String, dynamic> toMap() {
-    return {
-      'count': count,
-      'data': data.map((x) => x.toMap()).toList(),
-      'nextPageKey': nextPageKey,
-    };
-  }
-
-  factory GetAllAssetResponse.fromMap(Map<String, dynamic> map) {
-    return GetAllAssetResponse(
-      count: map['count']?.toInt() ?? 0,
-      data: List<ImmichAssetGroupByDate>.from(
-          map['data']?.map((x) => ImmichAssetGroupByDate.fromMap(x))),
-      nextPageKey: map['nextPageKey'] ?? '',
-    );
-  }
-
-  String toJson() => json.encode(toMap());
-
-  factory GetAllAssetResponse.fromJson(String source) =>
-      GetAllAssetResponse.fromMap(json.decode(source));
-
   @override
   @override
   String toString() =>
   String toString() =>
       'GetAllAssetResponse(count: $count, data: $data, nextPageKey: $nextPageKey)';
       'GetAllAssetResponse(count: $count, data: $data, nextPageKey: $nextPageKey)';

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

@@ -1,12 +1,10 @@
-import 'dart:convert';
-
 import 'package:collection/collection.dart';
 import 'package:collection/collection.dart';
 
 
-import 'package:immich_mobile/shared/models/immich_asset.model.dart';
+import 'package:openapi/api.dart';
 
 
 class HomePageState {
 class HomePageState {
   final bool isMultiSelectEnable;
   final bool isMultiSelectEnable;
-  final Set<ImmichAsset> selectedItems;
+  final Set<AssetResponseDto> selectedItems;
   final Set<String> selectedDateGroup;
   final Set<String> selectedDateGroup;
   HomePageState({
   HomePageState({
     required this.isMultiSelectEnable,
     required this.isMultiSelectEnable,
@@ -16,7 +14,7 @@ class HomePageState {
 
 
   HomePageState copyWith({
   HomePageState copyWith({
     bool? isMultiSelectEnable,
     bool? isMultiSelectEnable,
-    Set<ImmichAsset>? selectedItems,
+    Set<AssetResponseDto>? selectedItems,
     Set<String>? selectedDateGroup,
     Set<String>? selectedDateGroup,
   }) {
   }) {
     return HomePageState(
     return HomePageState(
@@ -26,28 +24,6 @@ class HomePageState {
     );
     );
   }
   }
 
 
-  Map<String, dynamic> toMap() {
-    return {
-      'isMultiSelectEnable': isMultiSelectEnable,
-      'selectedItems': selectedItems.map((x) => x.toMap()).toList(),
-      'selectedDateGroup': selectedDateGroup.toList(),
-    };
-  }
-
-  factory HomePageState.fromMap(Map<String, dynamic> map) {
-    return HomePageState(
-      isMultiSelectEnable: map['isMultiSelectEnable'] ?? false,
-      selectedItems: Set<ImmichAsset>.from(
-          map['selectedItems']?.map((x) => ImmichAsset.fromMap(x))),
-      selectedDateGroup: Set<String>.from(map['selectedDateGroup']),
-    );
-  }
-
-  String toJson() => json.encode(toMap());
-
-  factory HomePageState.fromJson(String source) =>
-      HomePageState.fromMap(json.decode(source));
-
   @override
   @override
   String toString() =>
   String toString() =>
       'HomePageState(isMultiSelectEnable: $isMultiSelectEnable, selectedItems: $selectedItems, selectedDateGroup: $selectedDateGroup)';
       'HomePageState(isMultiSelectEnable: $isMultiSelectEnable, selectedItems: $selectedItems, selectedDateGroup: $selectedDateGroup)';

+ 17 - 12
mobile/lib/modules/home/providers/home_page_state.provider.dart

@@ -1,6 +1,6 @@
 import 'package:hooks_riverpod/hooks_riverpod.dart';
 import 'package:hooks_riverpod/hooks_riverpod.dart';
 import 'package:immich_mobile/modules/home/models/home_page_state.model.dart';
 import 'package:immich_mobile/modules/home/models/home_page_state.model.dart';
-import 'package:immich_mobile/shared/models/immich_asset.model.dart';
+import 'package:openapi/api.dart';
 
 
 class HomePageStateNotifier extends StateNotifier<HomePageState> {
 class HomePageStateNotifier extends StateNotifier<HomePageState> {
   HomePageStateNotifier()
   HomePageStateNotifier()
@@ -14,7 +14,8 @@ class HomePageStateNotifier extends StateNotifier<HomePageState> {
 
 
   void addSelectedDateGroup(String dateGroupTitle) {
   void addSelectedDateGroup(String dateGroupTitle) {
     state = state.copyWith(
     state = state.copyWith(
-        selectedDateGroup: {...state.selectedDateGroup, dateGroupTitle});
+      selectedDateGroup: {...state.selectedDateGroup, dateGroupTitle},
+    );
   }
   }
 
 
   void removeSelectedDateGroup(String dateGroupTitle) {
   void removeSelectedDateGroup(String dateGroupTitle) {
@@ -25,36 +26,39 @@ class HomePageStateNotifier extends StateNotifier<HomePageState> {
     state = state.copyWith(selectedDateGroup: currentDateGroup);
     state = state.copyWith(selectedDateGroup: currentDateGroup);
   }
   }
 
 
-  void enableMultiSelect(Set<ImmichAsset> selectedItems) {
+  void enableMultiSelect(Set<AssetResponseDto> selectedItems) {
     state =
     state =
         state.copyWith(isMultiSelectEnable: true, selectedItems: selectedItems);
         state.copyWith(isMultiSelectEnable: true, selectedItems: selectedItems);
   }
   }
 
 
   void disableMultiSelect() {
   void disableMultiSelect() {
     state = state.copyWith(
     state = state.copyWith(
-        isMultiSelectEnable: false, selectedItems: {}, selectedDateGroup: {});
+      isMultiSelectEnable: false,
+      selectedItems: {},
+      selectedDateGroup: {},
+    );
   }
   }
 
 
-  void addSingleSelectedItem(ImmichAsset asset) {
+  void addSingleSelectedItem(AssetResponseDto asset) {
     state = state.copyWith(selectedItems: {...state.selectedItems, asset});
     state = state.copyWith(selectedItems: {...state.selectedItems, asset});
   }
   }
 
 
-  void addMultipleSelectedItems(List<ImmichAsset> assets) {
+  void addMultipleSelectedItems(List<AssetResponseDto> assets) {
     state = state.copyWith(selectedItems: {...state.selectedItems, ...assets});
     state = state.copyWith(selectedItems: {...state.selectedItems, ...assets});
   }
   }
 
 
-  void removeSingleSelectedItem(ImmichAsset asset) {
-    Set<ImmichAsset> currentList = state.selectedItems;
+  void removeSingleSelectedItem(AssetResponseDto asset) {
+    Set<AssetResponseDto> currentList = state.selectedItems;
 
 
     currentList.removeWhere((e) => e.id == asset.id);
     currentList.removeWhere((e) => e.id == asset.id);
 
 
     state = state.copyWith(selectedItems: currentList);
     state = state.copyWith(selectedItems: currentList);
   }
   }
 
 
-  void removeMultipleSelectedItem(List<ImmichAsset> assets) {
-    Set<ImmichAsset> currentList = state.selectedItems;
+  void removeMultipleSelectedItem(List<AssetResponseDto> assets) {
+    Set<AssetResponseDto> currentList = state.selectedItems;
 
 
-    for (ImmichAsset asset in assets) {
+    for (AssetResponseDto asset in assets) {
       currentList.removeWhere((e) => e.id == asset.id);
       currentList.removeWhere((e) => e.id == asset.id);
     }
     }
 
 
@@ -64,4 +68,5 @@ class HomePageStateNotifier extends StateNotifier<HomePageState> {
 
 
 final homePageStateProvider =
 final homePageStateProvider =
     StateNotifierProvider<HomePageStateNotifier, HomePageState>(
     StateNotifierProvider<HomePageStateNotifier, HomePageState>(
-        ((ref) => HomePageStateNotifier()));
+  ((ref) => HomePageStateNotifier()),
+);

+ 11 - 7
mobile/lib/modules/home/providers/upload_profile_image.provider.dart

@@ -73,10 +73,12 @@ class UploadProfileImageState {
 class UploadProfileImageNotifier
 class UploadProfileImageNotifier
     extends StateNotifier<UploadProfileImageState> {
     extends StateNotifier<UploadProfileImageState> {
   UploadProfileImageNotifier(this._userSErvice)
   UploadProfileImageNotifier(this._userSErvice)
-      : super(UploadProfileImageState(
-          profileImagePath: '',
-          status: UploadProfileStatus.idle,
-        ));
+      : super(
+          UploadProfileImageState(
+            profileImagePath: '',
+            status: UploadProfileStatus.idle,
+          ),
+        );
 
 
   final UserService _userSErvice;
   final UserService _userSErvice;
 
 
@@ -88,8 +90,9 @@ class UploadProfileImageNotifier
     if (res != null) {
     if (res != null) {
       debugPrint("Succesfully upload profile image");
       debugPrint("Succesfully upload profile image");
       state = state.copyWith(
       state = state.copyWith(
-          status: UploadProfileStatus.success,
-          profileImagePath: res.profileImagePath);
+        status: UploadProfileStatus.success,
+        profileImagePath: res.profileImagePath,
+      );
       return true;
       return true;
     }
     }
 
 
@@ -100,4 +103,5 @@ class UploadProfileImageNotifier
 
 
 final uploadProfileImageProvider =
 final uploadProfileImageProvider =
     StateNotifierProvider<UploadProfileImageNotifier, UploadProfileImageState>(
     StateNotifierProvider<UploadProfileImageNotifier, UploadProfileImageState>(
-        ((ref) => UploadProfileImageNotifier(ref.watch(userServiceProvider))));
+  ((ref) => UploadProfileImageNotifier(ref.watch(userServiceProvider))),
+);

+ 23 - 92
mobile/lib/modules/home/services/asset.service.dart

@@ -1,120 +1,51 @@
-import 'dart:convert';
+import 'dart:async';
 
 
 import 'package:flutter/material.dart';
 import 'package:flutter/material.dart';
 import 'package:hooks_riverpod/hooks_riverpod.dart';
 import 'package:hooks_riverpod/hooks_riverpod.dart';
-import 'package:immich_mobile/modules/home/models/delete_asset_response.model.dart';
-import 'package:immich_mobile/modules/home/models/get_all_asset_response.model.dart';
-import 'package:immich_mobile/shared/models/immich_asset.model.dart';
-import 'package:immich_mobile/shared/models/immich_asset_with_exif.model.dart';
-import 'package:immich_mobile/shared/services/network.service.dart';
+import 'package:immich_mobile/shared/services/api.service.dart';
+import 'package:openapi/api.dart';
 
 
-final assetServiceProvider =
-    Provider((ref) => AssetService(ref.watch(networkServiceProvider)));
+final assetServiceProvider = Provider(
+  (ref) => AssetService(
+    ref.watch(apiServiceProvider),
+  ),
+);
 
 
 class AssetService {
 class AssetService {
-  final NetworkService _networkService;
-  AssetService(this._networkService);
+  final ApiService _apiService;
 
 
-  Future<List<ImmichAsset>?> getAllAsset() async {
-    var res = await _networkService.getRequest(url: "asset/");
-    try {
-      List<dynamic> decodedData = jsonDecode(res.toString());
-
-      List<ImmichAsset> result =
-          List.from(decodedData.map((a) => ImmichAsset.fromMap(a)));
-      return result;
-    } catch (e) {
-      debugPrint("Error getAllAsset  ${e.toString()}");
-    }
-    return null;
-  }
-
-  Future<GetAllAssetResponse?> getAllAssetWithPagination() async {
-    var res = await _networkService.getRequest(url: "asset/all");
-    try {
-      Map<String, dynamic> decodedData = jsonDecode(res.toString());
-
-      GetAllAssetResponse result = GetAllAssetResponse.fromMap(decodedData);
-      return result;
-    } catch (e) {
-      debugPrint("Error getAllAsset  ${e.toString()}");
-    }
-    return null;
-  }
-
-  Future<GetAllAssetResponse?> getOlderAsset(String? nextPageKey) async {
-    try {
-      var res = await _networkService.getRequest(
-        url: "asset/all?nextPageKey=$nextPageKey",
-      );
-
-      Map<String, dynamic> decodedData = jsonDecode(res.toString());
-
-      GetAllAssetResponse result = GetAllAssetResponse.fromMap(decodedData);
-      if (result.count != 0) {
-        return result;
-      }
-    } catch (e) {
-      debugPrint("Error getAllAsset  ${e.toString()}");
-    }
-    return null;
-  }
+  AssetService(this._apiService);
 
 
-  Future<List<ImmichAsset>> getNewAsset(String latestDate) async {
+  Future<List<AssetResponseDto>?> getAllAsset() async {
     try {
     try {
-      var res = await _networkService.getRequest(
-        url: "asset/new?latestDate=$latestDate",
-      );
-
-      List<dynamic> decodedData = jsonDecode(res.toString());
-
-      List<ImmichAsset> result =
-          List.from(decodedData.map((a) => ImmichAsset.fromMap(a)));
-      if (result.isNotEmpty) {
-        return result;
-      }
-
-      return [];
+      return await _apiService.assetApi.getAllAssets();
     } catch (e) {
     } catch (e) {
-      debugPrint("Error getAllAsset  ${e.toString()}");
-      return [];
+      debugPrint("Error [getAllAsset]  ${e.toString()}");
+      return null;
     }
     }
   }
   }
 
 
-  Future<ImmichAssetWithExif?> getAssetById(String assetId) async {
+  Future<AssetResponseDto?> getAssetById(String assetId) async {
     try {
     try {
-      var res = await _networkService.getRequest(
-        url: "asset/assetById/$assetId",
-      );
-
-      Map<String, dynamic> decodedData = jsonDecode(res.toString());
-
-      ImmichAssetWithExif result = ImmichAssetWithExif.fromMap(decodedData);
-      return result;
+      return await _apiService.assetApi.getAssetById(assetId);
     } catch (e) {
     } catch (e) {
-      debugPrint("Error getAllAsset  ${e.toString()}");
+      debugPrint("Error [getAssetById]  ${e.toString()}");
       return null;
       return null;
     }
     }
   }
   }
 
 
-  Future<List<DeleteAssetResponse>?> deleteAssets(
-      Set<ImmichAsset> deleteAssets) async {
+  Future<List<DeleteAssetResponseDto>?> deleteAssets(
+    Set<AssetResponseDto> deleteAssets,
+  ) async {
     try {
     try {
-      var payload = [];
+      List<String> payload = [];
 
 
       for (var asset in deleteAssets) {
       for (var asset in deleteAssets) {
         payload.add(asset.id);
         payload.add(asset.id);
       }
       }
 
 
-      var res = await _networkService
-          .deleteRequest(url: "asset/", data: {"ids": payload});
-
-      List<dynamic> decodedData = jsonDecode(res.toString());
-
-      List<DeleteAssetResponse> result =
-          List.from(decodedData.map((a) => DeleteAssetResponse.fromMap(a)));
-
-      return result;
+      return await _apiService.assetApi
+          .deleteAsset(DeleteAssetDto(ids: payload));
     } catch (e) {
     } catch (e) {
       debugPrint("Error getAllAsset  ${e.toString()}");
       debugPrint("Error getAllAsset  ${e.toString()}");
       return null;
       return null;

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

@@ -15,7 +15,9 @@ class ControlBottomAppBar extends StatelessWidget {
         height: MediaQuery.of(context).size.height * 0.15,
         height: MediaQuery.of(context).size.height * 0.15,
         decoration: BoxDecoration(
         decoration: BoxDecoration(
           borderRadius: const BorderRadius.only(
           borderRadius: const BorderRadius.only(
-              topLeft: Radius.circular(15), topRight: Radius.circular(15)),
+            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(
@@ -48,12 +50,12 @@ 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})
-      : super(key: key);
+  const ControlBoxButton({
+    Key? key,
+    required this.label,
+    required this.iconData,
+    required this.onPressed,
+  }) : super(key: key);
 
 
   final String label;
   final String label;
   final IconData iconData;
   final IconData iconData;

+ 10 - 6
mobile/lib/modules/home/ui/daily_title_text.dart

@@ -2,8 +2,7 @@ import 'package:easy_localization/easy_localization.dart';
 import 'package:flutter/material.dart';
 import 'package:flutter/material.dart';
 import 'package:hooks_riverpod/hooks_riverpod.dart';
 import 'package:hooks_riverpod/hooks_riverpod.dart';
 import 'package:immich_mobile/modules/home/providers/home_page_state.provider.dart';
 import 'package:immich_mobile/modules/home/providers/home_page_state.provider.dart';
-import 'package:immich_mobile/shared/models/immich_asset.model.dart';
-import 'package:intl/intl.dart';
+import 'package:openapi/api.dart';
 
 
 class DailyTitleText extends ConsumerWidget {
 class DailyTitleText extends ConsumerWidget {
   const DailyTitleText({
   const DailyTitleText({
@@ -13,14 +12,15 @@ class DailyTitleText extends ConsumerWidget {
   }) : super(key: key);
   }) : super(key: key);
 
 
   final String isoDate;
   final String isoDate;
-  final List<ImmichAsset> assetGroup;
+  final List<AssetResponseDto> assetGroup;
 
 
   @override
   @override
   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 ? "daily_title_text_date".tr() : "daily_title_text_date_year".tr();
+    var formatDateTemplate = currentYear == groupYear
+        ? "daily_title_text_date".tr()
+        : "daily_title_text_date_year".tr();
     var dateText =
     var dateText =
         DateFormat(formatDateTemplate).format(DateTime.parse(isoDate));
         DateFormat(formatDateTemplate).format(DateTime.parse(isoDate));
     var isMultiSelectEnable =
     var isMultiSelectEnable =
@@ -74,7 +74,11 @@ class DailyTitleText extends ConsumerWidget {
     return SliverToBoxAdapter(
     return SliverToBoxAdapter(
       child: Padding(
       child: Padding(
         padding: const EdgeInsets.only(
         padding: const EdgeInsets.only(
-            top: 29.0, bottom: 29.0, left: 12.0, right: 12.0),
+          top: 29.0,
+          bottom: 29.0,
+          left: 12.0,
+          right: 12.0,
+        ),
         child: Row(
         child: Row(
           children: [
           children: [
             Text(
             Text(

+ 12 - 9
mobile/lib/modules/home/ui/disable_multi_select_button.dart

@@ -29,15 +29,18 @@ class DisableMultiSelectButton extends ConsumerWidget {
             child: Padding(
             child: Padding(
               padding: const EdgeInsets.symmetric(horizontal: 4.0),
               padding: const EdgeInsets.symmetric(horizontal: 4.0),
               child: TextButton.icon(
               child: TextButton.icon(
-                  onPressed: () {
-                    onPressed();
-                  },
-                  icon: const Icon(Icons.close_rounded),
-                  label: Text(
-                    '$selectedItemCount',
-                    style: const TextStyle(
-                        fontWeight: FontWeight.w600, fontSize: 18),
-                  )),
+                onPressed: () {
+                  onPressed();
+                },
+                icon: const Icon(Icons.close_rounded),
+                label: Text(
+                  '$selectedItemCount',
+                  style: const TextStyle(
+                    fontWeight: FontWeight.w600,
+                    fontSize: 18,
+                  ),
+                ),
+              ),
             ),
             ),
           ),
           ),
         ),
         ),

+ 68 - 48
mobile/lib/modules/home/ui/draggable_scrollbar.dart

@@ -118,20 +118,24 @@ class DraggableScrollbar extends StatefulWidget {
     this.labelConstraints,
     this.labelConstraints,
   })  : assert(child.scrollDirection == Axis.vertical),
   })  : assert(child.scrollDirection == Axis.vertical),
         scrollThumbBuilder = _thumbSemicircleBuilder(
         scrollThumbBuilder = _thumbSemicircleBuilder(
-            heightScrollThumb * 0.6, scrollThumbKey, alwaysVisibleScrollThumb),
+          heightScrollThumb * 0.6,
+          scrollThumbKey,
+          alwaysVisibleScrollThumb,
+        ),
         super(key: key);
         super(key: key);
 
 
   @override
   @override
   DraggableScrollbarState createState() => DraggableScrollbarState();
   DraggableScrollbarState createState() => DraggableScrollbarState();
 
 
-  static buildScrollThumbAndLabel(
-      {required Widget scrollThumb,
-      required Color backgroundColor,
-      required Animation<double>? thumbAnimation,
-      required Animation<double>? labelAnimation,
-      required Text? labelText,
-      required BoxConstraints? labelConstraints,
-      required bool alwaysVisibleScrollThumb}) {
+  static buildScrollThumbAndLabel({
+    required Widget scrollThumb,
+    required Color backgroundColor,
+    required Animation<double>? thumbAnimation,
+    required Animation<double>? labelAnimation,
+    required Text? labelText,
+    required BoxConstraints? labelConstraints,
+    required bool alwaysVisibleScrollThumb,
+  }) {
     var scrollThumbAndLabel = labelText == null
     var scrollThumbAndLabel = labelText == null
         ? scrollThumb
         ? scrollThumb
         : Row(
         : Row(
@@ -158,7 +162,10 @@ class DraggableScrollbar extends StatefulWidget {
   }
   }
 
 
   static ScrollThumbBuilder _thumbSemicircleBuilder(
   static ScrollThumbBuilder _thumbSemicircleBuilder(
-      double width, Key? scrollThumbKey, bool alwaysVisibleScrollThumb) {
+    double width,
+    Key? scrollThumbKey,
+    bool alwaysVisibleScrollThumb,
+  ) {
     return (
     return (
       Color backgroundColor,
       Color backgroundColor,
       Animation<double> thumbAnimation,
       Animation<double> thumbAnimation,
@@ -198,7 +205,9 @@ class DraggableScrollbar extends StatefulWidget {
   }
   }
 
 
   static ScrollThumbBuilder _thumbArrowBuilder(
   static ScrollThumbBuilder _thumbArrowBuilder(
-      Key? scrollThumbKey, bool alwaysVisibleScrollThumb) {
+    Key? scrollThumbKey,
+    bool alwaysVisibleScrollThumb,
+  ) {
     return (
     return (
       Color backgroundColor,
       Color backgroundColor,
       Animation<double> thumbAnimation,
       Animation<double> thumbAnimation,
@@ -234,7 +243,9 @@ class DraggableScrollbar extends StatefulWidget {
   }
   }
 
 
   static ScrollThumbBuilder _thumbRRectBuilder(
   static ScrollThumbBuilder _thumbRRectBuilder(
-      Key? scrollThumbKey, bool alwaysVisibleScrollThumb) {
+    Key? scrollThumbKey,
+    bool alwaysVisibleScrollThumb,
+  ) {
     return (
     return (
       Color backgroundColor,
       Color backgroundColor,
       Animation<double> thumbAnimation,
       Animation<double> thumbAnimation,
@@ -372,42 +383,44 @@ class DraggableScrollbarState extends State<DraggableScrollbar>
     }
     }
 
 
     return LayoutBuilder(
     return LayoutBuilder(
-        builder: (BuildContext context, BoxConstraints constraints) {
-      //print("LayoutBuilder constraints=$constraints");
-
-      return NotificationListener<ScrollNotification>(
-        onNotification: (ScrollNotification notification) {
-          changePosition(notification);
-          return false;
-        },
-        child: Stack(
-          children: <Widget>[
-            RepaintBoundary(
-              child: widget.child,
-            ),
-            RepaintBoundary(
+      builder: (BuildContext context, BoxConstraints constraints) {
+        //print("LayoutBuilder constraints=$constraints");
+
+        return NotificationListener<ScrollNotification>(
+          onNotification: (ScrollNotification notification) {
+            changePosition(notification);
+            return false;
+          },
+          child: Stack(
+            children: <Widget>[
+              RepaintBoundary(
+                child: widget.child,
+              ),
+              RepaintBoundary(
                 child: GestureDetector(
                 child: GestureDetector(
-              onVerticalDragStart: _onVerticalDragStart,
-              onVerticalDragUpdate: _onVerticalDragUpdate,
-              onVerticalDragEnd: _onVerticalDragEnd,
-              child: Container(
-                alignment: Alignment.topRight,
-                margin: EdgeInsets.only(top: _barOffset),
-                padding: widget.padding,
-                child: widget.scrollThumbBuilder(
-                  widget.backgroundColor,
-                  _thumbAnimation,
-                  _labelAnimation,
-                  widget.heightScrollThumb,
-                  labelText: labelText,
-                  labelConstraints: widget.labelConstraints,
+                  onVerticalDragStart: _onVerticalDragStart,
+                  onVerticalDragUpdate: _onVerticalDragUpdate,
+                  onVerticalDragEnd: _onVerticalDragEnd,
+                  child: Container(
+                    alignment: Alignment.topRight,
+                    margin: EdgeInsets.only(top: _barOffset),
+                    padding: widget.padding,
+                    child: widget.scrollThumbBuilder(
+                      widget.backgroundColor,
+                      _thumbAnimation,
+                      _labelAnimation,
+                      widget.heightScrollThumb,
+                      labelText: labelText,
+                      labelConstraints: widget.labelConstraints,
+                    ),
+                  ),
                 ),
                 ),
               ),
               ),
-            )),
-          ],
-        ),
-      );
-    });
+            ],
+          ),
+        );
+      },
+    );
   }
   }
 
 
   //scroll bar has received notification that it's view was scrolled
   //scroll bar has received notification that it's view was scrolled
@@ -498,7 +511,10 @@ class DraggableScrollbarState extends State<DraggableScrollbar>
         }
         }
 
 
         double viewDelta = getScrollViewDelta(
         double viewDelta = getScrollViewDelta(
-            details.delta.dy, barMaxScrollExtent, viewMaxScrollExtent);
+          details.delta.dy,
+          barMaxScrollExtent,
+          viewMaxScrollExtent,
+        );
 
 
         _viewOffset = widget.controller.position.pixels + viewDelta;
         _viewOffset = widget.controller.position.pixels + viewDelta;
         if (_viewOffset < widget.controller.position.minScrollExtent) {
         if (_viewOffset < widget.controller.position.minScrollExtent) {
@@ -579,7 +595,9 @@ class ArrowClipper extends CustomClipper<Path> {
     path.lineTo(startPointX + arrowWidth, startPointY);
     path.lineTo(startPointX + arrowWidth, startPointY);
     path.lineTo(startPointX + arrowWidth, startPointY + 1.0);
     path.lineTo(startPointX + arrowWidth, startPointY + 1.0);
     path.lineTo(
     path.lineTo(
-        startPointX + arrowWidth / 2, startPointY - arrowWidth / 2 + 1.0);
+      startPointX + arrowWidth / 2,
+      startPointY - arrowWidth / 2 + 1.0,
+    );
     path.lineTo(startPointX, startPointY + 1.0);
     path.lineTo(startPointX, startPointY + 1.0);
     path.close();
     path.close();
 
 
@@ -589,7 +607,9 @@ class ArrowClipper extends CustomClipper<Path> {
     path.lineTo(startPointX, startPointY);
     path.lineTo(startPointX, startPointY);
     path.lineTo(startPointX, startPointY - 1.0);
     path.lineTo(startPointX, startPointY - 1.0);
     path.lineTo(
     path.lineTo(
-        startPointX + arrowWidth / 2, startPointY + arrowWidth / 2 - 1.0);
+      startPointX + arrowWidth / 2,
+      startPointY + arrowWidth / 2 - 1.0,
+    );
     path.lineTo(startPointX + arrowWidth, startPointY - 1.0);
     path.lineTo(startPointX + arrowWidth, startPointY - 1.0);
     path.close();
     path.close();
 
 

+ 3 - 3
mobile/lib/modules/home/ui/image_grid.dart

@@ -1,10 +1,10 @@
 import 'package:flutter/material.dart';
 import 'package:flutter/material.dart';
 import 'package:hooks_riverpod/hooks_riverpod.dart';
 import 'package:hooks_riverpod/hooks_riverpod.dart';
 import 'package:immich_mobile/modules/home/ui/thumbnail_image.dart';
 import 'package:immich_mobile/modules/home/ui/thumbnail_image.dart';
-import 'package:immich_mobile/shared/models/immich_asset.model.dart';
+import 'package:openapi/api.dart';
 
 
 class ImageGrid extends ConsumerWidget {
 class ImageGrid extends ConsumerWidget {
-  final List<ImmichAsset> assetGroup;
+  final List<AssetResponseDto> assetGroup;
 
 
   const ImageGrid({Key? key, required this.assetGroup}) : super(key: key);
   const ImageGrid({Key? key, required this.assetGroup}) : super(key: key);
 
 
@@ -25,7 +25,7 @@ class ImageGrid extends ConsumerWidget {
             child: Stack(
             child: Stack(
               children: [
               children: [
                 ThumbnailImage(asset: assetGroup[index]),
                 ThumbnailImage(asset: assetGroup[index]),
-                if (assetType != 'IMAGE')
+                if (assetType != AssetTypeEnum.IMAGE)
                   Positioned(
                   Positioned(
                     top: 5,
                     top: 5,
                     right: 5,
                     right: 5,

+ 6 - 3
mobile/lib/modules/home/ui/immich_sliver_appbar.dart

@@ -31,7 +31,8 @@ class ImmichSliverAppBar extends ConsumerWidget {
       pinned: false,
       pinned: false,
       snap: false,
       snap: false,
       shape: const RoundedRectangleBorder(
       shape: const RoundedRectangleBorder(
-          borderRadius: BorderRadius.all(Radius.circular(5))),
+        borderRadius: BorderRadius.all(Radius.circular(5)),
+      ),
       leading: Builder(
       leading: Builder(
         builder: (BuildContext context) {
         builder: (BuildContext context) {
           return Stack(
           return Stack(
@@ -99,7 +100,8 @@ class ImmichSliverAppBar extends ConsumerWidget {
                   child: CircularProgressIndicator(
                   child: CircularProgressIndicator(
                     strokeWidth: 1,
                     strokeWidth: 1,
                     valueColor: AlwaysStoppedAnimation<Color>(
                     valueColor: AlwaysStoppedAnimation<Color>(
-                        Theme.of(context).primaryColor),
+                      Theme.of(context).primaryColor,
+                    ),
                   ),
                   ),
                 ),
                 ),
               ),
               ),
@@ -117,7 +119,8 @@ class ImmichSliverAppBar extends ConsumerWidget {
                         Icons.cloud_off_rounded,
                         Icons.cloud_off_rounded,
                         size: 8,
                         size: 8,
                       ),
                       ),
-                      child: const Icon(Icons.backup_rounded)),
+                      child: const Icon(Icons.backup_rounded),
+                    ),
               onPressed: () async {
               onPressed: () async {
                 var onPop = await AutoRouter.of(context)
                 var onPop = await AutoRouter.of(context)
                     .push(const BackupControllerRoute());
                     .push(const BackupControllerRoute());

+ 2 - 2
mobile/lib/modules/home/ui/monthly_title_text.dart

@@ -1,6 +1,5 @@
 import 'package:easy_localization/easy_localization.dart';
 import 'package:easy_localization/easy_localization.dart';
 import 'package:flutter/material.dart';
 import 'package:flutter/material.dart';
-import 'package:intl/intl.dart';
 
 
 class MonthlyTitleText extends StatelessWidget {
 class MonthlyTitleText extends StatelessWidget {
   const MonthlyTitleText({
   const MonthlyTitleText({
@@ -12,7 +11,8 @@ class MonthlyTitleText extends StatelessWidget {
 
 
   @override
   @override
   Widget build(BuildContext context) {
   Widget build(BuildContext context) {
-    var monthTitleText = DateFormat("monthly_title_text_date_format".tr()).format(DateTime.parse(isoDate));
+    var monthTitleText = DateFormat("monthly_title_text_date_format".tr())
+        .format(DateTime.parse(isoDate));
 
 
     return SliverToBoxAdapter(
     return SliverToBoxAdapter(
       child: Padding(
       child: Padding(

+ 27 - 16
mobile/lib/modules/home/ui/profile_drawer.dart

@@ -55,7 +55,8 @@ class ProfileDrawer extends HookConsumerWidget {
           return CircleAvatar(
           return CircleAvatar(
             radius: 35,
             radius: 35,
             backgroundImage: NetworkImage(
             backgroundImage: NetworkImage(
-                '$endpoint/user/profile-image/${authState.userId}?d=${dummmy++}'),
+              '$endpoint/user/profile-image/${authState.userId}?d=${dummmy++}',
+            ),
             backgroundColor: Colors.transparent,
             backgroundColor: Colors.transparent,
           );
           );
         } else {
         } else {
@@ -71,7 +72,8 @@ class ProfileDrawer extends HookConsumerWidget {
         return CircleAvatar(
         return CircleAvatar(
           radius: 35,
           radius: 35,
           backgroundImage: NetworkImage(
           backgroundImage: NetworkImage(
-              '$endpoint/user/profile-image/${authState.userId}?d=${dummmy++}'),
+            '$endpoint/user/profile-image/${authState.userId}?d=${dummmy++}',
+          ),
           backgroundColor: Colors.transparent,
           backgroundColor: Colors.transparent,
         );
         );
       }
       }
@@ -93,7 +95,10 @@ class ProfileDrawer extends HookConsumerWidget {
 
 
     _pickUserProfileImage() async {
     _pickUserProfileImage() async {
       final XFile? image = await ImagePicker().pickImage(
       final XFile? image = await ImagePicker().pickImage(
-          source: ImageSource.gallery, maxHeight: 1024, maxWidth: 1024);
+        source: ImageSource.gallery,
+        maxHeight: 1024,
+        maxWidth: 1024,
+      );
 
 
       if (image != null) {
       if (image != null) {
         var success =
         var success =
@@ -101,16 +106,20 @@ class ProfileDrawer extends HookConsumerWidget {
 
 
         if (success) {
         if (success) {
           ref.watch(authenticationProvider.notifier).updateUserProfileImagePath(
           ref.watch(authenticationProvider.notifier).updateUserProfileImagePath(
-              ref.read(uploadProfileImageProvider).profileImagePath);
+                ref.read(uploadProfileImageProvider).profileImagePath,
+              );
         }
         }
       }
       }
     }
     }
 
 
-    useEffect(() {
-      _getPackageInfo();
-      _buildUserProfileImage();
-      return null;
-    }, []);
+    useEffect(
+      () {
+        _getPackageInfo();
+        _buildUserProfileImage();
+        return null;
+      },
+      [],
+    );
     return Drawer(
     return Drawer(
       child: Column(
       child: Column(
         mainAxisAlignment: MainAxisAlignment.spaceBetween,
         mainAxisAlignment: MainAxisAlignment.spaceBetween,
@@ -186,9 +195,10 @@ class ProfileDrawer extends HookConsumerWidget {
                 title: const Text(
                 title: const Text(
                   "profile_drawer_sign_out",
                   "profile_drawer_sign_out",
                   style: TextStyle(
                   style: TextStyle(
-                      color: Colors.black54,
-                      fontSize: 14,
-                      fontWeight: FontWeight.bold),
+                    color: Colors.black54,
+                    fontSize: 14,
+                    fontWeight: FontWeight.bold,
+                  ),
                 ).tr(),
                 ).tr(),
                 onTap: () async {
                 onTap: () async {
                   bool res =
                   bool res =
@@ -231,9 +241,10 @@ class ProfileDrawer extends HookConsumerWidget {
                             : "profile_drawer_client_server_up_to_date".tr(),
                             : "profile_drawer_client_server_up_to_date".tr(),
                         textAlign: TextAlign.center,
                         textAlign: TextAlign.center,
                         style: TextStyle(
                         style: TextStyle(
-                            fontSize: 11,
-                            color: Theme.of(context).primaryColor,
-                            fontWeight: FontWeight.w600),
+                          fontSize: 11,
+                          color: Theme.of(context).primaryColor,
+                          fontWeight: FontWeight.w600,
+                        ),
                       ),
                       ),
                     ),
                     ),
                     const Divider(),
                     const Divider(),
@@ -271,7 +282,7 @@ class ProfileDrawer extends HookConsumerWidget {
                           ),
                           ),
                         ),
                         ),
                         Text(
                         Text(
-                          "${serverInfoState.serverVersion.major}.${serverInfoState.serverVersion.minor}.${serverInfoState.serverVersion.patch}",
+                          "${serverInfoState.serverVersion.major}.${serverInfoState.serverVersion.minor}.${serverInfoState.serverVersion.patch_}",
                           style: TextStyle(
                           style: TextStyle(
                             fontSize: 11,
                             fontSize: 11,
                             color: Colors.grey[500],
                             color: Colors.grey[500],

+ 16 - 12
mobile/lib/modules/home/ui/thumbnail_image.dart

@@ -8,11 +8,11 @@ import 'package:hooks_riverpod/hooks_riverpod.dart';
 import 'package:immich_mobile/constants/hive_box.dart';
 import 'package:immich_mobile/constants/hive_box.dart';
 import 'package:immich_mobile/modules/home/providers/home_page_state.provider.dart';
 import 'package:immich_mobile/modules/home/providers/home_page_state.provider.dart';
 import 'package:immich_mobile/modules/login/providers/authentication.provider.dart';
 import 'package:immich_mobile/modules/login/providers/authentication.provider.dart';
-import 'package:immich_mobile/shared/models/immich_asset.model.dart';
 import 'package:immich_mobile/routing/router.dart';
 import 'package:immich_mobile/routing/router.dart';
+import 'package:openapi/api.dart';
 
 
 class ThumbnailImage extends HookConsumerWidget {
 class ThumbnailImage extends HookConsumerWidget {
-  final ImmichAsset asset;
+  final AssetResponseDto asset;
 
 
   const ThumbnailImage({Key? key, required this.asset}) : super(key: key);
   const ThumbnailImage({Key? key, required this.asset}) : super(key: key);
 
 
@@ -22,14 +22,13 @@ class ThumbnailImage 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/thumbnail/${asset.id}';
     var selectedAsset = ref.watch(homePageStateProvider).selectedItems;
     var selectedAsset = ref.watch(homePageStateProvider).selectedItems;
     var isMultiSelectEnable =
     var isMultiSelectEnable =
         ref.watch(homePageStateProvider).isMultiSelectEnable;
         ref.watch(homePageStateProvider).isMultiSelectEnable;
     var deviceId = ref.watch(authenticationProvider).deviceId;
     var deviceId = ref.watch(authenticationProvider).deviceId;
 
 
-    Widget _buildSelectionIcon(ImmichAsset asset) {
+    Widget _buildSelectionIcon(AssetResponseDto asset) {
       if (selectedAsset.contains(asset)) {
       if (selectedAsset.contains(asset)) {
         return Icon(
         return Icon(
           Icons.check_circle,
           Icons.check_circle,
@@ -61,7 +60,7 @@ class ThumbnailImage extends HookConsumerWidget {
               .watch(homePageStateProvider.notifier)
               .watch(homePageStateProvider.notifier)
               .addSingleSelectedItem(asset);
               .addSingleSelectedItem(asset);
         } else {
         } else {
-          if (asset.type == 'IMAGE') {
+          if (asset.type == AssetTypeEnum.IMAGE) {
             AutoRouter.of(context).push(
             AutoRouter.of(context).push(
               ImageViewerRoute(
               ImageViewerRoute(
                 imageUrl:
                 imageUrl:
@@ -74,9 +73,10 @@ 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}',
-                  asset: asset),
+                videoUrl:
+                    '${box.get(serverEndpointKey)}/asset/file?aid=${asset.deviceAssetId}&did=${asset.deviceId}',
+                asset: asset,
+              ),
             );
             );
           }
           }
         }
         }
@@ -94,14 +94,16 @@ class ThumbnailImage extends HookConsumerWidget {
               decoration: BoxDecoration(
               decoration: BoxDecoration(
                 border: isMultiSelectEnable && selectedAsset.contains(asset)
                 border: isMultiSelectEnable && selectedAsset.contains(asset)
                     ? Border.all(
                     ? Border.all(
-                        color: Theme.of(context).primaryColorLight, width: 10)
+                        color: Theme.of(context).primaryColorLight,
+                        width: 10,
+                      )
                     : const Border(),
                     : const Border(),
               ),
               ),
               child: CachedNetworkImage(
               child: CachedNetworkImage(
                 cacheKey: "${asset.id}-${cacheKey.value}",
                 cacheKey: "${asset.id}-${cacheKey.value}",
                 width: 300,
                 width: 300,
                 height: 300,
                 height: 300,
-                memCacheHeight: asset.type == 'IMAGE' ? 250 : 400,
+                memCacheHeight: asset.type == AssetTypeEnum.IMAGE ? 250 : 400,
                 fit: BoxFit.cover,
                 fit: BoxFit.cover,
                 imageUrl: thumbnailRequestUrl,
                 imageUrl: thumbnailRequestUrl,
                 httpHeaders: {
                 httpHeaders: {
@@ -112,9 +114,11 @@ class ThumbnailImage extends HookConsumerWidget {
                     Transform.scale(
                     Transform.scale(
                   scale: 0.2,
                   scale: 0.2,
                   child: CircularProgressIndicator(
                   child: CircularProgressIndicator(
-                      value: downloadProgress.progress),
+                    value: downloadProgress.progress,
+                  ),
                 ),
                 ),
                 errorWidget: (context, url, error) {
                 errorWidget: (context, url, error) {
+                  debugPrint("Error getting thumbnail $url = $error");
                   return Icon(
                   return Icon(
                     Icons.image_not_supported_outlined,
                     Icons.image_not_supported_outlined,
                     color: Theme.of(context).primaryColor,
                     color: Theme.of(context).primaryColor,

+ 9 - 6
mobile/lib/modules/home/views/home_page.dart

@@ -26,12 +26,15 @@ class HomePage extends HookConsumerWidget {
         ref.watch(homePageStateProvider).isMultiSelectEnable;
         ref.watch(homePageStateProvider).isMultiSelectEnable;
     var homePageState = ref.watch(homePageStateProvider);
     var homePageState = ref.watch(homePageStateProvider);
 
 
-    useEffect(() {
-      ref.read(websocketProvider.notifier).connect();
-      ref.read(assetProvider.notifier).getAllAsset();
-      ref.watch(serverInfoProvider.notifier).getServerVersion();
-      return null;
-    }, []);
+    useEffect(
+      () {
+        ref.read(websocketProvider.notifier).connect();
+        ref.read(assetProvider.notifier).getAllAsset();
+        ref.watch(serverInfoProvider.notifier).getServerVersion();
+        return null;
+      },
+      [],
+    );
 
 
     void reloadAllAsset() {
     void reloadAllAsset() {
       ref.read(assetProvider.notifier).getAllAsset();
       ref.read(assetProvider.notifier).getAllAsset();

+ 5 - 47
mobile/lib/modules/login/models/authentication_state.model.dart

@@ -1,10 +1,8 @@
-import 'dart:convert';
-
-import 'package:immich_mobile/shared/models/device_info.model.dart';
+import 'package:openapi/api.dart';
 
 
 class AuthenticationState {
 class AuthenticationState {
   final String deviceId;
   final String deviceId;
-  final String deviceType;
+  final DeviceTypeEnum deviceType;
   final String userId;
   final String userId;
   final String userEmail;
   final String userEmail;
   final bool isAuthenticated;
   final bool isAuthenticated;
@@ -13,8 +11,7 @@ class AuthenticationState {
   final bool isAdmin;
   final bool isAdmin;
   final bool shouldChangePassword;
   final bool shouldChangePassword;
   final String profileImagePath;
   final String profileImagePath;
-  final DeviceInfoRemote deviceInfo;
-
+  final DeviceInfoResponseDto deviceInfo;
   AuthenticationState({
   AuthenticationState({
     required this.deviceId,
     required this.deviceId,
     required this.deviceType,
     required this.deviceType,
@@ -31,7 +28,7 @@ class AuthenticationState {
 
 
   AuthenticationState copyWith({
   AuthenticationState copyWith({
     String? deviceId,
     String? deviceId,
-    String? deviceType,
+    DeviceTypeEnum? deviceType,
     String? userId,
     String? userId,
     String? userEmail,
     String? userEmail,
     bool? isAuthenticated,
     bool? isAuthenticated,
@@ -40,7 +37,7 @@ class AuthenticationState {
     bool? isAdmin,
     bool? isAdmin,
     bool? shouldChangePassword,
     bool? shouldChangePassword,
     String? profileImagePath,
     String? profileImagePath,
-    DeviceInfoRemote? deviceInfo,
+    DeviceInfoResponseDto? deviceInfo,
   }) {
   }) {
     return AuthenticationState(
     return AuthenticationState(
       deviceId: deviceId ?? this.deviceId,
       deviceId: deviceId ?? this.deviceId,
@@ -57,45 +54,6 @@ class AuthenticationState {
     );
     );
   }
   }
 
 
-  Map<String, dynamic> toMap() {
-    final result = <String, dynamic>{};
-
-    result.addAll({'deviceId': deviceId});
-    result.addAll({'deviceType': deviceType});
-    result.addAll({'userId': userId});
-    result.addAll({'userEmail': userEmail});
-    result.addAll({'isAuthenticated': isAuthenticated});
-    result.addAll({'firstName': firstName});
-    result.addAll({'lastName': lastName});
-    result.addAll({'isAdmin': isAdmin});
-    result.addAll({'shouldChangePassword': shouldChangePassword});
-    result.addAll({'profileImagePath': profileImagePath});
-    result.addAll({'deviceInfo': deviceInfo.toMap()});
-
-    return result;
-  }
-
-  factory AuthenticationState.fromMap(Map<String, dynamic> map) {
-    return AuthenticationState(
-      deviceId: map['deviceId'] ?? '',
-      deviceType: map['deviceType'] ?? '',
-      userId: map['userId'] ?? '',
-      userEmail: map['userEmail'] ?? '',
-      isAuthenticated: map['isAuthenticated'] ?? false,
-      firstName: map['firstName'] ?? '',
-      lastName: map['lastName'] ?? '',
-      isAdmin: map['isAdmin'] ?? false,
-      shouldChangePassword: map['shouldChangePassword'] ?? false,
-      profileImagePath: map['profileImagePath'] ?? '',
-      deviceInfo: DeviceInfoRemote.fromMap(map['deviceInfo']),
-    );
-  }
-
-  String toJson() => json.encode(toMap());
-
-  factory AuthenticationState.fromJson(String source) =>
-      AuthenticationState.fromMap(json.decode(source));
-
   @override
   @override
   String toString() {
   String toString() {
     return 'AuthenticationState(deviceId: $deviceId, deviceType: $deviceType, userId: $userId, userEmail: $userEmail, isAuthenticated: $isAuthenticated, firstName: $firstName, lastName: $lastName, isAdmin: $isAdmin, shouldChangePassword: $shouldChangePassword, profileImagePath: $profileImagePath, deviceInfo: $deviceInfo)';
     return 'AuthenticationState(deviceId: $deviceId, deviceType: $deviceType, userId: $userId, userEmail: $userEmail, isAuthenticated: $isAuthenticated, firstName: $firstName, lastName: $lastName, isAdmin: $isAdmin, shouldChangePassword: $shouldChangePassword, profileImagePath: $profileImagePath, deviceInfo: $deviceInfo)';

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

@@ -16,9 +16,10 @@ 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,
+  });
 }
 }

+ 78 - 75
mobile/lib/modules/login/providers/authentication.provider.dart

@@ -1,23 +1,23 @@
-import 'package:dio/dio.dart';
 import 'package:flutter/material.dart';
 import 'package:flutter/material.dart';
 import 'package:hive/hive.dart';
 import 'package:hive/hive.dart';
 import 'package:hooks_riverpod/hooks_riverpod.dart';
 import 'package:hooks_riverpod/hooks_riverpod.dart';
 import 'package:immich_mobile/constants/hive_box.dart';
 import 'package:immich_mobile/constants/hive_box.dart';
 import 'package:immich_mobile/modules/login/models/authentication_state.model.dart';
 import 'package:immich_mobile/modules/login/models/authentication_state.model.dart';
 import 'package:immich_mobile/modules/login/models/hive_saved_login_info.model.dart';
 import 'package:immich_mobile/modules/login/models/hive_saved_login_info.model.dart';
-import 'package:immich_mobile/modules/login/models/login_response.model.dart';
 import 'package:immich_mobile/modules/backup/services/backup.service.dart';
 import 'package:immich_mobile/modules/backup/services/backup.service.dart';
+import 'package:immich_mobile/shared/services/api.service.dart';
 import 'package:immich_mobile/shared/services/device_info.service.dart';
 import 'package:immich_mobile/shared/services/device_info.service.dart';
-import 'package:immich_mobile/shared/services/network.service.dart';
-import 'package:immich_mobile/shared/models/device_info.model.dart';
+import 'package:openapi/api.dart';
 
 
 class AuthenticationNotifier extends StateNotifier<AuthenticationState> {
 class AuthenticationNotifier extends StateNotifier<AuthenticationState> {
   AuthenticationNotifier(
   AuthenticationNotifier(
-      this._deviceInfoService, this._backupService, this._networkService)
-      : super(
+    this._deviceInfoService,
+    this._backupService,
+    this._apiService,
+  ) : super(
           AuthenticationState(
           AuthenticationState(
             deviceId: "",
             deviceId: "",
-            deviceType: "",
+            deviceType: DeviceTypeEnum.ANDROID,
             userId: "",
             userId: "",
             userEmail: "",
             userEmail: "",
             firstName: '',
             firstName: '',
@@ -26,12 +26,11 @@ class AuthenticationNotifier extends StateNotifier<AuthenticationState> {
             isAdmin: false,
             isAdmin: false,
             shouldChangePassword: false,
             shouldChangePassword: false,
             isAuthenticated: false,
             isAuthenticated: false,
-            deviceInfo: DeviceInfoRemote(
+            deviceInfo: DeviceInfoResponseDto(
               id: 0,
               id: 0,
               userId: "",
               userId: "",
               deviceId: "",
               deviceId: "",
-              deviceType: "",
-              notificationToken: "",
+              deviceType: DeviceTypeEnum.ANDROID,
               createdAt: "",
               createdAt: "",
               isAutoBackup: false,
               isAutoBackup: false,
             ),
             ),
@@ -40,10 +39,14 @@ class AuthenticationNotifier extends StateNotifier<AuthenticationState> {
 
 
   final DeviceInfoService _deviceInfoService;
   final DeviceInfoService _deviceInfoService;
   final BackupService _backupService;
   final BackupService _backupService;
-  final NetworkService _networkService;
-
-  Future<bool> login(String email, String password, String serverEndpoint,
-      bool isSavedLoginInfo) async {
+  final ApiService _apiService;
+
+  Future<bool> login(
+    String email,
+    String password,
+    String serverEndpoint,
+    bool isSavedLoginInfo,
+  ) async {
     // Store server endpoint to Hive and test endpoint
     // Store server endpoint to Hive and test endpoint
     if (serverEndpoint[serverEndpoint.length - 1] == "/") {
     if (serverEndpoint[serverEndpoint.length - 1] == "/") {
       var validUrl = serverEndpoint.substring(0, serverEndpoint.length - 1);
       var validUrl = serverEndpoint.substring(0, serverEndpoint.length - 1);
@@ -52,12 +55,12 @@ class AuthenticationNotifier extends StateNotifier<AuthenticationState> {
       Hive.box(userInfoBox).put(serverEndpointKey, serverEndpoint);
       Hive.box(userInfoBox).put(serverEndpointKey, serverEndpoint);
     }
     }
 
 
+    // Check Server URL validity
     try {
     try {
-      bool isServerEndpointVerified = await _networkService.pingServer();
-      if (!isServerEndpointVerified) {
-        return false;
-      }
+      _apiService.setEndpoint(Hive.box(userInfoBox).get(serverEndpointKey));
+      await _apiService.serverInfoApi.pingServer();
     } catch (e) {
     } catch (e) {
+      debugPrint('Invalid Server Endpoint Url $e');
       return false;
       return false;
     }
     }
 
 
@@ -72,56 +75,73 @@ class AuthenticationNotifier extends StateNotifier<AuthenticationState> {
 
 
     // Make sign-in request
     // Make sign-in request
     try {
     try {
-      Response res = await _networkService.postRequest(
-          url: 'auth/login', data: {'email': email, 'password': password});
+      var loginResponse = await _apiService.authenticationApi.login(
+        LoginCredentialDto(
+          email: email,
+          password: password,
+        ),
+      );
 
 
-      var payload = LogInReponse.fromJson(res.toString());
+      if (loginResponse == null) {
+        debugPrint('Login Response is null');
+        return false;
+      }
 
 
-      Hive.box(userInfoBox).put(accessTokenKey, payload.accessToken);
+      Hive.box(userInfoBox).put(accessTokenKey, loginResponse.accessToken);
 
 
       state = state.copyWith(
       state = state.copyWith(
         isAuthenticated: true,
         isAuthenticated: true,
-        userId: payload.userId,
-        userEmail: payload.userEmail,
-        firstName: payload.firstName,
-        lastName: payload.lastName,
-        profileImagePath: payload.profileImagePath,
-        isAdmin: payload.isAdmin,
-        shouldChangePassword: payload.shouldChangePassword,
+        userId: loginResponse.userId,
+        userEmail: loginResponse.userEmail,
+        firstName: loginResponse.firstName,
+        lastName: loginResponse.lastName,
+        profileImagePath: loginResponse.profileImagePath,
+        isAdmin: loginResponse.isAdmin,
+        shouldChangePassword: loginResponse.shouldChangePassword,
       );
       );
 
 
+      // Login Success - Set Access Token to API Client
+      _apiService.setAccessToken(loginResponse.accessToken);
+
       if (isSavedLoginInfo) {
       if (isSavedLoginInfo) {
         // Save login info to local storage
         // Save login info to local storage
         Hive.box<HiveSavedLoginInfo>(hiveLoginInfoBox).put(
         Hive.box<HiveSavedLoginInfo>(hiveLoginInfoBox).put(
           savedLoginInfoKey,
           savedLoginInfoKey,
           HiveSavedLoginInfo(
           HiveSavedLoginInfo(
-              email: email,
-              password: password,
-              isSaveLogin: true,
-              serverUrl: Hive.box(userInfoBox).get(serverEndpointKey)),
+            email: email,
+            password: password,
+            isSaveLogin: true,
+            serverUrl: Hive.box(userInfoBox).get(serverEndpointKey),
+          ),
         );
         );
       } else {
       } else {
         Hive.box<HiveSavedLoginInfo>(hiveLoginInfoBox)
         Hive.box<HiveSavedLoginInfo>(hiveLoginInfoBox)
             .delete(savedLoginInfoKey);
             .delete(savedLoginInfoKey);
       }
       }
     } catch (e) {
     } catch (e) {
+      debugPrint("Error logging in $e");
       return false;
       return false;
     }
     }
 
 
     // Register device info
     // Register device info
     try {
     try {
-      Response res = await _networkService.postRequest(
-        url: 'device-info',
-        data: {
-          'deviceId': state.deviceId,
-          'deviceType': state.deviceType,
-        },
+      DeviceInfoResponseDto? deviceInfo =
+          await _apiService.deviceInfoApi.createDeviceInfo(
+        CreateDeviceInfoDto(
+          deviceId: state.deviceId,
+          deviceType: state.deviceType,
+        ),
       );
       );
 
 
-      DeviceInfoRemote deviceInfo = DeviceInfoRemote.fromJson(res.toString());
+      if (deviceInfo == null) {
+        debugPrint('Device Info Response is null');
+        return false;
+      }
+
       state = state.copyWith(deviceInfo: deviceInfo);
       state = state.copyWith(deviceInfo: deviceInfo);
     } catch (e) {
     } catch (e) {
       debugPrint("ERROR Register Device Info: $e");
       debugPrint("ERROR Register Device Info: $e");
+      return false;
     }
     }
 
 
     return true;
     return true;
@@ -129,27 +149,7 @@ class AuthenticationNotifier extends StateNotifier<AuthenticationState> {
 
 
   Future<bool> logout() async {
   Future<bool> logout() async {
     Hive.box(userInfoBox).delete(accessTokenKey);
     Hive.box(userInfoBox).delete(accessTokenKey);
-    state = AuthenticationState(
-      deviceId: "",
-      deviceType: "",
-      userId: "",
-      userEmail: "",
-      firstName: '',
-      lastName: '',
-      profileImagePath: '',
-      shouldChangePassword: false,
-      isAuthenticated: false,
-      isAdmin: false,
-      deviceInfo: DeviceInfoRemote(
-        id: 0,
-        userId: "",
-        deviceId: "",
-        deviceType: "",
-        notificationToken: "",
-        createdAt: "",
-        isAutoBackup: false,
-      ),
-    );
+    state = state.copyWith(isAuthenticated: false);
 
 
     return true;
     return true;
   }
   }
@@ -157,11 +157,13 @@ class AuthenticationNotifier extends StateNotifier<AuthenticationState> {
   setAutoBackup(bool backupState) async {
   setAutoBackup(bool backupState) async {
     var deviceInfo = await _deviceInfoService.getDeviceInfo();
     var deviceInfo = await _deviceInfoService.getDeviceInfo();
     var deviceId = deviceInfo["deviceId"];
     var deviceId = deviceInfo["deviceId"];
-    var deviceType = deviceInfo["deviceType"];
 
 
-    DeviceInfoRemote deviceInfoRemote =
+    DeviceTypeEnum deviceType = deviceInfo["deviceType"];
+
+    DeviceInfoResponseDto updatedDeviceInfo =
         await _backupService.setAutoBackup(backupState, deviceId, deviceType);
         await _backupService.setAutoBackup(backupState, deviceId, deviceType);
-    state = state.copyWith(deviceInfo: deviceInfoRemote);
+
+    state = state.copyWith(deviceInfo: updatedDeviceInfo);
   }
   }
 
 
   updateUserProfileImagePath(String path) {
   updateUserProfileImagePath(String path) {
@@ -169,19 +171,20 @@ class AuthenticationNotifier extends StateNotifier<AuthenticationState> {
   }
   }
 
 
   Future<bool> changePassword(String newPassword) async {
   Future<bool> changePassword(String newPassword) async {
-    Response res = await _networkService.putRequest(
-      url: 'user',
-      data: {
-        'id': state.userId,
-        'password': newPassword,
-        'shouldChangePassword': false,
-      },
-    );
+    try {
+      await _apiService.userApi.updateUser(
+        UpdateUserDto(
+          id: state.userId,
+          password: newPassword,
+          shouldChangePassword: false,
+        ),
+      );
 
 
-    if (res.statusCode == 200) {
       state = state.copyWith(shouldChangePassword: false);
       state = state.copyWith(shouldChangePassword: false);
+
       return true;
       return true;
-    } else {
+    } catch (e) {
+      debugPrint("Error changing password $e");
       return false;
       return false;
     }
     }
   }
   }
@@ -192,6 +195,6 @@ final authenticationProvider =
   return AuthenticationNotifier(
   return AuthenticationNotifier(
     ref.watch(deviceInfoServiceProvider),
     ref.watch(deviceInfoServiceProvider),
     ref.watch(backupServiceProvider),
     ref.watch(backupServiceProvider),
-    ref.watch(networkServiceProvider),
+    ref.watch(apiServiceProvider),
   );
   );
 });
 });

+ 27 - 26
mobile/lib/modules/login/ui/change_password_form.dart

@@ -140,35 +140,36 @@ class ChangePasswordButton extends ConsumerWidget {
   @override
   @override
   Widget build(BuildContext context, WidgetRef ref) {
   Widget build(BuildContext context, WidgetRef ref) {
     return ElevatedButton(
     return ElevatedButton(
-        style: ElevatedButton.styleFrom(
-          visualDensity: VisualDensity.standard,
-          primary: Theme.of(context).primaryColor,
-          onPrimary: Colors.grey[50],
-          elevation: 2,
-          padding: const EdgeInsets.symmetric(vertical: 10, horizontal: 25),
-        ),
-        onPressed: () async {
-          if (formKey.currentState!.validate()) {
-            var isSuccess = await ref
-                .watch(authenticationProvider.notifier)
-                .changePassword(passwordController.value.text);
+      style: ElevatedButton.styleFrom(
+        visualDensity: VisualDensity.standard,
+        primary: Theme.of(context).primaryColor,
+        onPrimary: Colors.grey[50],
+        elevation: 2,
+        padding: const EdgeInsets.symmetric(vertical: 10, horizontal: 25),
+      ),
+      onPressed: () async {
+        if (formKey.currentState!.validate()) {
+          var isSuccess = await ref
+              .watch(authenticationProvider.notifier)
+              .changePassword(passwordController.value.text);
 
 
-            if (isSuccess) {
-              bool res =
-                  await ref.watch(authenticationProvider.notifier).logout();
+          if (isSuccess) {
+            bool res =
+                await ref.watch(authenticationProvider.notifier).logout();
 
 
-              if (res) {
-                ref.watch(backupProvider.notifier).cancelBackup();
-                ref.watch(assetProvider.notifier).clearAllAsset();
-                ref.watch(websocketProvider.notifier).disconnect();
-                AutoRouter.of(context).replace(const LoginRoute());
-              }
+            if (res) {
+              ref.watch(backupProvider.notifier).cancelBackup();
+              ref.watch(assetProvider.notifier).clearAllAsset();
+              ref.watch(websocketProvider.notifier).disconnect();
+              AutoRouter.of(context).replace(const LoginRoute());
             }
             }
           }
           }
-        },
-        child: const Text(
-          "Change Password",
-          style: TextStyle(fontSize: 14, fontWeight: FontWeight.bold),
-        ));
+        }
+      },
+      child: const Text(
+        "Change Password",
+        style: TextStyle(fontSize: 14, fontWeight: FontWeight.bold),
+      ),
+    );
   }
   }
 }
 }

+ 71 - 61
mobile/lib/modules/login/ui/login_form.dart

@@ -22,22 +22,25 @@ class LoginForm extends HookConsumerWidget {
     final passwordController =
     final passwordController =
         useTextEditingController.fromValue(TextEditingValue.empty);
         useTextEditingController.fromValue(TextEditingValue.empty);
     final serverEndpointController =
     final serverEndpointController =
-        useTextEditingController(text: 'login_endpoint_hint'.tr());
+        useTextEditingController(text: 'login_form_endpoint_hint'.tr());
     final isSaveLoginInfo = useState<bool>(false);
     final isSaveLoginInfo = useState<bool>(false);
 
 
-    useEffect(() {
-      var loginInfo =
-          Hive.box<HiveSavedLoginInfo>(hiveLoginInfoBox).get(savedLoginInfoKey);
-
-      if (loginInfo != null) {
-        usernameController.text = loginInfo.email;
-        passwordController.text = loginInfo.password;
-        serverEndpointController.text = loginInfo.serverUrl;
-        isSaveLoginInfo.value = loginInfo.isSaveLogin;
-      }
-
-      return null;
-    }, []);
+    useEffect(
+      () {
+        var loginInfo = Hive.box<HiveSavedLoginInfo>(hiveLoginInfoBox)
+            .get(savedLoginInfoKey);
+
+        if (loginInfo != null) {
+          usernameController.text = loginInfo.email;
+          passwordController.text = loginInfo.password;
+          serverEndpointController.text = loginInfo.serverUrl;
+          isSaveLoginInfo.value = loginInfo.isSaveLogin;
+        }
+
+        return null;
+      },
+      [],
+    );
 
 
     return Center(
     return Center(
       child: ConstrainedBox(
       child: ConstrainedBox(
@@ -71,14 +74,16 @@ class LoginForm extends HookConsumerWidget {
                 dense: true,
                 dense: true,
                 side: const BorderSide(color: Colors.grey, width: 1.5),
                 side: const BorderSide(color: Colors.grey, width: 1.5),
                 shape: RoundedRectangleBorder(
                 shape: RoundedRectangleBorder(
-                    borderRadius: BorderRadius.circular(5)),
+                  borderRadius: BorderRadius.circular(5),
+                ),
                 enableFeedback: true,
                 enableFeedback: true,
                 title: const Text(
                 title: const Text(
                   "login_form_save_login",
                   "login_form_save_login",
                   style: TextStyle(
                   style: TextStyle(
-                      fontSize: 16,
-                      fontWeight: FontWeight.bold,
-                      color: Colors.grey),
+                    fontSize: 16,
+                    fontWeight: FontWeight.bold,
+                    color: Colors.grey,
+                  ),
                 ).tr(),
                 ).tr(),
                 value: isSaveLoginInfo.value,
                 value: isSaveLoginInfo.value,
                 onChanged: (switchValue) {
                 onChanged: (switchValue) {
@@ -108,7 +113,6 @@ class ServerEndpointInput extends StatelessWidget {
       : super(key: key);
       : super(key: key);
 
 
   String? _validateInput(String? url) {
   String? _validateInput(String? url) {
-
     if (url?.startsWith(RegExp(r'https?://')) == true) {
     if (url?.startsWith(RegExp(r'https?://')) == true) {
       return null;
       return null;
     } else {
     } else {
@@ -122,7 +126,7 @@ class ServerEndpointInput extends StatelessWidget {
       controller: controller,
       controller: controller,
       decoration: InputDecoration(
       decoration: InputDecoration(
         labelText: 'login_form_endpoint_url'.tr(),
         labelText: 'login_form_endpoint_url'.tr(),
-        border: OutlineInputBorder(),
+        border: const OutlineInputBorder(),
         hintText: 'login_form_endpoint_hint'.tr(),
         hintText: 'login_form_endpoint_hint'.tr(),
       ),
       ),
       validator: _validateInput,
       validator: _validateInput,
@@ -140,8 +144,9 @@ class EmailInput extends StatelessWidget {
     if (email == null || email == '') return null;
     if (email == null || email == '') return null;
     if (email.endsWith(' ')) return 'login_form_err_trailing_whitespace'.tr();
     if (email.endsWith(' ')) return 'login_form_err_trailing_whitespace'.tr();
     if (email.startsWith(' ')) return 'login_form_err_leading_whitespace'.tr();
     if (email.startsWith(' ')) return 'login_form_err_leading_whitespace'.tr();
-    if (email.contains(' ') || !email.contains('@'))
+    if (email.contains(' ') || !email.contains('@')) {
       return 'login_form_err_invalid_email'.tr();
       return 'login_form_err_invalid_email'.tr();
+    }
     return null;
     return null;
   }
   }
 
 
@@ -151,7 +156,7 @@ class EmailInput extends StatelessWidget {
       controller: controller,
       controller: controller,
       decoration: InputDecoration(
       decoration: InputDecoration(
         labelText: 'login_form_label_email'.tr(),
         labelText: 'login_form_label_email'.tr(),
-        border: OutlineInputBorder(),
+        border: const OutlineInputBorder(),
         hintText: 'login_form_email_hint'.tr(),
         hintText: 'login_form_email_hint'.tr(),
       ),
       ),
       validator: _validateInput,
       validator: _validateInput,
@@ -171,9 +176,10 @@ class PasswordInput extends StatelessWidget {
       obscureText: true,
       obscureText: true,
       controller: controller,
       controller: controller,
       decoration: InputDecoration(
       decoration: InputDecoration(
-          labelText: 'login_form_label_password'.tr(),
-          border: OutlineInputBorder(),
-          hintText: 'login_form_password_hint'.tr()),
+        labelText: 'login_form_label_password'.tr(),
+        border: const OutlineInputBorder(),
+        hintText: 'login_form_password_hint'.tr(),
+      ),
     );
     );
   }
   }
 }
 }
@@ -195,43 +201,47 @@ class LoginButton extends ConsumerWidget {
   @override
   @override
   Widget build(BuildContext context, WidgetRef ref) {
   Widget build(BuildContext context, WidgetRef ref) {
     return ElevatedButton(
     return ElevatedButton(
-        style: ElevatedButton.styleFrom(
-          visualDensity: VisualDensity.standard,
-          primary: Theme.of(context).primaryColor,
-          onPrimary: Colors.grey[50],
-          elevation: 2,
-          padding: const EdgeInsets.symmetric(vertical: 10, horizontal: 25),
-        ),
-        onPressed: () async {
-          // This will remove current cache asset state of previous user login.
-          ref.watch(assetProvider.notifier).clearAllAsset();
-
-          var isAuthenticated = await ref
-              .watch(authenticationProvider.notifier)
-              .login(emailController.text, passwordController.text,
-                  serverEndpointController.text, isSavedLoginInfo);
-
-          if (isAuthenticated) {
-            // Resume backup (if enable) then navigate
-
-            if (ref.watch(authenticationProvider).shouldChangePassword &&
-                !ref.watch(authenticationProvider).isAdmin) {
-              AutoRouter.of(context).push(const ChangePasswordRoute());
-            } else {
-              ref.watch(backupProvider.notifier).resumeBackup();
-              AutoRouter.of(context).pushNamed("/tab-controller-page");
-            }
+      style: ElevatedButton.styleFrom(
+        visualDensity: VisualDensity.standard,
+        primary: Theme.of(context).primaryColor,
+        onPrimary: Colors.grey[50],
+        elevation: 2,
+        padding: const EdgeInsets.symmetric(vertical: 10, horizontal: 25),
+      ),
+      onPressed: () async {
+        // This will remove current cache asset state of previous user login.
+        ref.watch(assetProvider.notifier).clearAllAsset();
+
+        var isAuthenticated =
+            await ref.watch(authenticationProvider.notifier).login(
+                  emailController.text,
+                  passwordController.text,
+                  serverEndpointController.text,
+                  isSavedLoginInfo,
+                );
+
+        if (isAuthenticated) {
+          // Resume backup (if enable) then navigate
+
+          if (ref.watch(authenticationProvider).shouldChangePassword &&
+              !ref.watch(authenticationProvider).isAdmin) {
+            AutoRouter.of(context).push(const ChangePasswordRoute());
           } else {
           } else {
-            ImmichToast.show(
-              context: context,
-              msg: "login_failed".tr(),
-              toastType: ToastType.error,
-            );
+            ref.watch(backupProvider.notifier).resumeBackup();
+            AutoRouter.of(context).pushNamed("/tab-controller-page");
           }
           }
-        },
-        child: const Text(
-          "login_form_button_text",
-          style: TextStyle(fontSize: 14, fontWeight: FontWeight.bold),
-        ).tr());
+        } else {
+          ImmichToast.show(
+            context: context,
+            msg: "login_form_failed_login".tr(),
+            toastType: ToastType.error,
+          );
+        }
+      },
+      child: const Text(
+        "login_form_button_text",
+        style: TextStyle(fontSize: 14, fontWeight: FontWeight.bold),
+      ).tr(),
+    );
   }
   }
 }
 }

+ 0 - 84
mobile/lib/modules/search/models/curated_location.model.dart

@@ -1,84 +0,0 @@
-import 'dart:convert';
-
-class CuratedLocation {
-  final String id;
-  final String city;
-  final String resizePath;
-  final String deviceAssetId;
-  final String deviceId;
-
-  CuratedLocation({
-    required this.id,
-    required this.city,
-    required this.resizePath,
-    required this.deviceAssetId,
-    required this.deviceId,
-  });
-
-  CuratedLocation copyWith({
-    String? id,
-    String? city,
-    String? resizePath,
-    String? deviceAssetId,
-    String? deviceId,
-  }) {
-    return CuratedLocation(
-      id: id ?? this.id,
-      city: city ?? this.city,
-      resizePath: resizePath ?? this.resizePath,
-      deviceAssetId: deviceAssetId ?? this.deviceAssetId,
-      deviceId: deviceId ?? this.deviceId,
-    );
-  }
-
-  Map<String, dynamic> toMap() {
-    return {
-      'id': id,
-      'city': city,
-      'resizePath': resizePath,
-      'deviceAssetId': deviceAssetId,
-      'deviceId': deviceId,
-    };
-  }
-
-  factory CuratedLocation.fromMap(Map<String, dynamic> map) {
-    return CuratedLocation(
-      id: map['id'] ?? '',
-      city: map['city'] ?? '',
-      resizePath: map['resizePath'] ?? '',
-      deviceAssetId: map['deviceAssetId'] ?? '',
-      deviceId: map['deviceId'] ?? '',
-    );
-  }
-
-  String toJson() => json.encode(toMap());
-
-  factory CuratedLocation.fromJson(String source) =>
-      CuratedLocation.fromMap(json.decode(source));
-
-  @override
-  String toString() {
-    return 'CuratedLocation(id: $id, city: $city, resizePath: $resizePath, deviceAssetId: $deviceAssetId, deviceId: $deviceId)';
-  }
-
-  @override
-  bool operator ==(Object other) {
-    if (identical(this, other)) return true;
-
-    return other is CuratedLocation &&
-        other.id == id &&
-        other.city == city &&
-        other.resizePath == resizePath &&
-        other.deviceAssetId == deviceAssetId &&
-        other.deviceId == deviceId;
-  }
-
-  @override
-  int get hashCode {
-    return id.hashCode ^
-        city.hashCode ^
-        resizePath.hashCode ^
-        deviceAssetId.hashCode ^
-        deviceId.hashCode;
-  }
-}

+ 0 - 85
mobile/lib/modules/search/models/curated_object.model.dart

@@ -1,85 +0,0 @@
-import 'dart:convert';
-
-class CuratedObject {
-  final String id;
-  final String object;
-  final String resizePath;
-  final String deviceAssetId;
-  final String deviceId;
-  CuratedObject({
-    required this.id,
-    required this.object,
-    required this.resizePath,
-    required this.deviceAssetId,
-    required this.deviceId,
-  });
-
-  CuratedObject copyWith({
-    String? id,
-    String? object,
-    String? resizePath,
-    String? deviceAssetId,
-    String? deviceId,
-  }) {
-    return CuratedObject(
-      id: id ?? this.id,
-      object: object ?? this.object,
-      resizePath: resizePath ?? this.resizePath,
-      deviceAssetId: deviceAssetId ?? this.deviceAssetId,
-      deviceId: deviceId ?? this.deviceId,
-    );
-  }
-
-  Map<String, dynamic> toMap() {
-    final result = <String, dynamic>{};
-
-    result.addAll({'id': id});
-    result.addAll({'object': object});
-    result.addAll({'resizePath': resizePath});
-    result.addAll({'deviceAssetId': deviceAssetId});
-    result.addAll({'deviceId': deviceId});
-
-    return result;
-  }
-
-  factory CuratedObject.fromMap(Map<String, dynamic> map) {
-    return CuratedObject(
-      id: map['id'] ?? '',
-      object: map['object'] ?? '',
-      resizePath: map['resizePath'] ?? '',
-      deviceAssetId: map['deviceAssetId'] ?? '',
-      deviceId: map['deviceId'] ?? '',
-    );
-  }
-
-  String toJson() => json.encode(toMap());
-
-  factory CuratedObject.fromJson(String source) =>
-      CuratedObject.fromMap(json.decode(source));
-
-  @override
-  String toString() {
-    return 'CuratedObject(id: $id, object: $object, resizePath: $resizePath, deviceAssetId: $deviceAssetId, deviceId: $deviceId)';
-  }
-
-  @override
-  bool operator ==(Object other) {
-    if (identical(this, other)) return true;
-
-    return other is CuratedObject &&
-        other.id == id &&
-        other.object == object &&
-        other.resizePath == resizePath &&
-        other.deviceAssetId == deviceAssetId &&
-        other.deviceId == deviceId;
-  }
-
-  @override
-  int get hashCode {
-    return id.hashCode ^
-        object.hashCode ^
-        resizePath.hashCode ^
-        deviceAssetId.hashCode ^
-        deviceId.hashCode;
-  }
-}

+ 7 - 6
mobile/lib/modules/search/models/search_result_page_state.model.dart

@@ -1,13 +1,13 @@
 import 'dart:convert';
 import 'dart:convert';
 
 
 import 'package:collection/collection.dart';
 import 'package:collection/collection.dart';
-import 'package:immich_mobile/shared/models/immich_asset.model.dart';
+import 'package:openapi/api.dart';
 
 
 class SearchResultPageState {
 class SearchResultPageState {
   final bool isLoading;
   final bool isLoading;
   final bool isSuccess;
   final bool isSuccess;
   final bool isError;
   final bool isError;
-  final List<ImmichAsset> searchResult;
+  final List<AssetResponseDto> searchResult;
 
 
   SearchResultPageState({
   SearchResultPageState({
     required this.isLoading,
     required this.isLoading,
@@ -20,7 +20,7 @@ class SearchResultPageState {
     bool? isLoading,
     bool? isLoading,
     bool? isSuccess,
     bool? isSuccess,
     bool? isError,
     bool? isError,
-    List<ImmichAsset>? searchResult,
+    List<AssetResponseDto>? searchResult,
   }) {
   }) {
     return SearchResultPageState(
     return SearchResultPageState(
       isLoading: isLoading ?? this.isLoading,
       isLoading: isLoading ?? this.isLoading,
@@ -35,7 +35,7 @@ class SearchResultPageState {
       'isLoading': isLoading,
       'isLoading': isLoading,
       'isSuccess': isSuccess,
       'isSuccess': isSuccess,
       'isError': isError,
       'isError': isError,
-      'searchResult': searchResult.map((x) => x.toMap()).toList(),
+      'searchResult': searchResult.map((x) => x.toJson()).toList(),
     };
     };
   }
   }
 
 
@@ -44,8 +44,9 @@ 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<AssetResponseDto>.from(
+        map['searchResult']?.map((x) => AssetResponseDto.mapFromJson(x)),
+      ),
     );
     );
   }
   }
 
 

+ 3 - 4
mobile/lib/modules/search/providers/search_page_state.provider.dart

@@ -1,9 +1,8 @@
 import 'package:hooks_riverpod/hooks_riverpod.dart';
 import 'package:hooks_riverpod/hooks_riverpod.dart';
-import 'package:immich_mobile/modules/search/models/curated_location.model.dart';
-import 'package:immich_mobile/modules/search/models/curated_object.model.dart';
 import 'package:immich_mobile/modules/search/models/search_page_state.model.dart';
 import 'package:immich_mobile/modules/search/models/search_page_state.model.dart';
 
 
 import 'package:immich_mobile/modules/search/services/search.service.dart';
 import 'package:immich_mobile/modules/search/services/search.service.dart';
+import 'package:openapi/api.dart';
 
 
 class SearchPageStateNotifier extends StateNotifier<SearchPageState> {
 class SearchPageStateNotifier extends StateNotifier<SearchPageState> {
   SearchPageStateNotifier(this._searchService)
   SearchPageStateNotifier(this._searchService)
@@ -58,7 +57,7 @@ final searchPageStateProvider =
 });
 });
 
 
 final getCuratedLocationProvider =
 final getCuratedLocationProvider =
-    FutureProvider.autoDispose<List<CuratedLocation>>((ref) async {
+    FutureProvider.autoDispose<List<CuratedLocationsResponseDto>>((ref) async {
   final SearchService searchService = ref.watch(searchServiceProvider);
   final SearchService searchService = ref.watch(searchServiceProvider);
 
 
   var curatedLocation = await searchService.getCuratedLocation();
   var curatedLocation = await searchService.getCuratedLocation();
@@ -66,7 +65,7 @@ final getCuratedLocationProvider =
 });
 });
 
 
 final getCuratedObjectProvider =
 final getCuratedObjectProvider =
-    FutureProvider.autoDispose<List<CuratedObject>>((ref) async {
+    FutureProvider.autoDispose<List<CuratedObjectsResponseDto>>((ref) async {
   final SearchService searchService = ref.watch(searchServiceProvider);
   final SearchService searchService = ref.watch(searchServiceProvider);
 
 
   var curatedObject = await searchService.getCuratedObjects();
   var curatedObject = await searchService.getCuratedObjects();

+ 25 - 11
mobile/lib/modules/search/providers/search_result_page.provider.dart

@@ -3,8 +3,8 @@ import 'package:hooks_riverpod/hooks_riverpod.dart';
 import 'package:immich_mobile/modules/search/models/search_result_page_state.model.dart';
 import 'package:immich_mobile/modules/search/models/search_result_page_state.model.dart';
 
 
 import 'package:immich_mobile/modules/search/services/search.service.dart';
 import 'package:immich_mobile/modules/search/services/search.service.dart';
-import 'package:immich_mobile/shared/models/immich_asset.model.dart';
 import 'package:intl/intl.dart';
 import 'package:intl/intl.dart';
+import 'package:openapi/api.dart';
 
 
 class SearchResultPageNotifier extends StateNotifier<SearchResultPageState> {
 class SearchResultPageNotifier extends StateNotifier<SearchResultPageState> {
   SearchResultPageNotifier(this._searchService)
   SearchResultPageNotifier(this._searchService)
@@ -21,19 +21,29 @@ class SearchResultPageNotifier extends StateNotifier<SearchResultPageState> {
 
 
   void search(String searchTerm) async {
   void search(String searchTerm) async {
     state = state.copyWith(
     state = state.copyWith(
-        searchResult: [], isError: false, isLoading: true, isSuccess: false);
+      searchResult: [],
+      isError: false,
+      isLoading: true,
+      isSuccess: false,
+    );
 
 
-    List<ImmichAsset>? assets = await _searchService.searchAsset(searchTerm);
+    List<AssetResponseDto>? assets =
+        await _searchService.searchAsset(searchTerm);
 
 
     if (assets != null) {
     if (assets != null) {
       state = state.copyWith(
       state = state.copyWith(
-          searchResult: assets,
-          isError: false,
-          isLoading: false,
-          isSuccess: true);
+        searchResult: assets,
+        isError: false,
+        isLoading: false,
+        isSuccess: true,
+      );
     } else {
     } else {
       state = state.copyWith(
       state = state.copyWith(
-          searchResult: [], isError: true, isLoading: false, isSuccess: false);
+        searchResult: [],
+        isError: true,
+        isLoading: false,
+        isSuccess: false,
+      );
     }
     }
   }
   }
 }
 }
@@ -48,7 +58,11 @@ final searchResultGroupByDateTimeProvider = StateProvider((ref) {
   var assets = ref.watch(searchResultPageProvider).searchResult;
   var assets = ref.watch(searchResultPageProvider).searchResult;
 
 
   assets.sortByCompare<DateTime>(
   assets.sortByCompare<DateTime>(
-      (e) => DateTime.parse(e.createdAt), (a, b) => b.compareTo(a));
-  return assets.groupListsBy((element) =>
-      DateFormat('y-MM-dd').format(DateTime.parse(element.createdAt)));
+    (e) => DateTime.parse(e.createdAt),
+    (a, b) => b.compareTo(a),
+  );
+  return assets.groupListsBy(
+    (element) =>
+        DateFormat('y-MM-dd').format(DateTime.parse(element.createdAt)),
+  );
 });
 });

+ 22 - 47
mobile/lib/modules/search/services/search.service.dart

@@ -1,79 +1,54 @@
-import 'dart:convert';
-
 import 'package:flutter/material.dart';
 import 'package:flutter/material.dart';
 import 'package:hooks_riverpod/hooks_riverpod.dart';
 import 'package:hooks_riverpod/hooks_riverpod.dart';
-import 'package:immich_mobile/modules/search/models/curated_location.model.dart';
-import 'package:immich_mobile/modules/search/models/curated_object.model.dart';
-import 'package:immich_mobile/shared/models/immich_asset.model.dart';
-import 'package:immich_mobile/shared/services/network.service.dart';
+import 'package:immich_mobile/shared/services/api.service.dart';
+import 'package:openapi/api.dart';
 
 
-final searchServiceProvider =
-    Provider((ref) => SearchService(ref.watch(networkServiceProvider)));
+final searchServiceProvider = Provider(
+  (ref) => SearchService(
+    ref.watch(apiServiceProvider),
+  ),
+);
 
 
 class SearchService {
 class SearchService {
-  final NetworkService _networkService;
-  SearchService(this._networkService);
+  final ApiService _apiService;
+  SearchService(this._apiService);
 
 
   Future<List<String>?> getUserSuggestedSearchTerms() async {
   Future<List<String>?> getUserSuggestedSearchTerms() async {
     try {
     try {
-      var res = await _networkService.getRequest(url: "asset/searchTerm");
-      List<dynamic> decodedData = jsonDecode(res.toString());
-
-      return List.from(decodedData);
+      return await _apiService.assetApi.getAssetSearchTerms();
     } catch (e) {
     } catch (e) {
       debugPrint("[ERROR] [getUserSuggestedSearchTerms] ${e.toString()}");
       debugPrint("[ERROR] [getUserSuggestedSearchTerms] ${e.toString()}");
       return [];
       return [];
     }
     }
   }
   }
 
 
-  Future<List<ImmichAsset>?> searchAsset(String searchTerm) async {
+  Future<List<AssetResponseDto>?> searchAsset(String searchTerm) async {
     try {
     try {
-      var res = await _networkService.postRequest(
-        url: "asset/search",
-        data: {"searchTerm": searchTerm},
-      );
-
-      List<dynamic> decodedData = jsonDecode(res.toString());
-
-      List<ImmichAsset> result =
-          List.from(decodedData.map((a) => ImmichAsset.fromMap(a)));
-
-      return result;
+      return await _apiService.assetApi
+          .searchAsset(SearchAssetDto(searchTerm: searchTerm));
     } catch (e) {
     } catch (e) {
       debugPrint("[ERROR] [searchAsset] ${e.toString()}");
       debugPrint("[ERROR] [searchAsset] ${e.toString()}");
       return null;
       return null;
     }
     }
   }
   }
 
 
-  Future<List<CuratedLocation>?> getCuratedLocation() async {
+  Future<List<CuratedLocationsResponseDto>?> getCuratedLocation() async {
     try {
     try {
-      var res = await _networkService.getRequest(url: "asset/allLocation");
-
-      List<dynamic> decodedData = jsonDecode(res.toString());
+      var locations = await _apiService.assetApi.getCuratedLocations();
 
 
-      List<CuratedLocation> result =
-          List.from(decodedData.map((a) => CuratedLocation.fromMap(a)));
-
-      return result;
+      return locations;
     } catch (e) {
     } catch (e) {
-      debugPrint("[ERROR] [getCuratedLocation] ${e.toString()}");
-      throw Error();
+      debugPrint("Error [getCuratedLocation] ${e.toString()}");
+      return [];
     }
     }
   }
   }
 
 
-  Future<List<CuratedObject>?> getCuratedObjects() async {
+  Future<List<CuratedObjectsResponseDto>?> getCuratedObjects() async {
     try {
     try {
-      var res = await _networkService.getRequest(url: "asset/allObjects");
-
-      List<dynamic> decodedData = jsonDecode(res.toString());
-
-      List<CuratedObject> result =
-          List.from(decodedData.map((a) => CuratedObject.fromMap(a)));
-
-      return result;
+      return await _apiService.assetApi.getCuratedObjects();
     } catch (e) {
     } catch (e) {
-      debugPrint("[ERROR] [CuratedObject] ${e.toString()}");
-      throw Error();
+      debugPrint("Error [getCuratedObjects] ${e.toString()}");
+      throw [];
     }
     }
   }
   }
 }
 }

+ 9 - 6
mobile/lib/modules/search/ui/search_bar.dart

@@ -5,9 +5,11 @@ 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;
@@ -26,7 +28,8 @@ class SearchBar extends HookConsumerWidget with PreferredSizeWidget {
                 ref.watch(searchPageStateProvider.notifier).disableSearch();
                 ref.watch(searchPageStateProvider.notifier).disableSearch();
                 searchTermController.clear();
                 searchTermController.clear();
               },
               },
-              icon: const Icon(Icons.arrow_back_ios_rounded))
+              icon: const Icon(Icons.arrow_back_ios_rounded),
+            )
           : const Icon(Icons.search_rounded),
           : const Icon(Icons.search_rounded),
       title: TextField(
       title: TextField(
         controller: searchTermController,
         controller: searchTermController,
@@ -50,10 +53,10 @@ class SearchBar extends HookConsumerWidget with PreferredSizeWidget {
         },
         },
         decoration: InputDecoration(
         decoration: InputDecoration(
           hintText: 'search_bar_hint'.tr(),
           hintText: 'search_bar_hint'.tr(),
-          enabledBorder: UnderlineInputBorder(
+          enabledBorder: const UnderlineInputBorder(
             borderSide: BorderSide(color: Colors.transparent),
             borderSide: BorderSide(color: Colors.transparent),
           ),
           ),
-          focusedBorder: UnderlineInputBorder(
+          focusedBorder: const UnderlineInputBorder(
             borderSide: BorderSide(color: Colors.transparent),
             borderSide: BorderSide(color: Colors.transparent),
           ),
           ),
         ),
         ),

+ 6 - 7
mobile/lib/modules/search/ui/thumbnail_with_info.dart

@@ -2,15 +2,14 @@ import 'package:cached_network_image/cached_network_image.dart';
 import 'package:flutter/material.dart';
 import 'package:flutter/material.dart';
 import 'package:hive_flutter/hive_flutter.dart';
 import 'package:hive_flutter/hive_flutter.dart';
 import 'package:immich_mobile/constants/hive_box.dart';
 import 'package:immich_mobile/constants/hive_box.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})
-      : super(key: key);
+  const ThumbnailWithInfo({
+    Key? key,
+    required this.textInfo,
+    required this.imageUrl,
+    required this.onTap,
+  }) : super(key: key);
 
 
   final String textInfo;
   final String textInfo;
   final String imageUrl;
   final String imageUrl;

+ 23 - 18
mobile/lib/modules/search/views/search_page.dart

@@ -5,8 +5,6 @@ import 'package:flutter_hooks/flutter_hooks.dart';
 import 'package:hive_flutter/hive_flutter.dart';
 import 'package:hive_flutter/hive_flutter.dart';
 import 'package:hooks_riverpod/hooks_riverpod.dart';
 import 'package:hooks_riverpod/hooks_riverpod.dart';
 import 'package:immich_mobile/constants/hive_box.dart';
 import 'package:immich_mobile/constants/hive_box.dart';
-import 'package:immich_mobile/modules/search/models/curated_location.model.dart';
-import 'package:immich_mobile/modules/search/models/curated_object.model.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';
 import 'package:immich_mobile/modules/search/ui/search_bar.dart';
 import 'package:immich_mobile/modules/search/ui/search_bar.dart';
 import 'package:immich_mobile/modules/search/ui/search_suggestion_list.dart';
 import 'package:immich_mobile/modules/search/ui/search_suggestion_list.dart';
@@ -14,6 +12,7 @@ import 'package:immich_mobile/modules/search/ui/thumbnail_with_info.dart';
 import 'package:immich_mobile/routing/router.dart';
 import 'package:immich_mobile/routing/router.dart';
 import 'package:immich_mobile/shared/ui/immich_loading_indicator.dart';
 import 'package:immich_mobile/shared/ui/immich_loading_indicator.dart';
 import 'package:immich_mobile/utils/capitalize_first_letter.dart';
 import 'package:immich_mobile/utils/capitalize_first_letter.dart';
+import 'package:openapi/api.dart';
 
 
 // ignore: must_be_immutable
 // ignore: must_be_immutable
 class SearchPage extends HookConsumerWidget {
 class SearchPage extends HookConsumerWidget {
@@ -25,15 +24,18 @@ class SearchPage extends HookConsumerWidget {
   Widget build(BuildContext context, WidgetRef ref) {
   Widget build(BuildContext context, WidgetRef ref) {
     var box = Hive.box(userInfoBox);
     var box = Hive.box(userInfoBox);
     final isSearchEnabled = ref.watch(searchPageStateProvider).isSearchEnabled;
     final isSearchEnabled = ref.watch(searchPageStateProvider).isSearchEnabled;
-    AsyncValue<List<CuratedLocation>> curatedLocation =
+    AsyncValue<List<CuratedLocationsResponseDto>> curatedLocation =
         ref.watch(getCuratedLocationProvider);
         ref.watch(getCuratedLocationProvider);
-    AsyncValue<List<CuratedObject>> curatedObjects =
+    AsyncValue<List<CuratedObjectsResponseDto>> curatedObjects =
         ref.watch(getCuratedObjectProvider);
         ref.watch(getCuratedObjectProvider);
 
 
-    useEffect(() {
-      searchFocusNode = FocusNode();
-      return () => searchFocusNode.dispose();
-    }, []);
+    useEffect(
+      () {
+        searchFocusNode = FocusNode();
+        return () => searchFocusNode.dispose();
+      },
+      [],
+    );
 
 
     _onSearchSubmitted(String searchTerm) async {
     _onSearchSubmitted(String searchTerm) async {
       searchFocusNode.unfocus();
       searchFocusNode.unfocus();
@@ -58,16 +60,16 @@ class SearchPage extends HookConsumerWidget {
                     scrollDirection: Axis.horizontal,
                     scrollDirection: Axis.horizontal,
                     itemCount: curatedLocation.value?.length,
                     itemCount: curatedLocation.value?.length,
                     itemBuilder: ((context, index) {
                     itemBuilder: ((context, index) {
-                      CuratedLocation locationInfo = curatedLocations[index];
+                      var locationInfo = curatedLocations[index];
                       var thumbnailRequestUrl =
                       var thumbnailRequestUrl =
-                          '${box.get(serverEndpointKey)}/asset/file?aid=${locationInfo.deviceAssetId}&did=${locationInfo.deviceId}&isThumb=true';
-
+                          '${box.get(serverEndpointKey)}/asset/thumbnail/${locationInfo.id}';
                       return ThumbnailWithInfo(
                       return ThumbnailWithInfo(
                         imageUrl: thumbnailRequestUrl,
                         imageUrl: thumbnailRequestUrl,
                         textInfo: locationInfo.city,
                         textInfo: locationInfo.city,
                         onTap: () {
                         onTap: () {
                           AutoRouter.of(context).push(
                           AutoRouter.of(context).push(
-                              SearchResultRoute(searchTerm: locationInfo.city));
+                            SearchResultRoute(searchTerm: locationInfo.city),
+                          );
                         },
                         },
                       );
                       );
                     }),
                     }),
@@ -109,7 +111,7 @@ class SearchPage extends HookConsumerWidget {
                     scrollDirection: Axis.horizontal,
                     scrollDirection: Axis.horizontal,
                     itemCount: curatedObjects.value?.length,
                     itemCount: curatedObjects.value?.length,
                     itemBuilder: ((context, index) {
                     itemBuilder: ((context, index) {
-                      CuratedObject curatedObjectInfo = objects[index];
+                      var curatedObjectInfo = objects[index];
                       var thumbnailRequestUrl =
                       var thumbnailRequestUrl =
                           '${box.get(serverEndpointKey)}/asset/file?aid=${curatedObjectInfo.deviceAssetId}&did=${curatedObjectInfo.deviceId}&isThumb=true';
                           '${box.get(serverEndpointKey)}/asset/file?aid=${curatedObjectInfo.deviceAssetId}&did=${curatedObjectInfo.deviceId}&isThumb=true';
 
 
@@ -117,9 +119,12 @@ class SearchPage extends HookConsumerWidget {
                         imageUrl: thumbnailRequestUrl,
                         imageUrl: thumbnailRequestUrl,
                         textInfo: curatedObjectInfo.object,
                         textInfo: curatedObjectInfo.object,
                         onTap: () {
                         onTap: () {
-                          AutoRouter.of(context).push(SearchResultRoute(
+                          AutoRouter.of(context).push(
+                            SearchResultRoute(
                               searchTerm: curatedObjectInfo.object
                               searchTerm: curatedObjectInfo.object
-                                  .capitalizeFirstLetter()));
+                                  .capitalizeFirstLetter(),
+                            ),
+                          );
                         },
                         },
                       );
                       );
                     }),
                     }),
@@ -160,7 +165,7 @@ class SearchPage extends HookConsumerWidget {
             ListView(
             ListView(
               children: [
               children: [
                 Padding(
                 Padding(
-                  padding: EdgeInsets.all(16.0),
+                  padding: const EdgeInsets.all(16.0),
                   child: const Text(
                   child: const Text(
                     "search_page_places",
                     "search_page_places",
                     style: TextStyle(fontWeight: FontWeight.bold, fontSize: 16),
                     style: TextStyle(fontWeight: FontWeight.bold, fontSize: 16),
@@ -168,8 +173,8 @@ class SearchPage extends HookConsumerWidget {
                 ),
                 ),
                 _buildPlaces(),
                 _buildPlaces(),
                 Padding(
                 Padding(
-                  padding: EdgeInsets.all(16.0),
-                  child: const  Text(
+                  padding: const EdgeInsets.all(16.0),
+                  child: const Text(
                     "search_page_things",
                     "search_page_things",
                     style: TextStyle(fontWeight: FontWeight.bold, fontSize: 16),
                     style: TextStyle(fontWeight: FontWeight.bold, fontSize: 16),
                   ).tr(),
                   ).tr(),

+ 28 - 20
mobile/lib/modules/search/views/search_result_page.dart

@@ -29,13 +29,18 @@ class SearchResultPage extends HookConsumerWidget {
 
 
     late FocusNode searchFocusNode;
     late FocusNode searchFocusNode;
 
 
-    useEffect(() {
-      searchFocusNode = FocusNode();
-
-      Future.delayed(Duration.zero,
-          () => ref.read(searchResultPageProvider.notifier).search(searchTerm));
-      return () => searchFocusNode.dispose();
-    }, []);
+    useEffect(
+      () {
+        searchFocusNode = FocusNode();
+
+        Future.delayed(
+          Duration.zero,
+          () => ref.read(searchResultPageProvider.notifier).search(searchTerm),
+        );
+        return () => searchFocusNode.dispose();
+      },
+      [],
+    );
 
 
     _onSearchSubmitted(String newSearchTerm) {
     _onSearchSubmitted(String newSearchTerm) {
       debugPrint("Re-Search with $newSearchTerm");
       debugPrint("Re-Search with $newSearchTerm");
@@ -69,10 +74,10 @@ class SearchResultPage extends HookConsumerWidget {
         },
         },
         decoration: InputDecoration(
         decoration: InputDecoration(
           hintText: 'search_result_page_new_search_hint'.tr(),
           hintText: 'search_result_page_new_search_hint'.tr(),
-          enabledBorder: UnderlineInputBorder(
+          enabledBorder: const UnderlineInputBorder(
             borderSide: BorderSide(color: Colors.transparent),
             borderSide: BorderSide(color: Colors.transparent),
           ),
           ),
-          focusedBorder: UnderlineInputBorder(
+          focusedBorder: const UnderlineInputBorder(
             borderSide: BorderSide(color: Colors.transparent),
             borderSide: BorderSide(color: Colors.transparent),
           ),
           ),
         ),
         ),
@@ -90,9 +95,10 @@ class SearchResultPage extends HookConsumerWidget {
             Text(
             Text(
               currentSearchTerm.value,
               currentSearchTerm.value,
               style: TextStyle(
               style: TextStyle(
-                  color: Theme.of(context).primaryColor,
-                  fontSize: 13,
-                  fontWeight: FontWeight.bold),
+                color: Theme.of(context).primaryColor,
+                fontSize: 13,
+                fontWeight: FontWeight.bold,
+              ),
               maxLines: 1,
               maxLines: 1,
             ),
             ),
             Icon(
             Icon(
@@ -116,9 +122,10 @@ class SearchResultPage extends HookConsumerWidget {
 
 
       if (searchResultPageState.isLoading) {
       if (searchResultPageState.isLoading) {
         return Center(
         return Center(
-            child: SpinKitDancingSquare(
-          color: Theme.of(context).primaryColor,
-        ));
+          child: SpinKitDancingSquare(
+            color: Theme.of(context).primaryColor,
+          ),
+        );
       }
       }
 
 
       if (searchResultPageState.isSuccess) {
       if (searchResultPageState.isSuccess) {
@@ -184,11 +191,12 @@ class SearchResultPage extends HookConsumerWidget {
           icon: const Icon(Icons.arrow_back_ios_rounded),
           icon: const Icon(Icons.arrow_back_ios_rounded),
         ),
         ),
         title: GestureDetector(
         title: GestureDetector(
-            onTap: () {
-              isNewSearch.value = true;
-              searchFocusNode.requestFocus();
-            },
-            child: isNewSearch.value ? _buildTextField() : _buildChip()),
+          onTap: () {
+            isNewSearch.value = true;
+            searchFocusNode.requestFocus();
+          },
+          child: isNewSearch.value ? _buildTextField() : _buildChip(),
+        ),
         centerTitle: false,
         centerTitle: false,
       ),
       ),
       body: GestureDetector(
       body: GestureDetector(

+ 5 - 36
mobile/lib/modules/sharing/models/asset_selection_page_result.model.dart

@@ -1,12 +1,10 @@
-import 'dart:convert';
-
 import 'package:collection/collection.dart';
 import 'package:collection/collection.dart';
 
 
-import 'package:immich_mobile/shared/models/immich_asset.model.dart';
+import 'package:openapi/api.dart';
 
 
 class AssetSelectionPageResult {
 class AssetSelectionPageResult {
-  final Set<ImmichAsset> selectedNewAsset;
-  final Set<ImmichAsset> selectedAdditionalAsset;
+  final Set<AssetResponseDto> selectedNewAsset;
+  final Set<AssetResponseDto> selectedAdditionalAsset;
   final bool isAlbumExist;
   final bool isAlbumExist;
 
 
   AssetSelectionPageResult({
   AssetSelectionPageResult({
@@ -16,8 +14,8 @@ class AssetSelectionPageResult {
   });
   });
 
 
   AssetSelectionPageResult copyWith({
   AssetSelectionPageResult copyWith({
-    Set<ImmichAsset>? selectedNewAsset,
-    Set<ImmichAsset>? selectedAdditionalAsset,
+    Set<AssetResponseDto>? selectedNewAsset,
+    Set<AssetResponseDto>? selectedAdditionalAsset,
     bool? isAlbumExist,
     bool? isAlbumExist,
   }) {
   }) {
     return AssetSelectionPageResult(
     return AssetSelectionPageResult(
@@ -28,35 +26,6 @@ class AssetSelectionPageResult {
     );
     );
   }
   }
 
 
-  Map<String, dynamic> toMap() {
-    final result = <String, dynamic>{};
-
-    result.addAll(
-        {'selectedNewAsset': selectedNewAsset.map((x) => x.toMap()).toList()});
-    result.addAll({
-      'selectedAdditionalAsset':
-          selectedAdditionalAsset.map((x) => x.toMap()).toList()
-    });
-    result.addAll({'isAlbumExist': isAlbumExist});
-
-    return result;
-  }
-
-  factory AssetSelectionPageResult.fromMap(Map<String, dynamic> map) {
-    return AssetSelectionPageResult(
-      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,
-    );
-  }
-
-  String toJson() => json.encode(toMap());
-
-  factory AssetSelectionPageResult.fromJson(String source) =>
-      AssetSelectionPageResult.fromMap(json.decode(source));
-
   @override
   @override
   String toString() =>
   String toString() =>
       'AssetSelectionPageResult(selectedNewAsset: $selectedNewAsset, selectedAdditionalAsset: $selectedAdditionalAsset, isAlbumExist: $isAlbumExist)';
       'AssetSelectionPageResult(selectedNewAsset: $selectedNewAsset, selectedAdditionalAsset: $selectedAdditionalAsset, isAlbumExist: $isAlbumExist)';

+ 14 - 55
mobile/lib/modules/sharing/models/asset_selection_state.model.dart

@@ -1,14 +1,12 @@
-import 'dart:convert';
-
 import 'package:collection/collection.dart';
 import 'package:collection/collection.dart';
 
 
-import 'package:immich_mobile/shared/models/immich_asset.model.dart';
+import 'package:openapi/api.dart';
 
 
 class AssetSelectionState {
 class AssetSelectionState {
   final Set<String> selectedMonths;
   final Set<String> selectedMonths;
-  final Set<ImmichAsset> selectedNewAssetsForAlbum;
-  final Set<ImmichAsset> selectedAdditionalAssetsForAlbum;
-  final Set<ImmichAsset> selectedAssetsInAlbumViewer;
+  final Set<AssetResponseDto> selectedNewAssetsForAlbum;
+  final Set<AssetResponseDto> selectedAdditionalAssetsForAlbum;
+  final Set<AssetResponseDto> selectedAssetsInAlbumViewer;
   final bool isMultiselectEnable;
   final bool isMultiselectEnable;
 
 
   /// Indicate the asset selection page is navigated from existing album
   /// Indicate the asset selection page is navigated from existing album
@@ -24,9 +22,9 @@ class AssetSelectionState {
 
 
   AssetSelectionState copyWith({
   AssetSelectionState copyWith({
     Set<String>? selectedMonths,
     Set<String>? selectedMonths,
-    Set<ImmichAsset>? selectedNewAssetsForAlbum,
-    Set<ImmichAsset>? selectedAdditionalAssetsForAlbum,
-    Set<ImmichAsset>? selectedAssetsInAlbumViewer,
+    Set<AssetResponseDto>? selectedNewAssetsForAlbum,
+    Set<AssetResponseDto>? selectedAdditionalAssetsForAlbum,
+    Set<AssetResponseDto>? selectedAssetsInAlbumViewer,
     bool? isMultiselectEnable,
     bool? isMultiselectEnable,
     bool? isAlbumExist,
     bool? isAlbumExist,
   }) {
   }) {
@@ -43,49 +41,6 @@ class AssetSelectionState {
     );
     );
   }
   }
 
 
-  Map<String, dynamic> toMap() {
-    final result = <String, dynamic>{};
-
-    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({'isMultiselectEnable': isMultiselectEnable});
-    result.addAll({'isAlbumExist': isAlbumExist});
-
-    return result;
-  }
-
-  factory AssetSelectionState.fromMap(Map<String, dynamic> map) {
-    return AssetSelectionState(
-      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))),
-      isMultiselectEnable: map['isMultiselectEnable'] ?? false,
-      isAlbumExist: map['isAlbumExist'] ?? false,
-    );
-  }
-
-  String toJson() => json.encode(toMap());
-
-  factory AssetSelectionState.fromJson(String source) =>
-      AssetSelectionState.fromMap(json.decode(source));
-
   @override
   @override
   String toString() {
   String toString() {
     return 'AssetSelectionState(selectedMonths: $selectedMonths, selectedNewAssetsForAlbum: $selectedNewAssetsForAlbum, selectedAdditionalAssetsForAlbum: $selectedAdditionalAssetsForAlbum, selectedAssetsInAlbumViewer: $selectedAssetsInAlbumViewer, isMultiselectEnable: $isMultiselectEnable, isAlbumExist: $isAlbumExist)';
     return 'AssetSelectionState(selectedMonths: $selectedMonths, selectedNewAssetsForAlbum: $selectedNewAssetsForAlbum, selectedAdditionalAssetsForAlbum: $selectedAdditionalAssetsForAlbum, selectedAssetsInAlbumViewer: $selectedAssetsInAlbumViewer, isMultiselectEnable: $isMultiselectEnable, isAlbumExist: $isAlbumExist)';
@@ -99,10 +54,14 @@ 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(
         setEquals(
-            other.selectedAssetsInAlbumViewer, selectedAssetsInAlbumViewer) &&
+          other.selectedAdditionalAssetsForAlbum,
+          selectedAdditionalAssetsForAlbum,
+        ) &&
+        setEquals(
+          other.selectedAssetsInAlbumViewer,
+          selectedAssetsInAlbumViewer,
+        ) &&
         other.isMultiselectEnable == isMultiselectEnable &&
         other.isMultiselectEnable == isMultiselectEnable &&
         other.isAlbumExist == isAlbumExist;
         other.isAlbumExist == isAlbumExist;
   }
   }

+ 0 - 117
mobile/lib/modules/sharing/models/shared_album.model.dart

@@ -1,117 +0,0 @@
-import 'dart:convert';
-
-import 'package:collection/collection.dart';
-
-import 'package:immich_mobile/shared/models/immich_asset.model.dart';
-import 'package:immich_mobile/shared/models/user.model.dart';
-
-class SharedAlbum {
-  final String id;
-  final String ownerId;
-  final String albumName;
-  final String createdAt;
-  final String? albumThumbnailAssetId;
-  final List<User> sharedUsers;
-  final List<ImmichAsset>? assets;
-
-  SharedAlbum({
-    required this.id,
-    required this.ownerId,
-    required this.albumName,
-    required this.createdAt,
-    required this.albumThumbnailAssetId,
-    required this.sharedUsers,
-    this.assets,
-  });
-
-  SharedAlbum copyWith({
-    String? id,
-    String? ownerId,
-    String? albumName,
-    String? createdAt,
-    String? albumThumbnailAssetId,
-    List<User>? sharedUsers,
-    List<ImmichAsset>? assets,
-  }) {
-    return SharedAlbum(
-      id: id ?? this.id,
-      ownerId: ownerId ?? this.ownerId,
-      albumName: albumName ?? this.albumName,
-      createdAt: createdAt ?? this.createdAt,
-      albumThumbnailAssetId:
-          albumThumbnailAssetId ?? this.albumThumbnailAssetId,
-      sharedUsers: sharedUsers ?? this.sharedUsers,
-      assets: assets ?? this.assets,
-    );
-  }
-
-  Map<String, dynamic> toMap() {
-    final result = <String, dynamic>{};
-
-    result.addAll({'id': id});
-    result.addAll({'ownerId': ownerId});
-    result.addAll({'albumName': albumName});
-    result.addAll({'createdAt': createdAt});
-    if (albumThumbnailAssetId != null) {
-      result.addAll({'albumThumbnailAssetId': albumThumbnailAssetId});
-    }
-    result.addAll({'sharedUsers': sharedUsers.map((x) => x.toMap()).toList()});
-    if (assets != null) {
-      result.addAll({'assets': assets!.map((x) => x.toMap()).toList()});
-    }
-
-    return result;
-  }
-
-  factory SharedAlbum.fromMap(Map<String, dynamic> map) {
-    return SharedAlbum(
-      id: map['id'] ?? '',
-      ownerId: map['ownerId'] ?? '',
-      albumName: map['albumName'] ?? '',
-      createdAt: map['createdAt'] ?? '',
-      albumThumbnailAssetId: map['albumThumbnailAssetId'],
-      sharedUsers:
-          List<User>.from(map['sharedUsers']?.map((x) => User.fromMap(x))),
-      assets: map['assets'] != null
-          ? List<ImmichAsset>.from(
-              map['assets']?.map((x) => ImmichAsset.fromMap(x)))
-          : null,
-    );
-  }
-
-  String toJson() => json.encode(toMap());
-
-  factory SharedAlbum.fromJson(String source) =>
-      SharedAlbum.fromMap(json.decode(source));
-
-  @override
-  String toString() {
-    return 'SharedAlbum(id: $id, ownerId: $ownerId, albumName: $albumName, createdAt: $createdAt, albumThumbnailAssetId: $albumThumbnailAssetId, sharedUsers: $sharedUsers, assets: $assets)';
-  }
-
-  @override
-  bool operator ==(Object other) {
-    if (identical(this, other)) return true;
-    final listEquals = const DeepCollectionEquality().equals;
-
-    return other is SharedAlbum &&
-        other.id == id &&
-        other.ownerId == ownerId &&
-        other.albumName == albumName &&
-        other.createdAt == createdAt &&
-        other.albumThumbnailAssetId == albumThumbnailAssetId &&
-        listEquals(other.sharedUsers, sharedUsers) &&
-        listEquals(other.assets, assets);
-  }
-
-  @override
-  int get hashCode {
-    return id.hashCode ^
-        ownerId.hashCode ^
-        albumName.hashCode ^
-        createdAt.hashCode ^
-        albumThumbnailAssetId.hashCode ^
-        sharedUsers.hashCode ^
-        assets.hashCode;
-  }
-}

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

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

+ 4 - 1
mobile/lib/modules/sharing/providers/album_viewer.provider.dart

@@ -30,7 +30,10 @@ class AlbumViewerNotifier extends StateNotifier<AlbumViewerPageState> {
   }
   }
 
 
   Future<bool> changeAlbumTitle(
   Future<bool> changeAlbumTitle(
-      String albumId, String ownerId, String newAlbumTitle) async {
+    String albumId,
+    String ownerId,
+    String newAlbumTitle,
+  ) async {
     SharedAlbumService service = ref.watch(sharedAlbumServiceProvider);
     SharedAlbumService service = ref.watch(sharedAlbumServiceProvider);
 
 
     bool isSuccess =
     bool isSuccess =

+ 32 - 27
mobile/lib/modules/sharing/providers/asset_selection.provider.dart

@@ -1,41 +1,46 @@
 import 'package:hooks_riverpod/hooks_riverpod.dart';
 import 'package:hooks_riverpod/hooks_riverpod.dart';
 import 'package:immich_mobile/modules/sharing/models/asset_selection_state.model.dart';
 import 'package:immich_mobile/modules/sharing/models/asset_selection_state.model.dart';
 
 
-import 'package:immich_mobile/shared/models/immich_asset.model.dart';
+import 'package:openapi/api.dart';
 
 
 class AssetSelectionNotifier extends StateNotifier<AssetSelectionState> {
 class AssetSelectionNotifier extends StateNotifier<AssetSelectionState> {
   AssetSelectionNotifier()
   AssetSelectionNotifier()
-      : super(AssetSelectionState(
-          selectedNewAssetsForAlbum: {},
-          selectedMonths: {},
-          selectedAdditionalAssetsForAlbum: {},
-          selectedAssetsInAlbumViewer: {},
-          isAlbumExist: false,
-          isMultiselectEnable: false,
-        ));
+      : super(
+          AssetSelectionState(
+            selectedNewAssetsForAlbum: {},
+            selectedMonths: {},
+            selectedAdditionalAssetsForAlbum: {},
+            selectedAssetsInAlbumViewer: {},
+            isAlbumExist: false,
+            isMultiselectEnable: false,
+          ),
+        );
 
 
   void setIsAlbumExist(bool isAlbumExist) {
   void setIsAlbumExist(bool isAlbumExist) {
     state = state.copyWith(isAlbumExist: isAlbumExist);
     state = state.copyWith(isAlbumExist: isAlbumExist);
   }
   }
 
 
   void removeAssetsInMonth(
   void removeAssetsInMonth(
-      String removedMonth, List<ImmichAsset> assetsInMonth) {
-    Set<ImmichAsset> currentAssetList = state.selectedNewAssetsForAlbum;
+    String removedMonth,
+    List<AssetResponseDto> assetsInMonth,
+  ) {
+    Set<AssetResponseDto> currentAssetList = state.selectedNewAssetsForAlbum;
     Set<String> currentMonthList = state.selectedMonths;
     Set<String> currentMonthList = state.selectedMonths;
 
 
     currentMonthList
     currentMonthList
         .removeWhere((selectedMonth) => selectedMonth == removedMonth);
         .removeWhere((selectedMonth) => selectedMonth == removedMonth);
 
 
-    for (ImmichAsset asset in assetsInMonth) {
+    for (AssetResponseDto asset in assetsInMonth) {
       currentAssetList.removeWhere((e) => e.id == asset.id);
       currentAssetList.removeWhere((e) => e.id == asset.id);
     }
     }
 
 
     state = state.copyWith(
     state = state.copyWith(
-        selectedNewAssetsForAlbum: currentAssetList,
-        selectedMonths: currentMonthList);
+      selectedNewAssetsForAlbum: currentAssetList,
+      selectedMonths: currentMonthList,
+    );
   }
   }
 
 
-  void addAdditionalAssets(List<ImmichAsset> assets) {
+  void addAdditionalAssets(List<AssetResponseDto> assets) {
     state = state.copyWith(
     state = state.copyWith(
       selectedAdditionalAssetsForAlbum: {
       selectedAdditionalAssetsForAlbum: {
         ...state.selectedAdditionalAssetsForAlbum,
         ...state.selectedAdditionalAssetsForAlbum,
@@ -44,7 +49,7 @@ class AssetSelectionNotifier extends StateNotifier<AssetSelectionState> {
     );
     );
   }
   }
 
 
-  void addAllAssetsInMonth(String month, List<ImmichAsset> assetsInMonth) {
+  void addAllAssetsInMonth(String month, List<AssetResponseDto> assetsInMonth) {
     state = state.copyWith(
     state = state.copyWith(
       selectedMonths: {...state.selectedMonths, month},
       selectedMonths: {...state.selectedMonths, month},
       selectedNewAssetsForAlbum: {
       selectedNewAssetsForAlbum: {
@@ -54,7 +59,7 @@ class AssetSelectionNotifier extends StateNotifier<AssetSelectionState> {
     );
     );
   }
   }
 
 
-  void addNewAssets(List<ImmichAsset> assets) {
+  void addNewAssets(List<AssetResponseDto> assets) {
     state = state.copyWith(
     state = state.copyWith(
       selectedNewAssetsForAlbum: {
       selectedNewAssetsForAlbum: {
         ...state.selectedNewAssetsForAlbum,
         ...state.selectedNewAssetsForAlbum,
@@ -63,20 +68,20 @@ class AssetSelectionNotifier extends StateNotifier<AssetSelectionState> {
     );
     );
   }
   }
 
 
-  void removeSelectedNewAssets(List<ImmichAsset> assets) {
-    Set<ImmichAsset> currentList = state.selectedNewAssetsForAlbum;
+  void removeSelectedNewAssets(List<AssetResponseDto> assets) {
+    Set<AssetResponseDto> currentList = state.selectedNewAssetsForAlbum;
 
 
-    for (ImmichAsset asset in assets) {
+    for (AssetResponseDto asset in assets) {
       currentList.removeWhere((e) => e.id == asset.id);
       currentList.removeWhere((e) => e.id == asset.id);
     }
     }
 
 
     state = state.copyWith(selectedNewAssetsForAlbum: currentList);
     state = state.copyWith(selectedNewAssetsForAlbum: currentList);
   }
   }
 
 
-  void removeSelectedAdditionalAssets(List<ImmichAsset> assets) {
-    Set<ImmichAsset> currentList = state.selectedAdditionalAssetsForAlbum;
+  void removeSelectedAdditionalAssets(List<AssetResponseDto> assets) {
+    Set<AssetResponseDto> currentList = state.selectedAdditionalAssetsForAlbum;
 
 
-    for (ImmichAsset asset in assets) {
+    for (AssetResponseDto asset in assets) {
       currentList.removeWhere((e) => e.id == asset.id);
       currentList.removeWhere((e) => e.id == asset.id);
     }
     }
 
 
@@ -104,7 +109,7 @@ class AssetSelectionNotifier extends StateNotifier<AssetSelectionState> {
     );
     );
   }
   }
 
 
-  void addAssetsInAlbumViewer(List<ImmichAsset> assets) {
+  void addAssetsInAlbumViewer(List<AssetResponseDto> assets) {
     state = state.copyWith(
     state = state.copyWith(
       selectedAssetsInAlbumViewer: {
       selectedAssetsInAlbumViewer: {
         ...state.selectedAssetsInAlbumViewer,
         ...state.selectedAssetsInAlbumViewer,
@@ -113,10 +118,10 @@ class AssetSelectionNotifier extends StateNotifier<AssetSelectionState> {
     );
     );
   }
   }
 
 
-  void removeAssetsInAlbumViewer(List<ImmichAsset> assets) {
-    Set<ImmichAsset> currentList = state.selectedAssetsInAlbumViewer;
+  void removeAssetsInAlbumViewer(List<AssetResponseDto> assets) {
+    Set<AssetResponseDto> currentList = state.selectedAssetsInAlbumViewer;
 
 
-    for (ImmichAsset asset in assets) {
+    for (AssetResponseDto asset in assets) {
       currentList.removeWhere((e) => e.id == asset.id);
       currentList.removeWhere((e) => e.id == asset.id);
     }
     }
 
 

+ 11 - 7
mobile/lib/modules/sharing/providers/shared_album.provider.dart

@@ -1,17 +1,19 @@
 import 'package:hooks_riverpod/hooks_riverpod.dart';
 import 'package:hooks_riverpod/hooks_riverpod.dart';
-import 'package:immich_mobile/modules/sharing/models/shared_album.model.dart';
 import 'package:immich_mobile/modules/sharing/services/shared_album.service.dart';
 import 'package:immich_mobile/modules/sharing/services/shared_album.service.dart';
+import 'package:openapi/api.dart';
 
 
-class SharedAlbumNotifier extends StateNotifier<List<SharedAlbum>> {
+class SharedAlbumNotifier extends StateNotifier<List<AlbumResponseDto>> {
   SharedAlbumNotifier(this._sharedAlbumService) : super([]);
   SharedAlbumNotifier(this._sharedAlbumService) : super([]);
 
 
   final SharedAlbumService _sharedAlbumService;
   final SharedAlbumService _sharedAlbumService;
 
 
   getAllSharedAlbums() async {
   getAllSharedAlbums() async {
-    List<SharedAlbum> sharedAlbums =
+    List<AlbumResponseDto>? sharedAlbums =
         await _sharedAlbumService.getAllSharedAlbum();
         await _sharedAlbumService.getAllSharedAlbum();
 
 
-    state = sharedAlbums;
+    if (sharedAlbums != null) {
+      state = sharedAlbums;
+    }
   }
   }
 
 
   Future<bool> deleteAlbum(String albumId) async {
   Future<bool> deleteAlbum(String albumId) async {
@@ -37,7 +39,9 @@ class SharedAlbumNotifier extends StateNotifier<List<SharedAlbum>> {
   }
   }
 
 
   Future<bool> removeAssetFromAlbum(
   Future<bool> removeAssetFromAlbum(
-      String albumId, List<String> assetIds) async {
+    String albumId,
+    List<String> assetIds,
+  ) async {
     var res = await _sharedAlbumService.removeAssetFromAlbum(albumId, assetIds);
     var res = await _sharedAlbumService.removeAssetFromAlbum(albumId, assetIds);
 
 
     if (res) {
     if (res) {
@@ -49,12 +53,12 @@ class SharedAlbumNotifier extends StateNotifier<List<SharedAlbum>> {
 }
 }
 
 
 final sharedAlbumProvider =
 final sharedAlbumProvider =
-    StateNotifierProvider<SharedAlbumNotifier, List<SharedAlbum>>((ref) {
+    StateNotifierProvider<SharedAlbumNotifier, List<AlbumResponseDto>>((ref) {
   return SharedAlbumNotifier(ref.watch(sharedAlbumServiceProvider));
   return SharedAlbumNotifier(ref.watch(sharedAlbumServiceProvider));
 });
 });
 
 
 final sharedAlbumDetailProvider = FutureProvider.autoDispose
 final sharedAlbumDetailProvider = FutureProvider.autoDispose
-    .family<SharedAlbum, String>((ref, albumId) async {
+    .family<AlbumResponseDto?, String>((ref, albumId) async {
   final SharedAlbumService sharedAlbumService =
   final SharedAlbumService sharedAlbumService =
       ref.watch(sharedAlbumServiceProvider);
       ref.watch(sharedAlbumServiceProvider);
 
 

+ 3 - 3
mobile/lib/modules/sharing/providers/suggested_shared_users.provider.dart

@@ -1,10 +1,10 @@
 import 'package:hooks_riverpod/hooks_riverpod.dart';
 import 'package:hooks_riverpod/hooks_riverpod.dart';
-import 'package:immich_mobile/shared/models/user.model.dart';
 import 'package:immich_mobile/shared/services/user.service.dart';
 import 'package:immich_mobile/shared/services/user.service.dart';
+import 'package:openapi/api.dart';
 
 
 final suggestedSharedUsersProvider =
 final suggestedSharedUsersProvider =
-    FutureProvider.autoDispose<List<User>>((ref) async {
+    FutureProvider.autoDispose<List<UserResponseDto>>((ref) async {
   UserService userService = ref.watch(userServiceProvider);
   UserService userService = ref.watch(userServiceProvider);
 
 
-  return await userService.getAllUsersInfo();
+  return await userService.getAllUsersInfo(isAll: false) ?? [];
 });
 });

+ 65 - 78
mobile/lib/modules/sharing/services/shared_album.service.dart

@@ -1,73 +1,69 @@
 import 'dart:async';
 import 'dart:async';
-import 'dart:convert';
 
 
-import 'package:dio/dio.dart';
 import 'package:flutter/foundation.dart';
 import 'package:flutter/foundation.dart';
 import 'package:hooks_riverpod/hooks_riverpod.dart';
 import 'package:hooks_riverpod/hooks_riverpod.dart';
-import 'package:immich_mobile/modules/sharing/models/shared_album.model.dart';
-import 'package:immich_mobile/shared/models/immich_asset.model.dart';
-import 'package:immich_mobile/shared/services/network.service.dart';
+import 'package:immich_mobile/shared/services/api.service.dart';
+import 'package:openapi/api.dart';
 
 
-final sharedAlbumServiceProvider =
-    Provider((ref) => SharedAlbumService(ref.watch(networkServiceProvider)));
+final sharedAlbumServiceProvider = Provider(
+  (ref) => SharedAlbumService(
+    ref.watch(apiServiceProvider),
+  ),
+);
 
 
 class SharedAlbumService {
 class SharedAlbumService {
-  final NetworkService _networkService;
-  SharedAlbumService(this._networkService);
+  final ApiService _apiService;
+  SharedAlbumService(this._apiService);
 
 
-  Future<List<SharedAlbum>> getAllSharedAlbum() async {
+  Future<List<AlbumResponseDto>?> getAllSharedAlbum() async {
     try {
     try {
-      var res = await _networkService.getRequest(url: 'album?shared=true');
-      List<dynamic> decodedData = jsonDecode(res.toString());
-      List<SharedAlbum> result =
-          List.from(decodedData.map((e) => SharedAlbum.fromMap(e)));
-
-      return result;
+      return await _apiService.albumApi.getAllAlbums(shared: true);
     } catch (e) {
     } catch (e) {
       debugPrint("Error getAllSharedAlbum  ${e.toString()}");
       debugPrint("Error getAllSharedAlbum  ${e.toString()}");
+      return null;
     }
     }
-
-    return [];
   }
   }
 
 
-  Future<bool> createSharedAlbum(String albumName, Set<ImmichAsset> assets,
-      List<String> sharedUserIds) async {
+  Future<bool> createSharedAlbum(
+    String albumName,
+    Set<AssetResponseDto> assets,
+    List<String> sharedUserIds,
+  ) async {
     try {
     try {
-      var res = await _networkService.postRequest(url: 'album', data: {
-        "albumName": albumName,
-        "sharedWithUserIds": sharedUserIds,
-        "assetIds": assets.map((asset) => asset.id).toList(),
-      });
+      _apiService.albumApi.createAlbum(
+        CreateAlbumDto(
+          albumName: albumName,
+          assetIds: assets.map((asset) => asset.id).toList(),
+          sharedWithUserIds: sharedUserIds,
+        ),
+      );
 
 
-      return res != null;
+      return true;
     } catch (e) {
     } catch (e) {
       debugPrint("Error createSharedAlbum  ${e.toString()}");
       debugPrint("Error createSharedAlbum  ${e.toString()}");
       return false;
       return false;
     }
     }
   }
   }
 
 
-  Future<SharedAlbum> getAlbumDetail(String albumId) async {
+  Future<AlbumResponseDto?> getAlbumDetail(String albumId) async {
     try {
     try {
-      var res = await _networkService.getRequest(url: 'album/$albumId');
-      dynamic decodedData = jsonDecode(res.toString());
-      SharedAlbum result = SharedAlbum.fromMap(decodedData);
-
-      return result;
+      return await _apiService.albumApi.getAlbumInfo(albumId);
     } catch (e) {
     } catch (e) {
-      throw Exception('Error getAllSharedAlbum  ${e.toString()}');
+      debugPrint('Error [getAlbumDetail] ${e.toString()}');
+      return null;
     }
     }
   }
   }
 
 
   Future<bool> addAdditionalAssetToAlbum(
   Future<bool> addAdditionalAssetToAlbum(
-      Set<ImmichAsset> assets, String albumId) async {
+    Set<AssetResponseDto> assets,
+    String albumId,
+  ) async {
     try {
     try {
-      var res =
-          await _networkService.putRequest(url: 'album/$albumId/assets', data: {
-        "albumId": albumId,
-        "assetIds": assets.map((asset) => asset.id).toList(),
-      });
-
-      return res != null;
+      var result = await _apiService.albumApi.addAssetsToAlbum(
+        albumId,
+        AddAssetsDto(assetIds: assets.map((asset) => asset.id).toList()),
+      );
+      return result != null;
     } catch (e) {
     } catch (e) {
       debugPrint("Error addAdditionalAssetToAlbum  ${e.toString()}");
       debugPrint("Error addAdditionalAssetToAlbum  ${e.toString()}");
       return false;
       return false;
@@ -75,14 +71,16 @@ class SharedAlbumService {
   }
   }
 
 
   Future<bool> addAdditionalUserToAlbum(
   Future<bool> addAdditionalUserToAlbum(
-      List<String> sharedUserIds, String albumId) async {
+    List<String> sharedUserIds,
+    String albumId,
+  ) async {
     try {
     try {
-      var res =
-          await _networkService.putRequest(url: 'album/$albumId/users', data: {
-        "sharedUserIds": sharedUserIds,
-      });
+      var result = await _apiService.albumApi.addUsersToAlbum(
+        albumId,
+        AddUsersDto(sharedUserIds: sharedUserIds),
+      );
 
 
-      return res != null;
+      return result != null;
     } catch (e) {
     } catch (e) {
       debugPrint("Error addAdditionalUserToAlbum  ${e.toString()}");
       debugPrint("Error addAdditionalUserToAlbum  ${e.toString()}");
       return false;
       return false;
@@ -91,12 +89,7 @@ class SharedAlbumService {
 
 
   Future<bool> deleteAlbum(String albumId) async {
   Future<bool> deleteAlbum(String albumId) async {
     try {
     try {
-      Response res = await _networkService.deleteRequest(url: 'album/$albumId');
-
-      if (res.statusCode != 200) {
-        return false;
-      }
-
+      await _apiService.albumApi.deleteAlbum(albumId);
       return true;
       return true;
     } catch (e) {
     } catch (e) {
       debugPrint("Error deleteAlbum  ${e.toString()}");
       debugPrint("Error deleteAlbum  ${e.toString()}");
@@ -106,12 +99,7 @@ class SharedAlbumService {
 
 
   Future<bool> leaveAlbum(String albumId) async {
   Future<bool> leaveAlbum(String albumId) async {
     try {
     try {
-      Response res =
-          await _networkService.deleteRequest(url: 'album/$albumId/user/me');
-
-      if (res.statusCode != 200) {
-        return false;
-      }
+      await _apiService.albumApi.removeUserFromAlbum(albumId, "me");
 
 
       return true;
       return true;
     } catch (e) {
     } catch (e) {
@@ -121,16 +109,14 @@ class SharedAlbumService {
   }
   }
 
 
   Future<bool> removeAssetFromAlbum(
   Future<bool> removeAssetFromAlbum(
-      String albumId, List<String> assetIds) async {
+    String albumId,
+    List<String> assetIds,
+  ) async {
     try {
     try {
-      Response res = await _networkService
-          .deleteRequest(url: 'album/$albumId/assets', data: {
-        "assetIds": assetIds,
-      });
-
-      if (res.statusCode != 200) {
-        return false;
-      }
+      await _apiService.albumApi.removeAssetFromAlbum(
+        albumId,
+        RemoveAssetsDto(assetIds: assetIds),
+      );
 
 
       return true;
       return true;
     } catch (e) {
     } catch (e) {
@@ -140,17 +126,18 @@ class SharedAlbumService {
   }
   }
 
 
   Future<bool> changeTitleAlbum(
   Future<bool> changeTitleAlbum(
-      String albumId, String ownerId, String newAlbumTitle) async {
+    String albumId,
+    String ownerId,
+    String newAlbumTitle,
+  ) async {
     try {
     try {
-      Response res =
-          await _networkService.patchRequest(url: 'album/$albumId/', data: {
-        "ownerId": ownerId,
-        "albumName": newAlbumTitle,
-      });
-
-      if (res.statusCode != 200) {
-        return false;
-      }
+      await _apiService.albumApi.updateAlbumInfo(
+        albumId,
+        UpdateAlbumDto(
+          ownerId: ownerId,
+          albumName: newAlbumTitle,
+        ),
+      );
 
 
       return true;
       return true;
     } catch (e) {
     } catch (e) {

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

@@ -5,12 +5,12 @@ 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})
-      : super(key: key);
+  const AlbumActionOutlinedButton({
+    Key? key,
+    this.onPressed,
+    required this.labelText,
+    required this.iconData,
+  }) : super(key: key);
 
 
   @override
   @override
   Widget build(BuildContext context) {
   Widget build(BuildContext context) {
@@ -31,7 +31,10 @@ class AlbumActionOutlinedButton extends StatelessWidget {
         label: Text(
         label: Text(
           labelText,
           labelText,
           style: const TextStyle(
           style: const TextStyle(
-              fontSize: 12, fontWeight: FontWeight.bold, color: Colors.black87),
+            fontSize: 12,
+            fontWeight: FontWeight.bold,
+            color: Colors.black87,
+          ),
         ),
         ),
         onPressed: onPressed,
         onPressed: onPressed,
       ),
       ),

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

@@ -31,7 +31,10 @@ class AlbumTitleTextField extends ConsumerWidget {
       },
       },
       focusNode: albumTitleTextFieldFocusNode,
       focusNode: albumTitleTextFieldFocusNode,
       style: TextStyle(
       style: TextStyle(
-          fontSize: 28, color: Colors.grey[700], fontWeight: FontWeight.bold),
+        fontSize: 28,
+        color: Colors.grey[700],
+        fontWeight: FontWeight.bold,
+      ),
       controller: albumTitleController,
       controller: albumTitleController,
       onTap: () {
       onTap: () {
         isAlbumTitleTextFieldFocus.value = true;
         isAlbumTitleTextFieldFocus.value = true;

+ 5 - 5
mobile/lib/modules/sharing/ui/album_viewer_appbar.dart

@@ -4,24 +4,24 @@ import 'package:flutter/material.dart';
 import 'package:fluttertoast/fluttertoast.dart';
 import 'package:fluttertoast/fluttertoast.dart';
 import 'package:hooks_riverpod/hooks_riverpod.dart';
 import 'package:hooks_riverpod/hooks_riverpod.dart';
 import 'package:immich_mobile/constants/immich_colors.dart';
 import 'package:immich_mobile/constants/immich_colors.dart';
-import 'package:immich_mobile/modules/sharing/models/shared_album.model.dart';
 import 'package:immich_mobile/modules/sharing/providers/album_viewer.provider.dart';
 import 'package:immich_mobile/modules/sharing/providers/album_viewer.provider.dart';
 import 'package:immich_mobile/modules/sharing/providers/asset_selection.provider.dart';
 import 'package:immich_mobile/modules/sharing/providers/asset_selection.provider.dart';
 import 'package:immich_mobile/modules/sharing/providers/shared_album.provider.dart';
 import 'package:immich_mobile/modules/sharing/providers/shared_album.provider.dart';
 import 'package:immich_mobile/routing/router.dart';
 import 'package:immich_mobile/routing/router.dart';
 import 'package:immich_mobile/shared/ui/immich_toast.dart';
 import 'package:immich_mobile/shared/ui/immich_toast.dart';
 import 'package:immich_mobile/shared/views/immich_loading_overlay.dart';
 import 'package:immich_mobile/shared/views/immich_loading_overlay.dart';
+import 'package:openapi/api.dart';
 
 
 class AlbumViewerAppbar extends HookConsumerWidget with PreferredSizeWidget {
 class AlbumViewerAppbar extends HookConsumerWidget with PreferredSizeWidget {
   const AlbumViewerAppbar({
   const AlbumViewerAppbar({
     Key? key,
     Key? key,
-    required AsyncValue<SharedAlbum> albumInfo,
+    required AsyncValue<AlbumResponseDto?> albumInfo,
     required this.userId,
     required this.userId,
     required this.albumId,
     required this.albumId,
   })  : _albumInfo = albumInfo,
   })  : _albumInfo = albumInfo,
         super(key: key);
         super(key: key);
 
 
-  final AsyncValue<SharedAlbum> _albumInfo;
+  final AsyncValue<AlbumResponseDto?> _albumInfo;
   final String userId;
   final String userId;
   final String albumId;
   final String albumId;
 
 
@@ -105,7 +105,7 @@ class AlbumViewerAppbar extends HookConsumerWidget with PreferredSizeWidget {
 
 
     _buildBottomSheetActionButton() {
     _buildBottomSheetActionButton() {
       if (isMultiSelectionEnable) {
       if (isMultiSelectionEnable) {
-        if (_albumInfo.asData?.value.ownerId == userId) {
+        if (_albumInfo.asData?.value?.ownerId == userId) {
           return ListTile(
           return ListTile(
             leading: const Icon(Icons.delete_sweep_rounded),
             leading: const Icon(Icons.delete_sweep_rounded),
             title: const Text(
             title: const Text(
@@ -118,7 +118,7 @@ class AlbumViewerAppbar extends HookConsumerWidget with PreferredSizeWidget {
           return const SizedBox();
           return const SizedBox();
         }
         }
       } else {
       } else {
-        if (_albumInfo.asData?.value.ownerId == userId) {
+        if (_albumInfo.asData?.value?.ownerId == userId) {
           return ListTile(
           return ListTile(
             leading: const Icon(Icons.delete_forever_rounded),
             leading: const Icon(Icons.delete_forever_rounded),
             title: const Text(
             title: const Text(

+ 16 - 11
mobile/lib/modules/sharing/ui/album_viewer_editable_title.dart

@@ -2,15 +2,17 @@ import 'package:easy_localization/easy_localization.dart';
 import 'package:flutter/material.dart';
 import 'package:flutter/material.dart';
 import 'package:flutter_hooks/flutter_hooks.dart';
 import 'package:flutter_hooks/flutter_hooks.dart';
 import 'package:hooks_riverpod/hooks_riverpod.dart';
 import 'package:hooks_riverpod/hooks_riverpod.dart';
-import 'package:immich_mobile/modules/sharing/models/shared_album.model.dart';
 import 'package:immich_mobile/modules/sharing/providers/album_viewer.provider.dart';
 import 'package:immich_mobile/modules/sharing/providers/album_viewer.provider.dart';
+import 'package:openapi/api.dart';
 
 
 class AlbumViewerEditableTitle extends HookConsumerWidget {
 class AlbumViewerEditableTitle extends HookConsumerWidget {
-  final SharedAlbum albumInfo;
+  final AlbumResponseDto 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) {
@@ -24,12 +26,15 @@ class AlbumViewerEditableTitle extends HookConsumerWidget {
       }
       }
     }
     }
 
 
-    useEffect(() {
-      titleFocusNode.addListener(onFocusModeChange);
-      return () {
-        titleFocusNode.removeListener(onFocusModeChange);
-      };
-    }, []);
+    useEffect(
+      () {
+        titleFocusNode.addListener(onFocusModeChange);
+        return () {
+          titleFocusNode.removeListener(onFocusModeChange);
+        };
+      },
+      [],
+    );
 
 
     return TextField(
     return TextField(
       onChanged: (value) {
       onChanged: (value) {

+ 15 - 17
mobile/lib/modules/sharing/ui/album_viewer_thumbnail.dart

@@ -7,11 +7,11 @@ import 'package:hooks_riverpod/hooks_riverpod.dart';
 import 'package:immich_mobile/constants/hive_box.dart';
 import 'package:immich_mobile/constants/hive_box.dart';
 import 'package:immich_mobile/modules/login/providers/authentication.provider.dart';
 import 'package:immich_mobile/modules/login/providers/authentication.provider.dart';
 import 'package:immich_mobile/modules/sharing/providers/asset_selection.provider.dart';
 import 'package:immich_mobile/modules/sharing/providers/asset_selection.provider.dart';
-import 'package:immich_mobile/shared/models/immich_asset.model.dart';
 import 'package:immich_mobile/routing/router.dart';
 import 'package:immich_mobile/routing/router.dart';
+import 'package:openapi/api.dart';
 
 
 class AlbumViewerThumbnail extends HookConsumerWidget {
 class AlbumViewerThumbnail extends HookConsumerWidget {
-  final ImmichAsset asset;
+  final AssetResponseDto asset;
 
 
   const AlbumViewerThumbnail({Key? key, required this.asset}) : super(key: key);
   const AlbumViewerThumbnail({Key? key, required this.asset}) : super(key: key);
 
 
@@ -20,7 +20,7 @@ class AlbumViewerThumbnail extends HookConsumerWidget {
     final cacheKey = useState(1);
     final cacheKey = useState(1);
     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/thumbnail/${asset.id}';
     var deviceId = ref.watch(authenticationProvider).deviceId;
     var deviceId = ref.watch(authenticationProvider).deviceId;
     final selectedAssetsInAlbumViewer =
     final selectedAssetsInAlbumViewer =
         ref.watch(assetSelectionProvider).selectedAssetsInAlbumViewer;
         ref.watch(assetSelectionProvider).selectedAssetsInAlbumViewer;
@@ -28,7 +28,7 @@ class AlbumViewerThumbnail extends HookConsumerWidget {
         ref.watch(assetSelectionProvider).isMultiselectEnable;
         ref.watch(assetSelectionProvider).isMultiselectEnable;
 
 
     _viewAsset() {
     _viewAsset() {
-      if (asset.type == 'IMAGE') {
+      if (asset.type == AssetTypeEnum.IMAGE) {
         AutoRouter.of(context).push(
         AutoRouter.of(context).push(
           ImageViewerRoute(
           ImageViewerRoute(
             imageUrl:
             imageUrl:
@@ -41,9 +41,10 @@ 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}',
-              asset: asset),
+            videoUrl:
+                '${box.get(serverEndpointKey)}/asset/file?aid=${asset.deviceAssetId}&did=${asset.deviceId}',
+            asset: asset,
+          ),
         );
         );
       }
       }
     }
     }
@@ -170,16 +171,13 @@ class AlbumViewerThumbnail extends HookConsumerWidget {
     return GestureDetector(
     return GestureDetector(
       onTap: isMultiSelectionEnable ? _handleSelectionGesture : _viewAsset,
       onTap: isMultiSelectionEnable ? _handleSelectionGesture : _viewAsset,
       onLongPress: _enableMultiSelection,
       onLongPress: _enableMultiSelection,
-      child: Hero(
-        tag: asset.id,
-        child: Stack(
-          children: [
-            _buildThumbnailImage(),
-            _buildAssetStoreLocationIcon(),
-            if (asset.type != 'IMAGE') _buildVideoLabel(),
-            if (isMultiSelectionEnable) _buildAssetSelectionIcon(),
-          ],
-        ),
+      child: Stack(
+        children: [
+          _buildThumbnailImage(),
+          _buildAssetStoreLocationIcon(),
+          if (asset.type != AssetTypeEnum.IMAGE) _buildVideoLabel(),
+          if (isMultiSelectionEnable) _buildAssetSelectionIcon(),
+        ],
       ),
       ),
     );
     );
   }
   }

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

@@ -1,10 +1,10 @@
 import 'package:flutter/material.dart';
 import 'package:flutter/material.dart';
 import 'package:hooks_riverpod/hooks_riverpod.dart';
 import 'package:hooks_riverpod/hooks_riverpod.dart';
 import 'package:immich_mobile/modules/sharing/ui/selection_thumbnail_image.dart';
 import 'package:immich_mobile/modules/sharing/ui/selection_thumbnail_image.dart';
-import 'package:immich_mobile/shared/models/immich_asset.model.dart';
+import 'package:openapi/api.dart';
 
 
 class AssetGridByMonth extends HookConsumerWidget {
 class AssetGridByMonth extends HookConsumerWidget {
-  final List<ImmichAsset> assetGroup;
+  final List<AssetResponseDto> assetGroup;
   const AssetGridByMonth({Key? key, required this.assetGroup})
   const AssetGridByMonth({Key? key, required this.assetGroup})
       : super(key: key);
       : super(key: key);
   @override
   @override

+ 12 - 6
mobile/lib/modules/sharing/ui/month_group_title.dart

@@ -2,15 +2,17 @@ import 'package:flutter/material.dart';
 import 'package:flutter/services.dart';
 import 'package:flutter/services.dart';
 import 'package:hooks_riverpod/hooks_riverpod.dart';
 import 'package:hooks_riverpod/hooks_riverpod.dart';
 import 'package:immich_mobile/modules/sharing/providers/asset_selection.provider.dart';
 import 'package:immich_mobile/modules/sharing/providers/asset_selection.provider.dart';
-import 'package:immich_mobile/shared/models/immich_asset.model.dart';
+import 'package:openapi/api.dart';
 
 
 class MonthGroupTitle extends HookConsumerWidget {
 class MonthGroupTitle extends HookConsumerWidget {
   final String month;
   final String month;
-  final List<ImmichAsset> assetGroup;
+  final List<AssetResponseDto> 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) {
@@ -75,7 +77,11 @@ class MonthGroupTitle extends HookConsumerWidget {
     return SliverToBoxAdapter(
     return SliverToBoxAdapter(
       child: Padding(
       child: Padding(
         padding: const EdgeInsets.only(
         padding: const EdgeInsets.only(
-            top: 29.0, bottom: 29.0, left: 14.0, right: 8.0),
+          top: 29.0,
+          bottom: 29.0,
+          left: 14.0,
+          right: 8.0,
+        ),
         child: Row(
         child: Row(
           children: [
           children: [
             GestureDetector(
             GestureDetector(

+ 7 - 7
mobile/lib/modules/sharing/ui/selection_thumbnail_image.dart

@@ -5,10 +5,10 @@ import 'package:hive_flutter/hive_flutter.dart';
 import 'package:hooks_riverpod/hooks_riverpod.dart';
 import 'package:hooks_riverpod/hooks_riverpod.dart';
 import 'package:immich_mobile/constants/hive_box.dart';
 import 'package:immich_mobile/constants/hive_box.dart';
 import 'package:immich_mobile/modules/sharing/providers/asset_selection.provider.dart';
 import 'package:immich_mobile/modules/sharing/providers/asset_selection.provider.dart';
-import 'package:immich_mobile/shared/models/immich_asset.model.dart';
+import 'package:openapi/api.dart';
 
 
 class SelectionThumbnailImage extends HookConsumerWidget {
 class SelectionThumbnailImage extends HookConsumerWidget {
-  final ImmichAsset asset;
+  final AssetResponseDto asset;
 
 
   const SelectionThumbnailImage({Key? key, required this.asset})
   const SelectionThumbnailImage({Key? key, required this.asset})
       : super(key: key);
       : super(key: key);
@@ -18,14 +18,14 @@ class SelectionThumbnailImage extends HookConsumerWidget {
     final cacheKey = useState(1);
     final cacheKey = useState(1);
     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/thumbnail/${asset.id}';
     var selectedAsset =
     var selectedAsset =
         ref.watch(assetSelectionProvider).selectedNewAssetsForAlbum;
         ref.watch(assetSelectionProvider).selectedNewAssetsForAlbum;
     var newAssetsForAlbum =
     var newAssetsForAlbum =
         ref.watch(assetSelectionProvider).selectedAdditionalAssetsForAlbum;
         ref.watch(assetSelectionProvider).selectedAdditionalAssetsForAlbum;
     var isAlbumExist = ref.watch(assetSelectionProvider).isAlbumExist;
     var isAlbumExist = ref.watch(assetSelectionProvider).isAlbumExist;
 
 
-    Widget _buildSelectionIcon(ImmichAsset asset) {
+    Widget _buildSelectionIcon(AssetResponseDto asset) {
       if (selectedAsset.contains(asset) && !isAlbumExist) {
       if (selectedAsset.contains(asset) && !isAlbumExist) {
         return Icon(
         return Icon(
           Icons.check_circle,
           Icons.check_circle,
@@ -103,7 +103,7 @@ class SelectionThumbnailImage extends HookConsumerWidget {
               cacheKey: "${asset.id}-${cacheKey.value}",
               cacheKey: "${asset.id}-${cacheKey.value}",
               width: 150,
               width: 150,
               height: 150,
               height: 150,
-              memCacheHeight: asset.type == 'IMAGE' ? 150 : 150,
+              memCacheHeight: asset.type == AssetTypeEnum.IMAGE ? 150 : 150,
               fit: BoxFit.cover,
               fit: BoxFit.cover,
               imageUrl: thumbnailRequestUrl,
               imageUrl: thumbnailRequestUrl,
               httpHeaders: {
               httpHeaders: {
@@ -131,14 +131,14 @@ class SelectionThumbnailImage extends HookConsumerWidget {
               child: _buildSelectionIcon(asset),
               child: _buildSelectionIcon(asset),
             ),
             ),
           ),
           ),
-          if (asset.type != 'IMAGE')
+          if (asset.type != AssetTypeEnum.IMAGE)
             Positioned(
             Positioned(
               bottom: 5,
               bottom: 5,
               right: 5,
               right: 5,
               child: Row(
               child: Row(
                 children: [
                 children: [
                   Text(
                   Text(
-                    '${asset.duration?.substring(0, 7)}',
+                    asset.duration.substring(0, 7),
                     style: const TextStyle(
                     style: const TextStyle(
                       color: Colors.white,
                       color: Colors.white,
                       fontSize: 10,
                       fontSize: 10,

+ 4 - 4
mobile/lib/modules/sharing/ui/shared_album_thumbnail_image.dart

@@ -4,10 +4,10 @@ import 'package:flutter_hooks/flutter_hooks.dart';
 import 'package:hive_flutter/hive_flutter.dart';
 import 'package:hive_flutter/hive_flutter.dart';
 import 'package:hooks_riverpod/hooks_riverpod.dart';
 import 'package:hooks_riverpod/hooks_riverpod.dart';
 import 'package:immich_mobile/constants/hive_box.dart';
 import 'package:immich_mobile/constants/hive_box.dart';
-import 'package:immich_mobile/shared/models/immich_asset.model.dart';
+import 'package:openapi/api.dart';
 
 
 class SharedAlbumThumbnailImage extends HookConsumerWidget {
 class SharedAlbumThumbnailImage extends HookConsumerWidget {
-  final ImmichAsset asset;
+  final AssetResponseDto asset;
 
 
   const SharedAlbumThumbnailImage({Key? key, required this.asset})
   const SharedAlbumThumbnailImage({Key? key, required this.asset})
       : super(key: key);
       : super(key: key);
@@ -18,7 +18,7 @@ class SharedAlbumThumbnailImage 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/thumbnail/${asset.id}';
 
 
     return GestureDetector(
     return GestureDetector(
       onTap: () {
       onTap: () {
@@ -30,7 +30,7 @@ class SharedAlbumThumbnailImage extends HookConsumerWidget {
             cacheKey: "${asset.id}-${cacheKey.value}",
             cacheKey: "${asset.id}-${cacheKey.value}",
             width: 500,
             width: 500,
             height: 500,
             height: 500,
-            memCacheHeight: asset.type == 'IMAGE' ? 500 : 500,
+            memCacheHeight: 500,
             fit: BoxFit.cover,
             fit: BoxFit.cover,
             imageUrl: thumbnailRequestUrl,
             imageUrl: thumbnailRequestUrl,
             httpHeaders: {"Authorization": "Bearer ${box.get(accessTokenKey)}"},
             httpHeaders: {"Authorization": "Bearer ${box.get(accessTokenKey)}"},

+ 4 - 2
mobile/lib/modules/sharing/ui/sharing_sliver_appbar.dart

@@ -40,7 +40,8 @@ class SharingSliverAppBar extends StatelessWidget {
                   child: TextButton.icon(
                   child: TextButton.icon(
                     style: ButtonStyle(
                     style: ButtonStyle(
                       backgroundColor: MaterialStateProperty.all(
                       backgroundColor: MaterialStateProperty.all(
-                          Theme.of(context).primaryColor.withAlpha(20)),
+                        Theme.of(context).primaryColor.withAlpha(20),
+                      ),
                       // foregroundColor: MaterialStateProperty.all(Colors.white),
                       // foregroundColor: MaterialStateProperty.all(Colors.white),
                     ),
                     ),
                     onPressed: () {
                     onPressed: () {
@@ -65,7 +66,8 @@ class SharingSliverAppBar extends StatelessWidget {
                   child: TextButton.icon(
                   child: TextButton.icon(
                     style: ButtonStyle(
                     style: ButtonStyle(
                       backgroundColor: MaterialStateProperty.all(
                       backgroundColor: MaterialStateProperty.all(
-                          Theme.of(context).primaryColor.withAlpha(20)),
+                        Theme.of(context).primaryColor.withAlpha(20),
+                      ),
                       // foregroundColor: MaterialStateProperty.all(Colors.white),
                       // foregroundColor: MaterialStateProperty.all(Colors.white),
                     ),
                     ),
                     onPressed: null,
                     onPressed: null,

+ 46 - 29
mobile/lib/modules/sharing/views/album_viewer_page.dart

@@ -7,7 +7,6 @@ import 'package:immich_mobile/constants/immich_colors.dart';
 import 'package:immich_mobile/modules/home/ui/draggable_scrollbar.dart';
 import 'package:immich_mobile/modules/home/ui/draggable_scrollbar.dart';
 import 'package:immich_mobile/modules/login/providers/authentication.provider.dart';
 import 'package:immich_mobile/modules/login/providers/authentication.provider.dart';
 import 'package:immich_mobile/modules/sharing/models/asset_selection_page_result.model.dart';
 import 'package:immich_mobile/modules/sharing/models/asset_selection_page_result.model.dart';
-import 'package:immich_mobile/modules/sharing/models/shared_album.model.dart';
 import 'package:immich_mobile/modules/sharing/providers/asset_selection.provider.dart';
 import 'package:immich_mobile/modules/sharing/providers/asset_selection.provider.dart';
 import 'package:immich_mobile/modules/sharing/providers/shared_album.provider.dart';
 import 'package:immich_mobile/modules/sharing/providers/shared_album.provider.dart';
 import 'package:immich_mobile/modules/sharing/services/shared_album.service.dart';
 import 'package:immich_mobile/modules/sharing/services/shared_album.service.dart';
@@ -19,7 +18,7 @@ import 'package:immich_mobile/routing/router.dart';
 import 'package:immich_mobile/shared/ui/immich_loading_indicator.dart';
 import 'package:immich_mobile/shared/ui/immich_loading_indicator.dart';
 import 'package:immich_mobile/shared/ui/immich_sliver_persistent_app_bar_delegate.dart';
 import 'package:immich_mobile/shared/ui/immich_sliver_persistent_app_bar_delegate.dart';
 import 'package:immich_mobile/shared/views/immich_loading_overlay.dart';
 import 'package:immich_mobile/shared/views/immich_loading_overlay.dart';
-import 'package:intl/intl.dart';
+import 'package:openapi/api.dart';
 
 
 class AlbumViewerPage extends HookConsumerWidget {
 class AlbumViewerPage extends HookConsumerWidget {
   final String albumId;
   final String albumId;
@@ -30,18 +29,18 @@ class AlbumViewerPage extends HookConsumerWidget {
   Widget build(BuildContext context, WidgetRef ref) {
   Widget build(BuildContext context, WidgetRef ref) {
     FocusNode titleFocusNode = useFocusNode();
     FocusNode titleFocusNode = useFocusNode();
     ScrollController scrollController = useScrollController();
     ScrollController scrollController = useScrollController();
-    AsyncValue<SharedAlbum> albumInfo =
+    AsyncValue<AlbumResponseDto?> albumInfo =
         ref.watch(sharedAlbumDetailProvider(albumId));
         ref.watch(sharedAlbumDetailProvider(albumId));
 
 
     final userId = ref.watch(authenticationProvider).userId;
     final userId = ref.watch(authenticationProvider).userId;
 
 
     /// Find out if the assets in album exist on the device
     /// Find out if the assets in album exist on the device
     /// If they exist, add to selected asset state to show they are already selected.
     /// If they exist, add to selected asset state to show they are already selected.
-    void _onAddPhotosPressed(SharedAlbum albumInfo) async {
-      if (albumInfo.assets?.isNotEmpty == true) {
+    void _onAddPhotosPressed(AlbumResponseDto albumInfo) async {
+      if (albumInfo.assets.isNotEmpty == true) {
         ref
         ref
             .watch(assetSelectionProvider.notifier)
             .watch(assetSelectionProvider.notifier)
-            .addNewAssets(albumInfo.assets!.toList());
+            .addNewAssets(albumInfo.assets.toList());
       }
       }
 
 
       ref.watch(assetSelectionProvider.notifier).setIsAlbumExist(true);
       ref.watch(assetSelectionProvider.notifier).setIsAlbumExist(true);
@@ -57,7 +56,9 @@ class AlbumViewerPage extends HookConsumerWidget {
           var isSuccess = await ref
           var isSuccess = await ref
               .watch(sharedAlbumServiceProvider)
               .watch(sharedAlbumServiceProvider)
               .addAdditionalAssetToAlbum(
               .addAdditionalAssetToAlbum(
-                  returnPayload.selectedAdditionalAsset, albumId);
+                returnPayload.selectedAdditionalAsset,
+                albumId,
+              );
 
 
           if (isSuccess) {
           if (isSuccess) {
             ref.refresh(sharedAlbumDetailProvider(albumId));
             ref.refresh(sharedAlbumDetailProvider(albumId));
@@ -72,10 +73,11 @@ class AlbumViewerPage extends HookConsumerWidget {
       }
       }
     }
     }
 
 
-    void _onAddUsersPressed(SharedAlbum albumInfo) async {
-      List<String>? sharedUserIds = await AutoRouter.of(context)
-          .push<List<String>?>(
-              SelectAdditionalUserForSharingRoute(albumInfo: albumInfo));
+    void _onAddUsersPressed(AlbumResponseDto albumInfo) async {
+      List<String>? sharedUserIds =
+          await AutoRouter.of(context).push<List<String>?>(
+        SelectAdditionalUserForSharingRoute(albumInfo: albumInfo),
+      );
 
 
       if (sharedUserIds != null) {
       if (sharedUserIds != null) {
         ImmichLoadingOverlayController.appLoader.show();
         ImmichLoadingOverlayController.appLoader.show();
@@ -92,7 +94,7 @@ class AlbumViewerPage extends HookConsumerWidget {
       }
       }
     }
     }
 
 
-    Widget _buildTitle(SharedAlbum albumInfo) {
+    Widget _buildTitle(AlbumResponseDto albumInfo) {
       return Padding(
       return Padding(
         padding: const EdgeInsets.only(left: 8, right: 8, top: 16),
         padding: const EdgeInsets.only(left: 8, right: 8, top: 16),
         child: userId == albumInfo.ownerId
         child: userId == albumInfo.ownerId
@@ -102,19 +104,24 @@ class AlbumViewerPage extends HookConsumerWidget {
               )
               )
             : Padding(
             : Padding(
                 padding: const EdgeInsets.only(left: 8.0),
                 padding: const EdgeInsets.only(left: 8.0),
-                child: Text(albumInfo.albumName,
-                    style: const TextStyle(
-                        fontSize: 24, fontWeight: FontWeight.bold)),
+                child: Text(
+                  albumInfo.albumName,
+                  style: const TextStyle(
+                    fontSize: 24,
+                    fontWeight: FontWeight.bold,
+                  ),
+                ),
               ),
               ),
       );
       );
     }
     }
 
 
-    Widget _buildAlbumDateRange(SharedAlbum albumInfo) {
+    Widget _buildAlbumDateRange(AlbumResponseDto albumInfo) {
       String startDate = "";
       String startDate = "";
       DateTime parsedStartDate =
       DateTime parsedStartDate =
-          DateTime.parse(albumInfo.assets!.first.createdAt);
+          DateTime.parse(albumInfo.assets.first.createdAt);
       DateTime parsedEndDate = DateTime.parse(
       DateTime parsedEndDate = DateTime.parse(
-          albumInfo.assets?.last.createdAt ?? '11111111'); //Need default.
+        albumInfo.assets.last.createdAt,
+      ); //Need default.
 
 
       if (parsedStartDate.year == parsedEndDate.year) {
       if (parsedStartDate.year == parsedEndDate.year) {
         startDate = DateFormat('LLL d').format(parsedStartDate);
         startDate = DateFormat('LLL d').format(parsedStartDate);
@@ -129,18 +136,21 @@ class AlbumViewerPage extends HookConsumerWidget {
         child: Text(
         child: Text(
           "$startDate-$endDate",
           "$startDate-$endDate",
           style: const TextStyle(
           style: const TextStyle(
-              fontSize: 14, fontWeight: FontWeight.bold, color: Colors.grey),
+            fontSize: 14,
+            fontWeight: FontWeight.bold,
+            color: Colors.grey,
+          ),
         ),
         ),
       );
       );
     }
     }
 
 
-    Widget _buildHeader(SharedAlbum albumInfo) {
+    Widget _buildHeader(AlbumResponseDto albumInfo) {
       return SliverToBoxAdapter(
       return SliverToBoxAdapter(
         child: Column(
         child: Column(
           crossAxisAlignment: CrossAxisAlignment.start,
           crossAxisAlignment: CrossAxisAlignment.start,
           children: [
           children: [
             _buildTitle(albumInfo),
             _buildTitle(albumInfo),
-            if (albumInfo.assets?.isNotEmpty == true)
+            if (albumInfo.assets.isNotEmpty == true)
               _buildAlbumDateRange(albumInfo),
               _buildAlbumDateRange(albumInfo),
             SizedBox(
             SizedBox(
               height: 60,
               height: 60,
@@ -172,8 +182,8 @@ class AlbumViewerPage extends HookConsumerWidget {
       );
       );
     }
     }
 
 
-    Widget _buildImageGrid(SharedAlbum albumInfo) {
-      if (albumInfo.assets?.isNotEmpty == true) {
+    Widget _buildImageGrid(AlbumResponseDto albumInfo) {
+      if (albumInfo.assets.isNotEmpty) {
         return SliverPadding(
         return SliverPadding(
           padding: const EdgeInsets.only(top: 10.0),
           padding: const EdgeInsets.only(top: 10.0),
           sliver: SliverGrid(
           sliver: SliverGrid(
@@ -184,9 +194,9 @@ class AlbumViewerPage extends HookConsumerWidget {
             ),
             ),
             delegate: SliverChildBuilderDelegate(
             delegate: SliverChildBuilderDelegate(
               (BuildContext context, int index) {
               (BuildContext context, int index) {
-                return AlbumViewerThumbnail(asset: albumInfo.assets![index]);
+                return AlbumViewerThumbnail(asset: albumInfo.assets[index]);
               },
               },
-              childCount: albumInfo.assets?.length,
+              childCount: albumInfo.assets.length,
             ),
             ),
           ),
           ),
         );
         );
@@ -194,7 +204,7 @@ class AlbumViewerPage extends HookConsumerWidget {
       return const SliverToBoxAdapter();
       return const SliverToBoxAdapter();
     }
     }
 
 
-    Widget _buildControlButton(SharedAlbum albumInfo) {
+    Widget _buildControlButton(AlbumResponseDto albumInfo) {
       return Padding(
       return Padding(
         padding: const EdgeInsets.only(left: 16.0, top: 8, bottom: 8),
         padding: const EdgeInsets.only(left: 16.0, top: 8, bottom: 8),
         child: SizedBox(
         child: SizedBox(
@@ -219,7 +229,7 @@ class AlbumViewerPage extends HookConsumerWidget {
       );
       );
     }
     }
 
 
-    Widget _buildBody(SharedAlbum albumInfo) {
+    Widget _buildBody(AlbumResponseDto albumInfo) {
       return GestureDetector(
       return GestureDetector(
         onTap: () {
         onTap: () {
           titleFocusNode.unfocus();
           titleFocusNode.unfocus();
@@ -252,9 +262,16 @@ class AlbumViewerPage extends HookConsumerWidget {
 
 
     return Scaffold(
     return Scaffold(
       appBar: AlbumViewerAppbar(
       appBar: AlbumViewerAppbar(
-          albumInfo: albumInfo, userId: userId, albumId: albumId),
+        albumInfo: albumInfo,
+        userId: userId,
+        albumId: albumId,
+      ),
       body: albumInfo.when(
       body: albumInfo.when(
-        data: (albumInfo) => _buildBody(albumInfo),
+        data: (albumInfo) => albumInfo != null
+            ? _buildBody(albumInfo)
+            : const Center(
+                child: CircularProgressIndicator(),
+              ),
         error: (e, _) => Center(child: Text("Error loading album info $e")),
         error: (e, _) => Center(child: Text("Error loading album info $e")),
         loading: () => const Center(
         loading: () => const Center(
           child: ImmichLoadingIndicator(),
           child: ImmichLoadingIndicator(),

+ 73 - 65
mobile/lib/modules/sharing/views/create_shared_album_page.dart

@@ -56,10 +56,11 @@ class CreateSharedAlbumPage extends HookConsumerWidget {
           left: 10,
           left: 10,
         ),
         ),
         child: AlbumTitleTextField(
         child: AlbumTitleTextField(
-            isAlbumTitleEmpty: isAlbumTitleEmpty,
-            albumTitleTextFieldFocusNode: albumTitleTextFieldFocusNode,
-            albumTitleController: albumTitleController,
-            isAlbumTitleTextFieldFocus: isAlbumTitleTextFieldFocus),
+          isAlbumTitleEmpty: isAlbumTitleEmpty,
+          albumTitleTextFieldFocusNode: albumTitleTextFieldFocusNode,
+          albumTitleController: albumTitleController,
+          isAlbumTitleTextFieldFocus: isAlbumTitleTextFieldFocus,
+        ),
       );
       );
     }
     }
 
 
@@ -67,8 +68,8 @@ class CreateSharedAlbumPage extends HookConsumerWidget {
       if (selectedAssets.isEmpty) {
       if (selectedAssets.isEmpty) {
         return SliverToBoxAdapter(
         return SliverToBoxAdapter(
           child: Padding(
           child: Padding(
-            padding: EdgeInsets.only(top: 200, left: 18),
-            child: Text(
+            padding: const EdgeInsets.only(top: 200, left: 18),
+            child: const Text(
               'create_shared_album_page_share_add_assets',
               'create_shared_album_page_share_add_assets',
               style: TextStyle(fontSize: 12),
               style: TextStyle(fontSize: 12),
             ).tr(),
             ).tr(),
@@ -86,13 +87,16 @@ class CreateSharedAlbumPage extends HookConsumerWidget {
             padding: const EdgeInsets.only(top: 16, left: 18, right: 18),
             padding: const EdgeInsets.only(top: 16, left: 18, right: 18),
             child: OutlinedButton.icon(
             child: OutlinedButton.icon(
               style: OutlinedButton.styleFrom(
               style: OutlinedButton.styleFrom(
-                  alignment: Alignment.centerLeft,
-                  padding:
-                      const EdgeInsets.symmetric(vertical: 22, horizontal: 16),
-                  side: const BorderSide(
-                      color: Color.fromARGB(255, 206, 206, 206)),
-                  shape: RoundedRectangleBorder(
-                      borderRadius: BorderRadius.circular(5))),
+                alignment: Alignment.centerLeft,
+                padding:
+                    const EdgeInsets.symmetric(vertical: 22, horizontal: 16),
+                side: const BorderSide(
+                  color: Color.fromARGB(255, 206, 206, 206),
+                ),
+                shape: RoundedRectangleBorder(
+                  borderRadius: BorderRadius.circular(5),
+                ),
+              ),
               onPressed: _onSelectPhotosButtonPressed,
               onPressed: _onSelectPhotosButtonPressed,
               icon: const Icon(Icons.add_rounded),
               icon: const Icon(Icons.add_rounded),
               label: Padding(
               label: Padding(
@@ -100,9 +104,10 @@ class CreateSharedAlbumPage extends HookConsumerWidget {
                 child: Text(
                 child: Text(
                   'create_shared_album_page_share_select_photos',
                   'create_shared_album_page_share_select_photos',
                   style: TextStyle(
                   style: TextStyle(
-                      fontSize: 16,
-                      color: Colors.grey[700],
-                      fontWeight: FontWeight.bold),
+                    fontSize: 16,
+                    color: Colors.grey[700],
+                    fontWeight: FontWeight.bold,
+                  ),
                 ).tr(),
                 ).tr(),
               ),
               ),
             ),
             ),
@@ -147,7 +152,8 @@ class CreateSharedAlbumPage extends HookConsumerWidget {
                 return GestureDetector(
                 return GestureDetector(
                   onTap: _onBackgroundTapped,
                   onTap: _onBackgroundTapped,
                   child: SharedAlbumThumbnailImage(
                   child: SharedAlbumThumbnailImage(
-                      asset: selectedAssets.toList()[index]),
+                    asset: selectedAssets.toList()[index],
+                  ),
                 );
                 );
               },
               },
               childCount: selectedAssets.length,
               childCount: selectedAssets.length,
@@ -160,58 +166,60 @@ class CreateSharedAlbumPage extends HookConsumerWidget {
     }
     }
 
 
     return Scaffold(
     return Scaffold(
-        appBar: AppBar(
-          elevation: 0,
-          centerTitle: false,
-          leading: IconButton(
-              onPressed: () {
-                ref.watch(assetSelectionProvider.notifier).removeAll();
-                AutoRouter.of(context).pop();
-              },
-              icon: const Icon(Icons.close_rounded)),
-          title: const Text(
-            'share_create_album',
-            style: TextStyle(color: Colors.black),
-          ).tr(),
-          actions: [
-            TextButton(
-              onPressed: albumTitleController.text.isNotEmpty
-                  ? _showSelectUserPage
-                  : null,
-              child: Text(
-                'create_shared_album_page_share'.tr(),
-                style: TextStyle(
-                  fontWeight: FontWeight.bold,
+      appBar: AppBar(
+        elevation: 0,
+        centerTitle: false,
+        leading: IconButton(
+          onPressed: () {
+            ref.watch(assetSelectionProvider.notifier).removeAll();
+            AutoRouter.of(context).pop();
+          },
+          icon: const Icon(Icons.close_rounded),
+        ),
+        title: const Text(
+          'share_create_album',
+          style: TextStyle(color: Colors.black),
+        ).tr(),
+        actions: [
+          TextButton(
+            onPressed: albumTitleController.text.isNotEmpty
+                ? _showSelectUserPage
+                : null,
+            child: Text(
+              'create_shared_album_page_share'.tr(),
+              style: const TextStyle(
+                fontWeight: FontWeight.bold,
+              ),
+            ),
+          ),
+        ],
+      ),
+      body: GestureDetector(
+        onTap: _onBackgroundTapped,
+        child: CustomScrollView(
+          slivers: [
+            SliverAppBar(
+              elevation: 5,
+              automaticallyImplyLeading: false,
+              // leading: Container(),
+              pinned: true,
+              floating: false,
+              bottom: PreferredSize(
+                preferredSize: const Size.fromHeight(66.0),
+                child: Column(
+                  children: [
+                    _buildTitleInputField(),
+                    if (selectedAssets.isNotEmpty) _buildControlButton(),
+                  ],
                 ),
                 ),
               ),
               ),
             ),
             ),
+            _buildTitle(),
+            _buildSelectPhotosButton(),
+            _buildSelectedImageGrid(),
           ],
           ],
         ),
         ),
-        body: GestureDetector(
-          onTap: _onBackgroundTapped,
-          child: CustomScrollView(
-            slivers: [
-              SliverAppBar(
-                elevation: 5,
-                automaticallyImplyLeading: false,
-                // leading: Container(),
-                pinned: true,
-                floating: false,
-                bottom: PreferredSize(
-                  preferredSize: const Size.fromHeight(66.0),
-                  child: Column(
-                    children: [
-                      _buildTitleInputField(),
-                      if (selectedAssets.isNotEmpty) _buildControlButton(),
-                    ],
-                  ),
-                ),
-              ),
-              _buildTitle(),
-              _buildSelectPhotosButton(),
-              _buildSelectedImageGrid(),
-            ],
-          ),
-        ));
+      ),
+    );
   }
   }
 }
 }

+ 24 - 19
mobile/lib/modules/sharing/views/select_additional_user_for_sharing_page.dart

@@ -3,29 +3,28 @@ import 'package:easy_localization/easy_localization.dart';
 import 'package:flutter/material.dart';
 import 'package:flutter/material.dart';
 import 'package:flutter_hooks/flutter_hooks.dart';
 import 'package:flutter_hooks/flutter_hooks.dart';
 import 'package:hooks_riverpod/hooks_riverpod.dart';
 import 'package:hooks_riverpod/hooks_riverpod.dart';
-import 'package:immich_mobile/modules/sharing/models/shared_album.model.dart';
 import 'package:immich_mobile/modules/sharing/providers/suggested_shared_users.provider.dart';
 import 'package:immich_mobile/modules/sharing/providers/suggested_shared_users.provider.dart';
-import 'package:immich_mobile/shared/models/user.model.dart';
 import 'package:immich_mobile/shared/ui/immich_loading_indicator.dart';
 import 'package:immich_mobile/shared/ui/immich_loading_indicator.dart';
+import 'package:openapi/api.dart';
 
 
 class SelectAdditionalUserForSharingPage extends HookConsumerWidget {
 class SelectAdditionalUserForSharingPage extends HookConsumerWidget {
-  final SharedAlbum albumInfo;
+  final AlbumResponseDto albumInfo;
 
 
   const SelectAdditionalUserForSharingPage({Key? key, required this.albumInfo})
   const SelectAdditionalUserForSharingPage({Key? key, required this.albumInfo})
       : super(key: key);
       : super(key: key);
 
 
   @override
   @override
   Widget build(BuildContext context, WidgetRef ref) {
   Widget build(BuildContext context, WidgetRef ref) {
-    AsyncValue<List<User>> suggestedShareUsers =
+    AsyncValue<List<UserResponseDto>> suggestedShareUsers =
         ref.watch(suggestedSharedUsersProvider);
         ref.watch(suggestedSharedUsersProvider);
-    final sharedUsersList = useState<Set<User>>({});
+    final sharedUsersList = useState<Set<UserResponseDto>>({});
 
 
     _addNewUsersHandler() {
     _addNewUsersHandler() {
       AutoRouter.of(context)
       AutoRouter.of(context)
           .pop(sharedUsersList.value.map((e) => e.id).toList());
           .pop(sharedUsersList.value.map((e) => e.id).toList());
     }
     }
 
 
-    _buildTileIcon(User user) {
+    _buildTileIcon(UserResponseDto user) {
       if (sharedUsersList.value.contains(user)) {
       if (sharedUsersList.value.contains(user)) {
         return CircleAvatar(
         return CircleAvatar(
           backgroundColor: Theme.of(context).primaryColor,
           backgroundColor: Theme.of(context).primaryColor,
@@ -43,7 +42,7 @@ class SelectAdditionalUserForSharingPage extends HookConsumerWidget {
       }
       }
     }
     }
 
 
-    _buildUserList(List<User> users) {
+    _buildUserList(List<UserResponseDto> users) {
       List<Widget> usersChip = [];
       List<Widget> usersChip = [];
 
 
       for (var user in sharedUsersList.value) {
       for (var user in sharedUsersList.value) {
@@ -55,9 +54,10 @@ class SelectAdditionalUserForSharingPage extends HookConsumerWidget {
               label: Text(
               label: Text(
                 user.email,
                 user.email,
                 style: const TextStyle(
                 style: const TextStyle(
-                    fontSize: 12,
-                    color: Colors.black87,
-                    fontWeight: FontWeight.bold),
+                  fontSize: 12,
+                  color: Colors.black87,
+                  fontWeight: FontWeight.bold,
+                ),
               ),
               ),
             ),
             ),
           ),
           ),
@@ -70,13 +70,14 @@ class SelectAdditionalUserForSharingPage extends HookConsumerWidget {
             children: [...usersChip],
             children: [...usersChip],
           ),
           ),
           Padding(
           Padding(
-            padding: EdgeInsets.all(16.0),
+            padding: const EdgeInsets.all(16.0),
             child: Text(
             child: Text(
               'select_additional_user_for_sharing_page_suggestions'.tr(),
               'select_additional_user_for_sharing_page_suggestions'.tr(),
-              style: TextStyle(
-                  fontSize: 14,
-                  color: Colors.grey,
-                  fontWeight: FontWeight.bold),
+              style: const TextStyle(
+                fontSize: 14,
+                color: Colors.grey,
+                fontWeight: FontWeight.bold,
+              ),
             ),
             ),
           ),
           ),
           ListView.builder(
           ListView.builder(
@@ -87,13 +88,16 @@ class SelectAdditionalUserForSharingPage extends HookConsumerWidget {
                 title: Text(
                 title: Text(
                   users[index].email,
                   users[index].email,
                   style: const TextStyle(
                   style: const TextStyle(
-                      fontSize: 14, fontWeight: FontWeight.bold),
+                    fontSize: 14,
+                    fontWeight: FontWeight.bold,
+                  ),
                 ),
                 ),
                 onTap: () {
                 onTap: () {
                   if (sharedUsersList.value.contains(users[index])) {
                   if (sharedUsersList.value.contains(users[index])) {
                     sharedUsersList.value = sharedUsersList.value
                     sharedUsersList.value = sharedUsersList.value
-                        .where((selectedUser) =>
-                            selectedUser.id != users[index].id)
+                        .where(
+                          (selectedUser) => selectedUser.id != users[index].id,
+                        )
                         .toSet();
                         .toSet();
                   } else {
                   } else {
                     sharedUsersList.value = {
                     sharedUsersList.value = {
@@ -139,7 +143,8 @@ class SelectAdditionalUserForSharingPage extends HookConsumerWidget {
         data: (users) {
         data: (users) {
           for (var sharedUsers in albumInfo.sharedUsers) {
           for (var sharedUsers in albumInfo.sharedUsers) {
             users.removeWhere(
             users.removeWhere(
-                (u) => u.id == sharedUsers.id || u.id == albumInfo.ownerId);
+              (u) => u.id == sharedUsers.id || u.id == albumInfo.ownerId,
+            );
           }
           }
 
 
           return _buildUserList(users);
           return _buildUserList(users);

+ 35 - 24
mobile/lib/modules/sharing/views/select_user_for_sharing_page.dart

@@ -9,15 +9,16 @@ import 'package:immich_mobile/modules/sharing/providers/shared_album.provider.da
 import 'package:immich_mobile/modules/sharing/providers/suggested_shared_users.provider.dart';
 import 'package:immich_mobile/modules/sharing/providers/suggested_shared_users.provider.dart';
 import 'package:immich_mobile/modules/sharing/services/shared_album.service.dart';
 import 'package:immich_mobile/modules/sharing/services/shared_album.service.dart';
 import 'package:immich_mobile/routing/router.dart';
 import 'package:immich_mobile/routing/router.dart';
-import 'package:immich_mobile/shared/models/user.model.dart';
 import 'package:immich_mobile/shared/ui/immich_loading_indicator.dart';
 import 'package:immich_mobile/shared/ui/immich_loading_indicator.dart';
+import 'package:openapi/api.dart';
 
 
 class SelectUserForSharingPage extends HookConsumerWidget {
 class SelectUserForSharingPage extends HookConsumerWidget {
   const SelectUserForSharingPage({Key? key}) : super(key: key);
   const SelectUserForSharingPage({Key? key}) : super(key: key);
+
   @override
   @override
   Widget build(BuildContext context, WidgetRef ref) {
   Widget build(BuildContext context, WidgetRef ref) {
-    final sharedUsersList = useState<Set<User>>({});
-    AsyncValue<List<User>> suggestedShareUsers =
+    final sharedUsersList = useState<Set<UserResponseDto>>({});
+    AsyncValue<List<UserResponseDto>> suggestedShareUsers =
         ref.watch(suggestedSharedUsersProvider);
         ref.watch(suggestedSharedUsersProvider);
 
 
     _createSharedAlbum() async {
     _createSharedAlbum() async {
@@ -37,10 +38,14 @@ class SelectUserForSharingPage extends HookConsumerWidget {
             .navigate(const TabControllerRoute(children: [SharingRoute()]));
             .navigate(const TabControllerRoute(children: [SharingRoute()]));
       }
       }
 
 
-      ScaffoldMessenger(child: SnackBar(content: Text('select_user_for_sharing_page_err_album').tr()));
+      ScaffoldMessenger(
+        child: SnackBar(
+          content: const Text('select_user_for_sharing_page_err_album').tr(),
+        ),
+      );
     }
     }
 
 
-    _buildTileIcon(User user) {
+    _buildTileIcon(UserResponseDto user) {
       if (sharedUsersList.value.contains(user)) {
       if (sharedUsersList.value.contains(user)) {
         return CircleAvatar(
         return CircleAvatar(
           backgroundColor: Theme.of(context).primaryColor,
           backgroundColor: Theme.of(context).primaryColor,
@@ -58,7 +63,7 @@ class SelectUserForSharingPage extends HookConsumerWidget {
       }
       }
     }
     }
 
 
-    _buildUserList(List<User> users) {
+    _buildUserList(List<UserResponseDto> users) {
       List<Widget> usersChip = [];
       List<Widget> usersChip = [];
 
 
       for (var user in sharedUsersList.value) {
       for (var user in sharedUsersList.value) {
@@ -70,9 +75,10 @@ class SelectUserForSharingPage extends HookConsumerWidget {
               label: Text(
               label: Text(
                 user.email,
                 user.email,
                 style: const TextStyle(
                 style: const TextStyle(
-                    fontSize: 12,
-                    color: Colors.black87,
-                    fontWeight: FontWeight.bold),
+                  fontSize: 12,
+                  color: Colors.black87,
+                  fontWeight: FontWeight.bold,
+                ),
               ),
               ),
             ),
             ),
           ),
           ),
@@ -85,13 +91,14 @@ class SelectUserForSharingPage extends HookConsumerWidget {
             children: [...usersChip],
             children: [...usersChip],
           ),
           ),
           Padding(
           Padding(
-            padding: EdgeInsets.all(16.0),
-            child: Text(
-              'share_suggestions',
+            padding: const EdgeInsets.all(16.0),
+            child: const Text(
+              'select_user_for_sharing_page_share_suggestions',
               style: TextStyle(
               style: TextStyle(
-                  fontSize: 14,
-                  color: Colors.grey,
-                  fontWeight: FontWeight.bold),
+                fontSize: 14,
+                color: Colors.grey,
+                fontWeight: FontWeight.bold,
+              ),
             ).tr(),
             ).tr(),
           ),
           ),
           ListView.builder(
           ListView.builder(
@@ -102,13 +109,16 @@ class SelectUserForSharingPage extends HookConsumerWidget {
                 title: Text(
                 title: Text(
                   users[index].email,
                   users[index].email,
                   style: const TextStyle(
                   style: const TextStyle(
-                      fontSize: 14, fontWeight: FontWeight.bold),
+                    fontSize: 14,
+                    fontWeight: FontWeight.bold,
+                  ),
                 ),
                 ),
                 onTap: () {
                 onTap: () {
                   if (sharedUsersList.value.contains(users[index])) {
                   if (sharedUsersList.value.contains(users[index])) {
                     sharedUsersList.value = sharedUsersList.value
                     sharedUsersList.value = sharedUsersList.value
-                        .where((selectedUser) =>
-                            selectedUser.id != users[index].id)
+                        .where(
+                          (selectedUser) => selectedUser.id != users[index].id,
+                        )
                         .toSet();
                         .toSet();
                   } else {
                   } else {
                     sharedUsersList.value = {
                     sharedUsersList.value = {
@@ -141,12 +151,13 @@ class SelectUserForSharingPage extends HookConsumerWidget {
         ),
         ),
         actions: [
         actions: [
           TextButton(
           TextButton(
-              onPressed:
-                  sharedUsersList.value.isEmpty ? null : _createSharedAlbum,
-              child: const Text(
-                "share_create_album",
-                style: TextStyle(fontSize: 14, fontWeight: FontWeight.bold),
-              ).tr())
+            onPressed:
+                sharedUsersList.value.isEmpty ? null : _createSharedAlbum,
+            child: const Text(
+              "share_create_album",
+              style: TextStyle(fontSize: 14, fontWeight: FontWeight.bold),
+            ).tr(),
+          )
         ],
         ],
       ),
       ),
       body: suggestedShareUsers.when(
       body: suggestedShareUsers.when(

+ 15 - 11
mobile/lib/modules/sharing/views/sharing_page.dart

@@ -5,10 +5,10 @@ import 'package:flutter_hooks/flutter_hooks.dart';
 import 'package:hive/hive.dart';
 import 'package:hive/hive.dart';
 import 'package:hooks_riverpod/hooks_riverpod.dart';
 import 'package:hooks_riverpod/hooks_riverpod.dart';
 import 'package:immich_mobile/constants/hive_box.dart';
 import 'package:immich_mobile/constants/hive_box.dart';
-import 'package:immich_mobile/modules/sharing/models/shared_album.model.dart';
 import 'package:immich_mobile/modules/sharing/providers/shared_album.provider.dart';
 import 'package:immich_mobile/modules/sharing/providers/shared_album.provider.dart';
 import 'package:immich_mobile/modules/sharing/ui/sharing_sliver_appbar.dart';
 import 'package:immich_mobile/modules/sharing/ui/sharing_sliver_appbar.dart';
 import 'package:immich_mobile/routing/router.dart';
 import 'package:immich_mobile/routing/router.dart';
+import 'package:openapi/api.dart';
 import 'package:transparent_image/transparent_image.dart';
 import 'package:transparent_image/transparent_image.dart';
 
 
 class SharingPage extends HookConsumerWidget {
 class SharingPage extends HookConsumerWidget {
@@ -18,13 +18,16 @@ class SharingPage extends HookConsumerWidget {
   Widget build(BuildContext context, WidgetRef ref) {
   Widget build(BuildContext context, WidgetRef ref) {
     var box = Hive.box(userInfoBox);
     var box = Hive.box(userInfoBox);
     var thumbnailRequestUrl = '${box.get(serverEndpointKey)}/asset/thumbnail';
     var thumbnailRequestUrl = '${box.get(serverEndpointKey)}/asset/thumbnail';
-    final List<SharedAlbum> sharedAlbums = ref.watch(sharedAlbumProvider);
+    final List<AlbumResponseDto> sharedAlbums = ref.watch(sharedAlbumProvider);
 
 
-    useEffect(() {
-      ref.read(sharedAlbumProvider.notifier).getAllSharedAlbums();
+    useEffect(
+      () {
+        ref.read(sharedAlbumProvider.notifier).getAllSharedAlbums();
 
 
-      return null;
-    }, []);
+        return null;
+      },
+      [],
+    );
 
 
     _buildAlbumList() {
     _buildAlbumList() {
       return SliverList(
       return SliverList(
@@ -60,9 +63,10 @@ class SharingPage extends HookConsumerWidget {
                 maxLines: 1,
                 maxLines: 1,
                 overflow: TextOverflow.ellipsis,
                 overflow: TextOverflow.ellipsis,
                 style: TextStyle(
                 style: TextStyle(
-                    fontSize: 16,
-                    fontWeight: FontWeight.bold,
-                    color: Colors.grey.shade800),
+                  fontSize: 16,
+                  fontWeight: FontWeight.bold,
+                  color: Colors.grey.shade800,
+                ),
               ),
               ),
               onTap: () {
               onTap: () {
                 AutoRouter.of(context)
                 AutoRouter.of(context)
@@ -133,9 +137,9 @@ class SharingPage extends HookConsumerWidget {
         slivers: [
         slivers: [
           const SharingSliverAppBar(),
           const SharingSliverAppBar(),
           SliverPadding(
           SliverPadding(
-            padding: EdgeInsets.symmetric(horizontal: 12, vertical: 12),
+            padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 12),
             sliver: SliverToBoxAdapter(
             sliver: SliverToBoxAdapter(
-              child: Text(
+              child: const Text(
                 "sharing_page_album",
                 "sharing_page_album",
                 style: TextStyle(
                 style: TextStyle(
                   fontWeight: FontWeight.bold,
                   fontWeight: FontWeight.bold,

+ 13 - 9
mobile/lib/routing/auth_guard.dart

@@ -1,21 +1,25 @@
-import 'dart:convert';
-
 import 'package:auto_route/auto_route.dart';
 import 'package:auto_route/auto_route.dart';
-import 'package:immich_mobile/shared/services/network.service.dart';
+import 'package:flutter/foundation.dart';
+import 'package:immich_mobile/routing/router.dart';
+import 'package:immich_mobile/shared/services/api.service.dart';
 
 
 class AuthGuard extends AutoRouteGuard {
 class AuthGuard extends AutoRouteGuard {
-  final NetworkService _networkService = NetworkService();
-
+  final ApiService _apiService;
+  AuthGuard(this._apiService);
   @override
   @override
   void onNavigation(NavigationResolver resolver, StackRouter router) async {
   void onNavigation(NavigationResolver resolver, StackRouter router) async {
     try {
     try {
-      var res = await _networkService.postRequest(url: 'auth/validateToken');
-      var jsonReponse = jsonDecode(res.toString());
-      if (jsonReponse['authStatus']) {
+      var res = await _apiService.authenticationApi.validateAccessToken();
+
+      if (res != null && res.authStatus) {
         resolver.next(true);
         resolver.next(true);
+      } else {
+        router.replaceAll([const LoginRoute()]);
       }
       }
     } catch (e) {
     } catch (e) {
-      router.removeUntil((route) => route.name == "LoginRoute");
+      debugPrint("Error [onNavigation] ${e.toString()}");
+      router.replaceAll([const LoginRoute()]);
+      return;
     }
     }
   }
   }
 }
 }

+ 8 - 3
mobile/lib/routing/router.dart

@@ -1,5 +1,6 @@
 import 'package:auto_route/auto_route.dart';
 import 'package:auto_route/auto_route.dart';
 import 'package:flutter/material.dart';
 import 'package:flutter/material.dart';
+import 'package:hooks_riverpod/hooks_riverpod.dart';
 import 'package:immich_mobile/modules/backup/views/album_preview_page.dart';
 import 'package:immich_mobile/modules/backup/views/album_preview_page.dart';
 import 'package:immich_mobile/modules/backup/views/backup_album_selection_page.dart';
 import 'package:immich_mobile/modules/backup/views/backup_album_selection_page.dart';
 import 'package:immich_mobile/modules/backup/views/failed_backup_status_page.dart';
 import 'package:immich_mobile/modules/backup/views/failed_backup_status_page.dart';
@@ -9,7 +10,6 @@ import 'package:immich_mobile/modules/home/views/home_page.dart';
 import 'package:immich_mobile/modules/search/views/search_page.dart';
 import 'package:immich_mobile/modules/search/views/search_page.dart';
 import 'package:immich_mobile/modules/search/views/search_result_page.dart';
 import 'package:immich_mobile/modules/search/views/search_result_page.dart';
 import 'package:immich_mobile/modules/sharing/models/asset_selection_page_result.model.dart';
 import 'package:immich_mobile/modules/sharing/models/asset_selection_page_result.model.dart';
-import 'package:immich_mobile/modules/sharing/models/shared_album.model.dart';
 import 'package:immich_mobile/modules/sharing/views/album_viewer_page.dart';
 import 'package:immich_mobile/modules/sharing/views/album_viewer_page.dart';
 import 'package:immich_mobile/modules/sharing/views/asset_selection_page.dart';
 import 'package:immich_mobile/modules/sharing/views/asset_selection_page.dart';
 import 'package:immich_mobile/modules/sharing/views/create_shared_album_page.dart';
 import 'package:immich_mobile/modules/sharing/views/create_shared_album_page.dart';
@@ -17,12 +17,13 @@ import 'package:immich_mobile/modules/sharing/views/select_additional_user_for_s
 import 'package:immich_mobile/modules/sharing/views/select_user_for_sharing_page.dart';
 import 'package:immich_mobile/modules/sharing/views/select_user_for_sharing_page.dart';
 import 'package:immich_mobile/modules/sharing/views/sharing_page.dart';
 import 'package:immich_mobile/modules/sharing/views/sharing_page.dart';
 import 'package:immich_mobile/routing/auth_guard.dart';
 import 'package:immich_mobile/routing/auth_guard.dart';
-import 'package:immich_mobile/shared/models/immich_asset.model.dart';
 import 'package:immich_mobile/modules/backup/views/backup_controller_page.dart';
 import 'package:immich_mobile/modules/backup/views/backup_controller_page.dart';
 import 'package:immich_mobile/modules/asset_viewer/views/image_viewer_page.dart';
 import 'package:immich_mobile/modules/asset_viewer/views/image_viewer_page.dart';
+import 'package:immich_mobile/shared/services/api.service.dart';
 import 'package:immich_mobile/shared/views/splash_screen.dart';
 import 'package:immich_mobile/shared/views/splash_screen.dart';
 import 'package:immich_mobile/shared/views/tab_controller_page.dart';
 import 'package:immich_mobile/shared/views/tab_controller_page.dart';
 import 'package:immich_mobile/modules/asset_viewer/views/video_viewer_page.dart';
 import 'package:immich_mobile/modules/asset_viewer/views/video_viewer_page.dart';
+import 'package:openapi/api.dart';
 import 'package:photo_manager/photo_manager.dart';
 import 'package:photo_manager/photo_manager.dart';
 
 
 part 'router.gr.dart';
 part 'router.gr.dart';
@@ -74,5 +75,9 @@ part 'router.gr.dart';
   ],
   ],
 )
 )
 class AppRouter extends _$AppRouter {
 class AppRouter extends _$AppRouter {
-  AppRouter() : super(authGuard: AuthGuard());
+  final ApiService _apiService;
+  AppRouter(this._apiService) : super(authGuard: AuthGuard(_apiService));
 }
 }
+
+final appRouterProvider =
+    Provider((ref) => AppRouter(ref.watch(apiServiceProvider)));

+ 6 - 6
mobile/lib/routing/router.gr.dart

@@ -234,7 +234,7 @@ class ImageViewerRoute extends PageRouteInfo<ImageViewerRouteArgs> {
       required String imageUrl,
       required String imageUrl,
       required String heroTag,
       required String heroTag,
       required String thumbnailUrl,
       required String thumbnailUrl,
-      required ImmichAsset asset})
+      required AssetResponseDto asset})
       : super(ImageViewerRoute.name,
       : super(ImageViewerRoute.name,
             path: '/image-viewer-page',
             path: '/image-viewer-page',
             args: ImageViewerRouteArgs(
             args: ImageViewerRouteArgs(
@@ -263,7 +263,7 @@ class ImageViewerRouteArgs {
 
 
   final String thumbnailUrl;
   final String thumbnailUrl;
 
 
-  final ImmichAsset asset;
+  final AssetResponseDto asset;
 
 
   @override
   @override
   String toString() {
   String toString() {
@@ -275,7 +275,7 @@ class ImageViewerRouteArgs {
 /// [VideoViewerPage]
 /// [VideoViewerPage]
 class VideoViewerRoute extends PageRouteInfo<VideoViewerRouteArgs> {
 class VideoViewerRoute extends PageRouteInfo<VideoViewerRouteArgs> {
   VideoViewerRoute(
   VideoViewerRoute(
-      {Key? key, required String videoUrl, required ImmichAsset asset})
+      {Key? key, required String videoUrl, required AssetResponseDto asset})
       : super(VideoViewerRoute.name,
       : super(VideoViewerRoute.name,
             path: '/video-viewer-page',
             path: '/video-viewer-page',
             args: VideoViewerRouteArgs(
             args: VideoViewerRouteArgs(
@@ -292,7 +292,7 @@ class VideoViewerRouteArgs {
 
 
   final String videoUrl;
   final String videoUrl;
 
 
-  final ImmichAsset asset;
+  final AssetResponseDto asset;
 
 
   @override
   @override
   String toString() {
   String toString() {
@@ -390,7 +390,7 @@ class AlbumViewerRouteArgs {
 class SelectAdditionalUserForSharingRoute
 class SelectAdditionalUserForSharingRoute
     extends PageRouteInfo<SelectAdditionalUserForSharingRouteArgs> {
     extends PageRouteInfo<SelectAdditionalUserForSharingRouteArgs> {
   SelectAdditionalUserForSharingRoute(
   SelectAdditionalUserForSharingRoute(
-      {Key? key, required SharedAlbum albumInfo})
+      {Key? key, required AlbumResponseDto albumInfo})
       : super(SelectAdditionalUserForSharingRoute.name,
       : super(SelectAdditionalUserForSharingRoute.name,
             path: '/select-additional-user-for-sharing-page',
             path: '/select-additional-user-for-sharing-page',
             args: SelectAdditionalUserForSharingRouteArgs(
             args: SelectAdditionalUserForSharingRouteArgs(
@@ -405,7 +405,7 @@ class SelectAdditionalUserForSharingRouteArgs {
 
 
   final Key? key;
   final Key? key;
 
 
-  final SharedAlbum albumInfo;
+  final AlbumResponseDto albumInfo;
 
 
   @override
   @override
   String toString() {
   String toString() {

+ 3 - 1
mobile/lib/routing/tab_navigation_observer.dart

@@ -23,7 +23,9 @@ class TabNavigationObserver extends AutoRouterObserver {
 
 
   @override
   @override
   Future<void> didChangeTabRoute(
   Future<void> didChangeTabRoute(
-      TabPageRoute route, TabPageRoute previousRoute) async {
+    TabPageRoute route,
+    TabPageRoute previousRoute,
+  ) async {
     // Perform tasks on re-visit to SearchRoute
     // Perform tasks on re-visit to SearchRoute
     if (route.name == 'SearchRoute') {
     if (route.name == 'SearchRoute') {
       // Refresh Location State
       // Refresh Location State

+ 0 - 100
mobile/lib/shared/models/device_info.model.dart

@@ -1,100 +0,0 @@
-import 'dart:convert';
-
-class DeviceInfoRemote {
-  final int id;
-  final String userId;
-  final String deviceId;
-  final String deviceType;
-  final String notificationToken;
-  final String createdAt;
-  final bool isAutoBackup;
-
-  DeviceInfoRemote({
-    required this.id,
-    required this.userId,
-    required this.deviceId,
-    required this.deviceType,
-    required this.notificationToken,
-    required this.createdAt,
-    required this.isAutoBackup,
-  });
-
-  DeviceInfoRemote copyWith({
-    int? id,
-    String? userId,
-    String? deviceId,
-    String? deviceType,
-    String? notificationToken,
-    String? createdAt,
-    bool? isAutoBackup,
-  }) {
-    return DeviceInfoRemote(
-      id: id ?? this.id,
-      userId: userId ?? this.userId,
-      deviceId: deviceId ?? this.deviceId,
-      deviceType: deviceType ?? this.deviceType,
-      notificationToken: notificationToken ?? this.notificationToken,
-      createdAt: createdAt ?? this.createdAt,
-      isAutoBackup: isAutoBackup ?? this.isAutoBackup,
-    );
-  }
-
-  Map<String, dynamic> toMap() {
-    return {
-      'id': id,
-      'userId': userId,
-      'deviceId': deviceId,
-      'deviceType': deviceType,
-      'notificationToken': notificationToken,
-      'createdAt': createdAt,
-      'isAutoBackup': isAutoBackup,
-    };
-  }
-
-  factory DeviceInfoRemote.fromMap(Map<String, dynamic> map) {
-    return DeviceInfoRemote(
-      id: map['id']?.toInt() ?? 0,
-      userId: map['userId'] ?? '',
-      deviceId: map['deviceId'] ?? '',
-      deviceType: map['deviceType'] ?? '',
-      notificationToken: map['notificationToken'] ?? '',
-      createdAt: map['createdAt'] ?? '',
-      isAutoBackup: map['isAutoBackup'] ?? false,
-    );
-  }
-
-  String toJson() => json.encode(toMap());
-
-  factory DeviceInfoRemote.fromJson(String source) =>
-      DeviceInfoRemote.fromMap(json.decode(source));
-
-  @override
-  String toString() {
-    return 'DeviceInfo(id: $id, userId: $userId, deviceId: $deviceId, deviceType: $deviceType, notificationToken: $notificationToken, createdAt: $createdAt, isAutoBackup: $isAutoBackup)';
-  }
-
-  @override
-  bool operator ==(Object other) {
-    if (identical(this, other)) return true;
-
-    return other is DeviceInfoRemote &&
-        other.id == id &&
-        other.userId == userId &&
-        other.deviceId == deviceId &&
-        other.deviceType == deviceType &&
-        other.notificationToken == notificationToken &&
-        other.createdAt == createdAt &&
-        other.isAutoBackup == isAutoBackup;
-  }
-
-  @override
-  int get hashCode {
-    return id.hashCode ^
-        userId.hashCode ^
-        deviceId.hashCode ^
-        deviceType.hashCode ^
-        notificationToken.hashCode ^
-        createdAt.hashCode ^
-        isAutoBackup.hashCode;
-  }
-}

+ 0 - 212
mobile/lib/shared/models/exif.model.dart

@@ -1,212 +0,0 @@
-import 'dart:convert';
-
-class ImmichExif {
-  final int? id;
-  final String? assetId;
-  final String? make;
-  final String? model;
-  final String? imageName;
-  final int? exifImageWidth;
-  final int? exifImageHeight;
-  final int? fileSizeInByte;
-  final String? orientation;
-  final String? dateTimeOriginal;
-  final String? modifyDate;
-  final String? lensModel;
-  final double? fNumber;
-  final double? focalLength;
-  final int? iso;
-  final double? exposureTime;
-  final double? latitude;
-  final double? longitude;
-  final String? city;
-  final String? state;
-  final String? country;
-
-  ImmichExif({
-    this.id,
-    this.assetId,
-    this.make,
-    this.model,
-    this.imageName,
-    this.exifImageWidth,
-    this.exifImageHeight,
-    this.fileSizeInByte,
-    this.orientation,
-    this.dateTimeOriginal,
-    this.modifyDate,
-    this.lensModel,
-    this.fNumber,
-    this.focalLength,
-    this.iso,
-    this.exposureTime,
-    this.latitude,
-    this.longitude,
-    this.city,
-    this.state,
-    this.country,
-  });
-
-  ImmichExif copyWith({
-    int? id,
-    String? assetId,
-    String? make,
-    String? model,
-    String? imageName,
-    int? exifImageWidth,
-    int? exifImageHeight,
-    int? fileSizeInByte,
-    String? orientation,
-    String? dateTimeOriginal,
-    String? modifyDate,
-    String? lensModel,
-    double? fNumber,
-    double? focalLength,
-    int? iso,
-    double? exposureTime,
-    double? latitude,
-    double? longitude,
-    String? city,
-    String? state,
-    String? country,
-  }) {
-    return ImmichExif(
-      id: id ?? this.id,
-      assetId: assetId ?? this.assetId,
-      make: make ?? this.make,
-      model: model ?? this.model,
-      imageName: imageName ?? this.imageName,
-      exifImageWidth: exifImageWidth ?? this.exifImageWidth,
-      exifImageHeight: exifImageHeight ?? this.exifImageHeight,
-      fileSizeInByte: fileSizeInByte ?? this.fileSizeInByte,
-      orientation: orientation ?? this.orientation,
-      dateTimeOriginal: dateTimeOriginal ?? this.dateTimeOriginal,
-      modifyDate: modifyDate ?? this.modifyDate,
-      lensModel: lensModel ?? this.lensModel,
-      fNumber: fNumber ?? this.fNumber,
-      focalLength: focalLength ?? this.focalLength,
-      iso: iso ?? this.iso,
-      exposureTime: exposureTime ?? this.exposureTime,
-      latitude: latitude ?? this.latitude,
-      longitude: longitude ?? this.longitude,
-      city: city ?? this.city,
-      state: state ?? this.state,
-      country: country ?? this.country,
-    );
-  }
-
-  Map<String, dynamic> toMap() {
-    return {
-      'id': id,
-      'assetId': assetId,
-      'make': make,
-      'model': model,
-      'imageName': imageName,
-      'exifImageWidth': exifImageWidth,
-      'exifImageHeight': exifImageHeight,
-      'fileSizeInByte': fileSizeInByte,
-      'orientation': orientation,
-      'dateTimeOriginal': dateTimeOriginal,
-      'modifyDate': modifyDate,
-      'lensModel': lensModel,
-      'fNumber': fNumber,
-      'focalLength': focalLength,
-      'iso': iso,
-      'exposureTime': exposureTime,
-      'latitude': latitude,
-      'longitude': longitude,
-      'city': city,
-      'state': state,
-      'country': country,
-    };
-  }
-
-  factory ImmichExif.fromMap(Map<String, dynamic> map) {
-    return ImmichExif(
-      id: map['id']?.toInt(),
-      assetId: map['assetId'],
-      make: map['make'],
-      model: map['model'],
-      imageName: map['imageName'],
-      exifImageWidth: map['exifImageWidth']?.toInt(),
-      exifImageHeight: map['exifImageHeight']?.toInt(),
-      fileSizeInByte: map['fileSizeInByte']?.toInt(),
-      orientation: map['orientation'],
-      dateTimeOriginal: map['dateTimeOriginal'],
-      modifyDate: map['modifyDate'],
-      lensModel: map['lensModel'],
-      fNumber: map['fNumber']?.toDouble(),
-      focalLength: map['focalLength']?.toDouble(),
-      iso: map['iso']?.toInt(),
-      exposureTime: map['exposureTime']?.toDouble(),
-      latitude: map['latitude']?.toDouble(),
-      longitude: map['longitude']?.toDouble(),
-      city: map['city'],
-      state: map['state'],
-      country: map['country'],
-    );
-  }
-
-  String toJson() => json.encode(toMap());
-
-  factory ImmichExif.fromJson(String source) =>
-      ImmichExif.fromMap(json.decode(source));
-
-  @override
-  String toString() {
-    return 'ImmichExif(id: $id, assetId: $assetId, make: $make, model: $model, imageName: $imageName, exifImageWidth: $exifImageWidth, exifImageHeight: $exifImageHeight, fileSizeInByte: $fileSizeInByte, orientation: $orientation, dateTimeOriginal: $dateTimeOriginal, modifyDate: $modifyDate, lensModel: $lensModel, fNumber: $fNumber, focalLength: $focalLength, iso: $iso, exposureTime: $exposureTime, latitude: $latitude, longitude: $longitude, city: $city, state: $state, country: $country)';
-  }
-
-  @override
-  bool operator ==(Object other) {
-    if (identical(this, other)) return true;
-
-    return other is ImmichExif &&
-        other.id == id &&
-        other.assetId == assetId &&
-        other.make == make &&
-        other.model == model &&
-        other.imageName == imageName &&
-        other.exifImageWidth == exifImageWidth &&
-        other.exifImageHeight == exifImageHeight &&
-        other.fileSizeInByte == fileSizeInByte &&
-        other.orientation == orientation &&
-        other.dateTimeOriginal == dateTimeOriginal &&
-        other.modifyDate == modifyDate &&
-        other.lensModel == lensModel &&
-        other.fNumber == fNumber &&
-        other.focalLength == focalLength &&
-        other.iso == iso &&
-        other.exposureTime == exposureTime &&
-        other.latitude == latitude &&
-        other.longitude == longitude &&
-        other.city == city &&
-        other.state == state &&
-        other.country == country;
-  }
-
-  @override
-  int get hashCode {
-    return id.hashCode ^
-        assetId.hashCode ^
-        make.hashCode ^
-        model.hashCode ^
-        imageName.hashCode ^
-        exifImageWidth.hashCode ^
-        exifImageHeight.hashCode ^
-        fileSizeInByte.hashCode ^
-        orientation.hashCode ^
-        dateTimeOriginal.hashCode ^
-        modifyDate.hashCode ^
-        lensModel.hashCode ^
-        fNumber.hashCode ^
-        focalLength.hashCode ^
-        iso.hashCode ^
-        exposureTime.hashCode ^
-        latitude.hashCode ^
-        longitude.hashCode ^
-        city.hashCode ^
-        state.hashCode ^
-        country.hashCode;
-  }
-}

+ 0 - 110
mobile/lib/shared/models/immich_asset.model.dart

@@ -1,110 +0,0 @@
-import 'dart:convert';
-
-import 'package:equatable/equatable.dart';
-
-class ImmichAsset extends Equatable {
-  final String id;
-  final String deviceAssetId;
-  final String userId;
-  final String deviceId;
-  final String type;
-  final String createdAt;
-  final String modifiedAt;
-  final bool isFavorite;
-  final String? duration;
-  final String originalPath;
-  final String resizePath;
-
-  const ImmichAsset({
-    required this.id,
-    required this.deviceAssetId,
-    required this.userId,
-    required this.deviceId,
-    required this.type,
-    required this.createdAt,
-    required this.modifiedAt,
-    required this.isFavorite,
-    this.duration,
-    required this.originalPath,
-    required this.resizePath,
-  });
-
-  ImmichAsset copyWith({
-    String? id,
-    String? deviceAssetId,
-    String? userId,
-    String? deviceId,
-    String? type,
-    String? createdAt,
-    String? modifiedAt,
-    bool? isFavorite,
-    String? duration,
-    String? originalPath,
-    String? resizePath,
-  }) {
-    return ImmichAsset(
-      id: id ?? this.id,
-      deviceAssetId: deviceAssetId ?? this.deviceAssetId,
-      userId: userId ?? this.userId,
-      deviceId: deviceId ?? this.deviceId,
-      type: type ?? this.type,
-      createdAt: createdAt ?? this.createdAt,
-      modifiedAt: modifiedAt ?? this.modifiedAt,
-      isFavorite: isFavorite ?? this.isFavorite,
-      duration: duration ?? this.duration,
-      originalPath: originalPath ?? this.originalPath,
-      resizePath: resizePath ?? this.resizePath,
-    );
-  }
-
-  Map<String, dynamic> toMap() {
-    final result = <String, dynamic>{};
-
-    result.addAll({'id': id});
-    result.addAll({'deviceAssetId': deviceAssetId});
-    result.addAll({'userId': userId});
-    result.addAll({'deviceId': deviceId});
-    result.addAll({'type': type});
-    result.addAll({'createdAt': createdAt});
-    result.addAll({'modifiedAt': modifiedAt});
-    result.addAll({'isFavorite': isFavorite});
-    if (duration != null) {
-      result.addAll({'duration': duration});
-    }
-    result.addAll({'originalPath': originalPath});
-    result.addAll({'resizePath': resizePath});
-
-    return result;
-  }
-
-  factory ImmichAsset.fromMap(Map<String, dynamic> map) {
-    return ImmichAsset(
-      id: map['id'] ?? '',
-      deviceAssetId: map['deviceAssetId'] ?? '',
-      userId: map['userId'] ?? '',
-      deviceId: map['deviceId'] ?? '',
-      type: map['type'] ?? '',
-      createdAt: map['createdAt'] ?? '',
-      modifiedAt: map['modifiedAt'] ?? '',
-      isFavorite: map['isFavorite'] ?? false,
-      duration: map['duration'],
-      originalPath: map['originalPath'] ?? '',
-      resizePath: map['resizePath'] ?? '',
-    );
-  }
-
-  String toJson() => json.encode(toMap());
-
-  factory ImmichAsset.fromJson(String source) =>
-      ImmichAsset.fromMap(json.decode(source));
-
-  @override
-  String toString() {
-    return 'ImmichAsset(id: $id, deviceAssetId: $deviceAssetId, userId: $userId, deviceId: $deviceId, type: $type, createdAt: $createdAt, modifiedAt: $modifiedAt, isFavorite: $isFavorite, duration: $duration, originalPath: $originalPath, resizePath: $resizePath)';
-  }
-
-  @override
-  List<Object> get props {
-    return [id];
-  }
-}

+ 0 - 135
mobile/lib/shared/models/immich_asset_with_exif.model.dart

@@ -1,135 +0,0 @@
-import 'dart:convert';
-
-import 'package:immich_mobile/shared/models/exif.model.dart';
-
-class ImmichAssetWithExif {
-  final String id;
-  final String deviceAssetId;
-  final String userId;
-  final String deviceId;
-  final String type;
-  final String createdAt;
-  final String modifiedAt;
-  final String originalPath;
-  final bool isFavorite;
-  final String? duration;
-  final ImmichExif? exifInfo;
-
-  ImmichAssetWithExif({
-    required this.id,
-    required this.deviceAssetId,
-    required this.userId,
-    required this.deviceId,
-    required this.type,
-    required this.createdAt,
-    required this.modifiedAt,
-    required this.originalPath,
-    required this.isFavorite,
-    this.duration,
-    this.exifInfo,
-  });
-
-  ImmichAssetWithExif copyWith({
-    String? id,
-    String? deviceAssetId,
-    String? userId,
-    String? deviceId,
-    String? type,
-    String? createdAt,
-    String? modifiedAt,
-    String? originalPath,
-    bool? isFavorite,
-    String? duration,
-    ImmichExif? exifInfo,
-  }) {
-    return ImmichAssetWithExif(
-      id: id ?? this.id,
-      deviceAssetId: deviceAssetId ?? this.deviceAssetId,
-      userId: userId ?? this.userId,
-      deviceId: deviceId ?? this.deviceId,
-      type: type ?? this.type,
-      createdAt: createdAt ?? this.createdAt,
-      modifiedAt: modifiedAt ?? this.modifiedAt,
-      originalPath: originalPath ?? this.originalPath,
-      isFavorite: isFavorite ?? this.isFavorite,
-      duration: duration ?? this.duration,
-      exifInfo: exifInfo ?? this.exifInfo,
-    );
-  }
-
-  Map<String, dynamic> toMap() {
-    return {
-      'id': id,
-      'deviceAssetId': deviceAssetId,
-      'userId': userId,
-      'deviceId': deviceId,
-      'type': type,
-      'createdAt': createdAt,
-      'modifiedAt': modifiedAt,
-      'originalPath': originalPath,
-      'isFavorite': isFavorite,
-      'duration': duration,
-      'exifInfo': exifInfo?.toMap(),
-    };
-  }
-
-  factory ImmichAssetWithExif.fromMap(Map<String, dynamic> map) {
-    return ImmichAssetWithExif(
-      id: map['id'] ?? '',
-      deviceAssetId: map['deviceAssetId'] ?? '',
-      userId: map['userId'] ?? '',
-      deviceId: map['deviceId'] ?? '',
-      type: map['type'] ?? '',
-      createdAt: map['createdAt'] ?? '',
-      modifiedAt: map['modifiedAt'] ?? '',
-      originalPath: map['originalPath'] ?? '',
-      isFavorite: map['isFavorite'] ?? false,
-      duration: map['duration'],
-      exifInfo:
-          map['exifInfo'] != null ? ImmichExif.fromMap(map['exifInfo']) : null,
-    );
-  }
-
-  String toJson() => json.encode(toMap());
-
-  factory ImmichAssetWithExif.fromJson(String source) =>
-      ImmichAssetWithExif.fromMap(json.decode(source));
-
-  @override
-  String toString() {
-    return 'ImmichAssetWithExif(id: $id, deviceAssetId: $deviceAssetId, userId: $userId, deviceId: $deviceId, type: $type, createdAt: $createdAt, modifiedAt: $modifiedAt, originalPath: $originalPath, isFavorite: $isFavorite, duration: $duration, exifInfo: $exifInfo)';
-  }
-
-  @override
-  bool operator ==(Object other) {
-    if (identical(this, other)) return true;
-
-    return other is ImmichAssetWithExif &&
-        other.id == id &&
-        other.deviceAssetId == deviceAssetId &&
-        other.userId == userId &&
-        other.deviceId == deviceId &&
-        other.type == type &&
-        other.createdAt == createdAt &&
-        other.modifiedAt == modifiedAt &&
-        other.originalPath == originalPath &&
-        other.isFavorite == isFavorite &&
-        other.duration == duration &&
-        other.exifInfo == exifInfo;
-  }
-
-  @override
-  int get hashCode {
-    return id.hashCode ^
-        deviceAssetId.hashCode ^
-        userId.hashCode ^
-        deviceId.hashCode ^
-        type.hashCode ^
-        createdAt.hashCode ^
-        modifiedAt.hashCode ^
-        originalPath.hashCode ^
-        isFavorite.hashCode ^
-        duration.hashCode ^
-        exifInfo.hashCode;
-  }
-}

+ 0 - 55
mobile/lib/shared/models/mapbox_info.model.dart

@@ -1,55 +0,0 @@
-import 'dart:convert';
-
-class MapboxInfo {
-  final bool isEnable;
-  final String mapboxSecret;
-  MapboxInfo({
-    required this.isEnable,
-    required this.mapboxSecret,
-  });
-
-  MapboxInfo copyWith({
-    bool? isEnable,
-    String? mapboxSecret,
-  }) {
-    return MapboxInfo(
-      isEnable: isEnable ?? this.isEnable,
-      mapboxSecret: mapboxSecret ?? this.mapboxSecret,
-    );
-  }
-
-  Map<String, dynamic> toMap() {
-    return {
-      'isEnable': isEnable,
-      'mapboxSecret': mapboxSecret,
-    };
-  }
-
-  factory MapboxInfo.fromMap(Map<String, dynamic> map) {
-    return MapboxInfo(
-      isEnable: map['isEnable'] ?? false,
-      mapboxSecret: map['mapboxSecret'] ?? '',
-    );
-  }
-
-  String toJson() => json.encode(toMap());
-
-  factory MapboxInfo.fromJson(String source) =>
-      MapboxInfo.fromMap(json.decode(source));
-
-  @override
-  String toString() =>
-      'MapboxInfo(isEnable: $isEnable, mapboxSecret: $mapboxSecret)';
-
-  @override
-  bool operator ==(Object other) {
-    if (identical(this, other)) return true;
-
-    return other is MapboxInfo &&
-        other.isEnable == isEnable &&
-        other.mapboxSecret == mapboxSecret;
-  }
-
-  @override
-  int get hashCode => isEnable.hashCode ^ mapboxSecret.hashCode;
-}

+ 0 - 99
mobile/lib/shared/models/server_info.model.dart

@@ -1,99 +0,0 @@
-import 'dart:convert';
-
-class ServerInfo {
-  final String diskSize;
-  final String diskUse;
-  final String diskAvailable;
-  final int diskSizeRaw;
-  final int diskUseRaw;
-  final int diskAvailableRaw;
-  final double diskUsagePercentage;
-  ServerInfo({
-    required this.diskSize,
-    required this.diskUse,
-    required this.diskAvailable,
-    required this.diskSizeRaw,
-    required this.diskUseRaw,
-    required this.diskAvailableRaw,
-    required this.diskUsagePercentage,
-  });
-
-  ServerInfo copyWith({
-    String? diskSize,
-    String? diskUse,
-    String? diskAvailable,
-    int? diskSizeRaw,
-    int? diskUseRaw,
-    int? diskAvailableRaw,
-    double? diskUsagePercentage,
-  }) {
-    return ServerInfo(
-      diskSize: diskSize ?? this.diskSize,
-      diskUse: diskUse ?? this.diskUse,
-      diskAvailable: diskAvailable ?? this.diskAvailable,
-      diskSizeRaw: diskSizeRaw ?? this.diskSizeRaw,
-      diskUseRaw: diskUseRaw ?? this.diskUseRaw,
-      diskAvailableRaw: diskAvailableRaw ?? this.diskAvailableRaw,
-      diskUsagePercentage: diskUsagePercentage ?? this.diskUsagePercentage,
-    );
-  }
-
-  Map<String, dynamic> toMap() {
-    return {
-      'diskSize': diskSize,
-      'diskUse': diskUse,
-      'diskAvailable': diskAvailable,
-      'diskSizeRaw': diskSizeRaw,
-      'diskUseRaw': diskUseRaw,
-      'diskAvailableRaw': diskAvailableRaw,
-      'diskUsagePercentage': diskUsagePercentage,
-    };
-  }
-
-  factory ServerInfo.fromMap(Map<String, dynamic> map) {
-    return ServerInfo(
-      diskSize: map['diskSize'] ?? '',
-      diskUse: map['diskUse'] ?? '',
-      diskAvailable: map['diskAvailable'] ?? '',
-      diskSizeRaw: map['diskSizeRaw']?.toInt() ?? 0,
-      diskUseRaw: map['diskUseRaw']?.toInt() ?? 0,
-      diskAvailableRaw: map['diskAvailableRaw']?.toInt() ?? 0,
-      diskUsagePercentage: map['diskUsagePercentage']?.toDouble() ?? 0.0,
-    );
-  }
-
-  String toJson() => json.encode(toMap());
-
-  factory ServerInfo.fromJson(String source) =>
-      ServerInfo.fromMap(json.decode(source));
-
-  @override
-  String toString() {
-    return 'ServerInfo(diskSize: $diskSize, diskUse: $diskUse, diskAvailable: $diskAvailable, diskSizeRaw: $diskSizeRaw, diskUseRaw: $diskUseRaw, diskAvailableRaw: $diskAvailableRaw, diskUsagePercentage: $diskUsagePercentage)';
-  }
-
-  @override
-  bool operator ==(Object other) {
-    if (identical(this, other)) return true;
-
-    return other is ServerInfo &&
-        other.diskSize == diskSize &&
-        other.diskUse == diskUse &&
-        other.diskAvailable == diskAvailable &&
-        other.diskSizeRaw == diskSizeRaw &&
-        other.diskUseRaw == diskUseRaw &&
-        other.diskAvailableRaw == diskAvailableRaw &&
-        other.diskUsagePercentage == diskUsagePercentage;
-  }
-
-  @override
-  int get hashCode {
-    return diskSize.hashCode ^
-        diskUse.hashCode ^
-        diskAvailable.hashCode ^
-        diskSizeRaw.hashCode ^
-        diskUseRaw.hashCode ^
-        diskAvailableRaw.hashCode ^
-        diskUsagePercentage.hashCode;
-  }
-}

+ 5 - 37
mobile/lib/shared/models/server_info_state.model.dart

@@ -1,29 +1,22 @@
-import 'dart:convert';
-
-import 'package:immich_mobile/shared/models/mapbox_info.model.dart';
-import 'package:immich_mobile/shared/models/server_version.model.dart';
+import 'package:openapi/api.dart';
 
 
 class ServerInfoState {
 class ServerInfoState {
-  final MapboxInfo mapboxInfo;
-  final ServerVersion serverVersion;
+  final ServerVersionReponseDto serverVersion;
   final bool isVersionMismatch;
   final bool isVersionMismatch;
   final String versionMismatchErrorMessage;
   final String versionMismatchErrorMessage;
 
 
   ServerInfoState({
   ServerInfoState({
-    required this.mapboxInfo,
     required this.serverVersion,
     required this.serverVersion,
     required this.isVersionMismatch,
     required this.isVersionMismatch,
     required this.versionMismatchErrorMessage,
     required this.versionMismatchErrorMessage,
   });
   });
 
 
   ServerInfoState copyWith({
   ServerInfoState copyWith({
-    MapboxInfo? mapboxInfo,
-    ServerVersion? serverVersion,
+    ServerVersionReponseDto? serverVersion,
     bool? isVersionMismatch,
     bool? isVersionMismatch,
     String? versionMismatchErrorMessage,
     String? versionMismatchErrorMessage,
   }) {
   }) {
     return ServerInfoState(
     return ServerInfoState(
-      mapboxInfo: mapboxInfo ?? this.mapboxInfo,
       serverVersion: serverVersion ?? this.serverVersion,
       serverVersion: serverVersion ?? this.serverVersion,
       isVersionMismatch: isVersionMismatch ?? this.isVersionMismatch,
       isVersionMismatch: isVersionMismatch ?? this.isVersionMismatch,
       versionMismatchErrorMessage:
       versionMismatchErrorMessage:
@@ -31,32 +24,9 @@ class ServerInfoState {
     );
     );
   }
   }
 
 
-  Map<String, dynamic> toMap() {
-    return {
-      'mapboxInfo': mapboxInfo.toMap(),
-      'serverVersion': serverVersion.toMap(),
-      'isVersionMismatch': isVersionMismatch,
-      'versionMismatchErrorMessage': versionMismatchErrorMessage,
-    };
-  }
-
-  factory ServerInfoState.fromMap(Map<String, dynamic> map) {
-    return ServerInfoState(
-      mapboxInfo: MapboxInfo.fromMap(map['mapboxInfo']),
-      serverVersion: ServerVersion.fromMap(map['serverVersion']),
-      isVersionMismatch: map['isVersionMismatch'] ?? false,
-      versionMismatchErrorMessage: map['versionMismatchErrorMessage'] ?? '',
-    );
-  }
-
-  String toJson() => json.encode(toMap());
-
-  factory ServerInfoState.fromJson(String source) =>
-      ServerInfoState.fromMap(json.decode(source));
-
   @override
   @override
   String toString() {
   String toString() {
-    return 'ServerInfoState(mapboxInfo: $mapboxInfo, serverVersion: $serverVersion, isVersionMismatch: $isVersionMismatch, versionMismatchErrorMessage: $versionMismatchErrorMessage)';
+    return 'ServerInfoState( serverVersion: $serverVersion, isVersionMismatch: $isVersionMismatch, versionMismatchErrorMessage: $versionMismatchErrorMessage)';
   }
   }
 
 
   @override
   @override
@@ -64,7 +34,6 @@ class ServerInfoState {
     if (identical(this, other)) return true;
     if (identical(this, other)) return true;
 
 
     return other is ServerInfoState &&
     return other is ServerInfoState &&
-        other.mapboxInfo == mapboxInfo &&
         other.serverVersion == serverVersion &&
         other.serverVersion == serverVersion &&
         other.isVersionMismatch == isVersionMismatch &&
         other.isVersionMismatch == isVersionMismatch &&
         other.versionMismatchErrorMessage == versionMismatchErrorMessage;
         other.versionMismatchErrorMessage == versionMismatchErrorMessage;
@@ -72,8 +41,7 @@ class ServerInfoState {
 
 
   @override
   @override
   int get hashCode {
   int get hashCode {
-    return mapboxInfo.hashCode ^
-        serverVersion.hashCode ^
+    return serverVersion.hashCode ^
         isVersionMismatch.hashCode ^
         isVersionMismatch.hashCode ^
         versionMismatchErrorMessage.hashCode;
         versionMismatchErrorMessage.hashCode;
   }
   }

+ 0 - 73
mobile/lib/shared/models/server_version.model.dart

@@ -1,73 +0,0 @@
-import 'dart:convert';
-
-class ServerVersion {
-  final int major;
-  final int minor;
-  final int patch;
-  final int build;
-
-  ServerVersion({
-    required this.major,
-    required this.minor,
-    required this.patch,
-    required this.build,
-  });
-
-  ServerVersion copyWith({
-    int? major,
-    int? minor,
-    int? patch,
-    int? build,
-  }) {
-    return ServerVersion(
-      major: major ?? this.major,
-      minor: minor ?? this.minor,
-      patch: patch ?? this.patch,
-      build: build ?? this.build,
-    );
-  }
-
-  Map<String, dynamic> toMap() {
-    return {
-      'major': major,
-      'minor': minor,
-      'patch': patch,
-      'build': build,
-    };
-  }
-
-  factory ServerVersion.fromMap(Map<String, dynamic> map) {
-    return ServerVersion(
-      major: map['major']?.toInt() ?? 0,
-      minor: map['minor']?.toInt() ?? 0,
-      patch: map['patch']?.toInt() ?? 0,
-      build: map['build']?.toInt() ?? 0,
-    );
-  }
-
-  String toJson() => json.encode(toMap());
-
-  factory ServerVersion.fromJson(String source) =>
-      ServerVersion.fromMap(json.decode(source));
-
-  @override
-  String toString() {
-    return 'ServerVersion(major: $major, minor: $minor, patch: $patch, build: $build)';
-  }
-
-  @override
-  bool operator ==(Object other) {
-    if (identical(this, other)) return true;
-
-    return other is ServerVersion &&
-        other.major == major &&
-        other.minor == minor &&
-        other.patch == patch &&
-        other.build == build;
-  }
-
-  @override
-  int get hashCode {
-    return major.hashCode ^ minor.hashCode ^ patch.hashCode ^ build.hashCode;
-  }
-}

+ 0 - 76
mobile/lib/shared/models/user.model.dart

@@ -1,76 +0,0 @@
-import 'dart:convert';
-
-class User {
-  final String id;
-  final String email;
-  final String createdAt;
-  final String firstName;
-  final String lastName;
-
-  User({
-    required this.id,
-    required this.email,
-    required this.createdAt,
-    required this.firstName,
-    required this.lastName,
-  });
-
-  User copyWith({
-    String? id,
-    String? email,
-    String? createdAt,
-    String? firstName,
-    String? lastName,
-  }) {
-    return User(
-      id: id ?? this.id,
-      email: email ?? this.email,
-      createdAt: createdAt ?? this.createdAt,
-      firstName: firstName ?? this.firstName,
-      lastName: lastName ?? this.lastName,
-    );
-  }
-
-  Map<String, dynamic> toMap() {
-    final result = <String, dynamic>{};
-
-    result.addAll({'id': id});
-    result.addAll({'email': email});
-    result.addAll({'createdAt': createdAt});
-
-    return result;
-  }
-
-  factory User.fromMap(Map<String, dynamic> map) {
-    return User(
-      id: map['id'] ?? '',
-      email: map['email'] ?? '',
-      createdAt: map['createdAt'] ?? '',
-      firstName: map['firstName'] ?? '',
-      lastName: map['lastName'] ?? '',
-    );
-  }
-
-  String toJson() => json.encode(toMap());
-
-  factory User.fromJson(String source) => User.fromMap(json.decode(source));
-
-  @override
-  String toString() =>
-      'UserInfo(id: $id, email: $email, createdAt: $createdAt)';
-
-  @override
-  bool operator ==(Object other) {
-    if (identical(this, other)) return true;
-
-    return other is User &&
-        other.id == id &&
-        other.email == email &&
-        other.createdAt == createdAt &&
-        other.firstName == firstName &&
-        other.lastName == lastName;
-  }
-
-  @override
-  int get hashCode => id.hashCode ^ email.hashCode ^ createdAt.hashCode;
-}

+ 24 - 16
mobile/lib/shared/providers/asset.provider.dart

@@ -1,21 +1,20 @@
 import 'package:flutter/foundation.dart';
 import 'package:flutter/foundation.dart';
 import 'package:hooks_riverpod/hooks_riverpod.dart';
 import 'package:hooks_riverpod/hooks_riverpod.dart';
-import 'package:immich_mobile/modules/home/models/delete_asset_response.model.dart';
 import 'package:immich_mobile/modules/home/services/asset.service.dart';
 import 'package:immich_mobile/modules/home/services/asset.service.dart';
-import 'package:immich_mobile/shared/models/immich_asset.model.dart';
 import 'package:immich_mobile/shared/services/device_info.service.dart';
 import 'package:immich_mobile/shared/services/device_info.service.dart';
 import 'package:collection/collection.dart';
 import 'package:collection/collection.dart';
 import 'package:intl/intl.dart';
 import 'package:intl/intl.dart';
+import 'package:openapi/api.dart';
 import 'package:photo_manager/photo_manager.dart';
 import 'package:photo_manager/photo_manager.dart';
 
 
-class AssetNotifier extends StateNotifier<List<ImmichAsset>> {
+class AssetNotifier extends StateNotifier<List<AssetResponseDto>> {
   final AssetService _assetService;
   final AssetService _assetService;
   final DeviceInfoService _deviceInfoService = DeviceInfoService();
   final DeviceInfoService _deviceInfoService = DeviceInfoService();
 
 
   AssetNotifier(this._assetService) : super([]);
   AssetNotifier(this._assetService) : super([]);
 
 
   getAllAsset() async {
   getAllAsset() async {
-    List<ImmichAsset>? allAssets = await _assetService.getAllAsset();
+    var allAssets = await _assetService.getAllAsset();
 
 
     if (allAssets != null) {
     if (allAssets != null) {
       state = allAssets;
       state = allAssets;
@@ -26,11 +25,11 @@ class AssetNotifier extends StateNotifier<List<ImmichAsset>> {
     state = [];
     state = [];
   }
   }
 
 
-  onNewAssetUploaded(ImmichAsset newAsset) {
+  onNewAssetUploaded(AssetResponseDto newAsset) {
     state = [...state, newAsset];
     state = [...state, newAsset];
   }
   }
 
 
-  deleteAssets(Set<ImmichAsset> deleteAssets) async {
+  deleteAssets(Set<AssetResponseDto> deleteAssets) async {
     var deviceInfo = await _deviceInfoService.getDeviceInfo();
     var deviceInfo = await _deviceInfoService.getDeviceInfo();
     var deviceId = deviceInfo["deviceId"];
     var deviceId = deviceInfo["deviceId"];
     var deleteIdList = <String>[];
     var deleteIdList = <String>[];
@@ -53,14 +52,15 @@ class AssetNotifier extends StateNotifier<List<ImmichAsset>> {
     }
     }
 
 
     // Delete asset on server
     // Delete asset on server
-    List<DeleteAssetResponse>? deleteAssetResult =
+    List<DeleteAssetResponseDto>? deleteAssetResult =
         await _assetService.deleteAssets(deleteAssets);
         await _assetService.deleteAssets(deleteAssets);
+
     if (deleteAssetResult == null) {
     if (deleteAssetResult == null) {
       return;
       return;
     }
     }
 
 
     for (var asset in deleteAssetResult) {
     for (var asset in deleteAssetResult) {
-      if (asset.status == 'success') {
+      if (asset.status == DeleteAssetStatus.SUCCESS) {
         state =
         state =
             state.where((immichAsset) => immichAsset.id != asset.id).toList();
             state.where((immichAsset) => immichAsset.id != asset.id).toList();
       }
       }
@@ -69,7 +69,7 @@ class AssetNotifier extends StateNotifier<List<ImmichAsset>> {
 }
 }
 
 
 final assetProvider =
 final assetProvider =
-    StateNotifierProvider<AssetNotifier, List<ImmichAsset>>((ref) {
+    StateNotifierProvider<AssetNotifier, List<AssetResponseDto>>((ref) {
   return AssetNotifier(ref.watch(assetServiceProvider));
   return AssetNotifier(ref.watch(assetServiceProvider));
 });
 });
 
 
@@ -77,17 +77,25 @@ final assetGroupByDateTimeProvider = StateProvider((ref) {
   var assets = ref.watch(assetProvider);
   var assets = ref.watch(assetProvider);
 
 
   assets.sortByCompare<DateTime>(
   assets.sortByCompare<DateTime>(
-      (e) => DateTime.parse(e.createdAt), (a, b) => b.compareTo(a));
-  return assets.groupListsBy((element) =>
-      DateFormat('y-MM-dd').format(DateTime.parse(element.createdAt)));
+    (e) => DateTime.parse(e.createdAt),
+    (a, b) => b.compareTo(a),
+  );
+  return assets.groupListsBy(
+    (element) =>
+        DateFormat('y-MM-dd').format(DateTime.parse(element.createdAt)),
+  );
 });
 });
 
 
 final assetGroupByMonthYearProvider = StateProvider((ref) {
 final assetGroupByMonthYearProvider = StateProvider((ref) {
   var assets = ref.watch(assetProvider);
   var assets = ref.watch(assetProvider);
 
 
   assets.sortByCompare<DateTime>(
   assets.sortByCompare<DateTime>(
-      (e) => DateTime.parse(e.createdAt), (a, b) => b.compareTo(a));
-
-  return assets.groupListsBy((element) =>
-      DateFormat('MMMM, y').format(DateTime.parse(element.createdAt)));
+    (e) => DateTime.parse(e.createdAt),
+    (a, b) => b.compareTo(a),
+  );
+
+  return assets.groupListsBy(
+    (element) =>
+        DateFormat('MMMM, y').format(DateTime.parse(element.createdAt)),
+  );
 });
 });

+ 2 - 1
mobile/lib/shared/providers/release_info.provider.dart

@@ -56,4 +56,5 @@ class ReleaseInfoNotifier extends StateNotifier<String> {
 }
 }
 
 
 final releaseInfoProvider = StateNotifierProvider<ReleaseInfoNotifier, String>(
 final releaseInfoProvider = StateNotifierProvider<ReleaseInfoNotifier, String>(
-    (ref) => ReleaseInfoNotifier());
+  (ref) => ReleaseInfoNotifier(),
+);

+ 12 - 7
mobile/lib/shared/providers/server_info.provider.dart

@@ -1,18 +1,20 @@
 import 'package:hooks_riverpod/hooks_riverpod.dart';
 import 'package:hooks_riverpod/hooks_riverpod.dart';
 
 
-import 'package:immich_mobile/shared/models/mapbox_info.model.dart';
 import 'package:immich_mobile/shared/models/server_info_state.model.dart';
 import 'package:immich_mobile/shared/models/server_info_state.model.dart';
-import 'package:immich_mobile/shared/models/server_version.model.dart';
 import 'package:immich_mobile/shared/services/server_info.service.dart';
 import 'package:immich_mobile/shared/services/server_info.service.dart';
+import 'package:openapi/api.dart';
 import 'package:package_info_plus/package_info_plus.dart';
 import 'package:package_info_plus/package_info_plus.dart';
 
 
 class ServerInfoNotifier extends StateNotifier<ServerInfoState> {
 class ServerInfoNotifier extends StateNotifier<ServerInfoState> {
   ServerInfoNotifier(this._serverInfoService)
   ServerInfoNotifier(this._serverInfoService)
       : super(
       : super(
           ServerInfoState(
           ServerInfoState(
-            mapboxInfo: MapboxInfo(isEnable: false, mapboxSecret: ""),
-            serverVersion:
-                ServerVersion(major: 0, patch: 0, minor: 0, build: 0),
+            serverVersion: ServerVersionReponseDto(
+              major: 0,
+              patch_: 0,
+              minor: 0,
+              build: 0,
+            ),
             isVersionMismatch: false,
             isVersionMismatch: false,
             versionMismatchErrorMessage: "",
             versionMismatchErrorMessage: "",
           ),
           ),
@@ -21,7 +23,8 @@ class ServerInfoNotifier extends StateNotifier<ServerInfoState> {
   final ServerInfoService _serverInfoService;
   final ServerInfoService _serverInfoService;
 
 
   getServerVersion() async {
   getServerVersion() async {
-    ServerVersion? serverVersion = await _serverInfoService.getServerVersion();
+    ServerVersionReponseDto? serverVersion =
+        await _serverInfoService.getServerVersion();
 
 
     if (serverVersion == null) {
     if (serverVersion == null) {
       state = state.copyWith(
       state = state.copyWith(
@@ -59,7 +62,9 @@ class ServerInfoNotifier extends StateNotifier<ServerInfoState> {
     }
     }
 
 
     state = state.copyWith(
     state = state.copyWith(
-        isVersionMismatch: false, versionMismatchErrorMessage: "");
+      isVersionMismatch: false,
+      versionMismatchErrorMessage: "",
+    );
   }
   }
 
 
   Map<String, int> _getDetailVersion(String version) {
   Map<String, int> _getDetailVersion(String version) {

+ 11 - 5
mobile/lib/shared/providers/websocket.provider.dart

@@ -5,8 +5,8 @@ import 'package:hive/hive.dart';
 import 'package:hooks_riverpod/hooks_riverpod.dart';
 import 'package:hooks_riverpod/hooks_riverpod.dart';
 import 'package:immich_mobile/constants/hive_box.dart';
 import 'package:immich_mobile/constants/hive_box.dart';
 import 'package:immich_mobile/modules/login/providers/authentication.provider.dart';
 import 'package:immich_mobile/modules/login/providers/authentication.provider.dart';
-import 'package:immich_mobile/shared/models/immich_asset.model.dart';
 import 'package:immich_mobile/shared/providers/asset.provider.dart';
 import 'package:immich_mobile/shared/providers/asset.provider.dart';
+import 'package:openapi/api.dart';
 import 'package:socket_io_client/socket_io_client.dart';
 import 'package:socket_io_client/socket_io_client.dart';
 
 
 class WebscoketState {
 class WebscoketState {
@@ -92,8 +92,11 @@ class WebsocketNotifier extends StateNotifier<WebscoketState> {
 
 
         socket.on('on_upload_success', (data) {
         socket.on('on_upload_success', (data) {
           var jsonString = jsonDecode(data.toString());
           var jsonString = jsonDecode(data.toString());
-          ImmichAsset newAsset = ImmichAsset.fromMap(jsonString);
-          ref.watch(assetProvider.notifier).onNewAssetUploaded(newAsset);
+          AssetResponseDto? newAsset = AssetResponseDto.fromJson(jsonString);
+
+          if (newAsset != null) {
+            ref.watch(assetProvider.notifier).onNewAssetUploaded(newAsset);
+          }
         });
         });
       } catch (e) {
       } catch (e) {
         debugPrint("[WEBSOCKET] Catch Websocket Error - ${e.toString()}");
         debugPrint("[WEBSOCKET] Catch Websocket Error - ${e.toString()}");
@@ -119,8 +122,11 @@ class WebsocketNotifier extends StateNotifier<WebscoketState> {
     debugPrint("[Websocket] Start listening to event on_upload_success");
     debugPrint("[Websocket] Start listening to event on_upload_success");
     state.socket?.on('on_upload_success', (data) {
     state.socket?.on('on_upload_success', (data) {
       var jsonString = jsonDecode(data.toString());
       var jsonString = jsonDecode(data.toString());
-      ImmichAsset newAsset = ImmichAsset.fromMap(jsonString);
-      ref.watch(assetProvider.notifier).onNewAssetUploaded(newAsset);
+      AssetResponseDto? newAsset = AssetResponseDto.fromJson(jsonString);
+
+      if (newAsset != null) {
+        ref.watch(assetProvider.notifier).onNewAssetUploaded(newAsset);
+      }
     });
     });
   }
   }
 }
 }

+ 30 - 0
mobile/lib/shared/services/api.service.dart

@@ -0,0 +1,30 @@
+import 'package:hooks_riverpod/hooks_riverpod.dart';
+import 'package:openapi/api.dart';
+
+final apiServiceProvider = Provider((ref) => ApiService());
+
+class ApiService {
+  late ApiClient _apiClient;
+
+  late UserApi userApi;
+  late AuthenticationApi authenticationApi;
+  late AlbumApi albumApi;
+  late AssetApi assetApi;
+  late ServerInfoApi serverInfoApi;
+  late DeviceInfoApi deviceInfoApi;
+
+  setEndpoint(String endpoint) {
+    _apiClient = ApiClient(basePath: endpoint);
+
+    userApi = UserApi(_apiClient);
+    authenticationApi = AuthenticationApi(_apiClient);
+    albumApi = AlbumApi(_apiClient);
+    assetApi = AssetApi(_apiClient);
+    serverInfoApi = ServerInfoApi(_apiClient);
+    deviceInfoApi = DeviceInfoApi(_apiClient);
+  }
+
+  setAccessToken(String accessToken) {
+    _apiClient.addDefaultHeader('Authorization', 'bearer $accessToken');
+  }
+}

+ 4 - 3
mobile/lib/shared/services/device_info.service.dart

@@ -2,6 +2,7 @@ import 'package:flutter_udid/flutter_udid.dart';
 import 'dart:io' show Platform;
 import 'dart:io' show Platform;
 
 
 import 'package:hooks_riverpod/hooks_riverpod.dart';
 import 'package:hooks_riverpod/hooks_riverpod.dart';
+import 'package:openapi/api.dart';
 
 
 final deviceInfoServiceProvider = Provider((_) => DeviceInfoService());
 final deviceInfoServiceProvider = Provider((_) => DeviceInfoService());
 
 
@@ -9,12 +10,12 @@ class DeviceInfoService {
   Future<Map<String, dynamic>> getDeviceInfo() async {
   Future<Map<String, dynamic>> getDeviceInfo() async {
     // Get device info
     // Get device info
     var deviceId = await FlutterUdid.consistentUdid;
     var deviceId = await FlutterUdid.consistentUdid;
-    var deviceType = "";
+    var deviceType = DeviceTypeEnum.ANDROID;
 
 
     if (Platform.isAndroid) {
     if (Platform.isAndroid) {
-      deviceType = "ANDROID";
+      deviceType = DeviceTypeEnum.ANDROID;
     } else if (Platform.isIOS) {
     } else if (Platform.isIOS) {
-      deviceType = "IOS";
+      deviceType = DeviceTypeEnum.IOS;
     }
     }
 
 
     return {"deviceId": deviceId, "deviceType": deviceType};
     return {"deviceId": deviceId, "deviceType": deviceType};

+ 0 - 21
mobile/lib/shared/services/local_storage.service.dart

@@ -1,21 +0,0 @@
-import 'package:hive/hive.dart';
-import 'package:hooks_riverpod/hooks_riverpod.dart';
-import 'package:immich_mobile/constants/hive_box.dart';
-
-final localStorageServiceProvider = Provider((_) => LocalStorageService());
-
-class LocalStorageService {
-  late Box _box;
-
-  LocalStorageService() {
-    _box = Hive.box(userInfoBox);
-  }
-
-  T get<T>(String key) {
-    return _box.get(key);
-  }
-
-  put<T>(String key, T value) {
-    return _box.put(key, value);
-  }
-}

+ 5 - 4
mobile/lib/shared/services/network.service.dart

@@ -33,10 +33,11 @@ class NetworkService {
     }
     }
   }
   }
 
 
-  Future<dynamic> getRequest(
-      {required String url,
-      bool isByteResponse = false,
-      bool isStreamReponse = false}) async {
+  Future<dynamic> getRequest({
+    required String url,
+    bool isByteResponse = false,
+    bool isStreamReponse = false,
+  }) async {
     try {
     try {
       var savedEndpoint = Hive.box(userInfoBox).get(serverEndpointKey);
       var savedEndpoint = Hive.box(userInfoBox).get(serverEndpointKey);
 
 

+ 19 - 19
mobile/lib/shared/services/server_info.service.dart

@@ -1,33 +1,33 @@
-import 'package:dio/dio.dart';
 import 'package:flutter/material.dart';
 import 'package:flutter/material.dart';
 import 'package:hooks_riverpod/hooks_riverpod.dart';
 import 'package:hooks_riverpod/hooks_riverpod.dart';
-import 'package:immich_mobile/shared/models/server_info.model.dart';
-import 'package:immich_mobile/shared/models/server_version.model.dart';
-import 'package:immich_mobile/shared/services/network.service.dart';
+import 'package:immich_mobile/shared/services/api.service.dart';
+import 'package:openapi/api.dart';
 
 
-final serverInfoServiceProvider =
-    Provider((ref) => ServerInfoService(ref.watch(networkServiceProvider)));
+final serverInfoServiceProvider = Provider(
+  (ref) => ServerInfoService(
+    ref.watch(apiServiceProvider),
+  ),
+);
 
 
 class ServerInfoService {
 class ServerInfoService {
-  final NetworkService _networkService;
-  ServerInfoService(this._networkService);
+  final ApiService _apiService;
+  ServerInfoService(this._apiService);
 
 
-  Future<ServerInfo> getServerInfo() async {
-    Response response = await _networkService.getRequest(url: 'server-info');
-
-    return ServerInfo.fromJson(response.toString());
+  Future<ServerInfoResponseDto?> getServerInfo() async {
+    try {
+      return await _apiService.serverInfoApi.getServerInfo();
+    } catch (e) {
+      debugPrint("Error [getServerInfo] ${e.toString()}");
+      return null;
+    }
   }
   }
 
 
-  Future<ServerVersion?> getServerVersion() async {
+  Future<ServerVersionReponseDto?> getServerVersion() async {
     try {
     try {
-      Response response =
-          await _networkService.getRequest(url: 'server-info/version');
-
-      return ServerVersion.fromJson(response.toString());
+      return await _apiService.serverInfoApi.getServerVersion();
     } catch (e) {
     } catch (e) {
       debugPrint("Error getting server info");
       debugPrint("Error getting server info");
+      return null;
     }
     }
-
-    return null;
   }
   }
 }
 }

+ 29 - 50
mobile/lib/shared/services/user.service.dart

@@ -1,70 +1,49 @@
-import 'dart:convert';
-
-import 'package:dio/dio.dart';
 import 'package:flutter/material.dart';
 import 'package:flutter/material.dart';
-import 'package:hive/hive.dart';
 import 'package:hooks_riverpod/hooks_riverpod.dart';
 import 'package:hooks_riverpod/hooks_riverpod.dart';
+import 'package:http/http.dart';
 import 'package:http_parser/http_parser.dart';
 import 'package:http_parser/http_parser.dart';
 import 'package:image_picker/image_picker.dart';
 import 'package:image_picker/image_picker.dart';
-import 'package:immich_mobile/constants/hive_box.dart';
-import 'package:immich_mobile/shared/models/upload_profile_image_repsonse.model.dart';
-import 'package:immich_mobile/shared/models/user.model.dart';
-import 'package:immich_mobile/shared/services/network.service.dart';
-import 'package:immich_mobile/utils/dio_http_interceptor.dart';
+import 'package:immich_mobile/shared/services/api.service.dart';
 import 'package:immich_mobile/utils/files_helper.dart';
 import 'package:immich_mobile/utils/files_helper.dart';
+import 'package:openapi/api.dart';
 
 
-final userServiceProvider =
-    Provider((ref) => UserService(ref.watch(networkServiceProvider)));
+final userServiceProvider = Provider(
+  (ref) => UserService(
+    ref.watch(apiServiceProvider),
+  ),
+);
 
 
 class UserService {
 class UserService {
-  final NetworkService _networkService;
-  UserService(this._networkService);
+  final ApiService _apiService;
 
 
-  Future<List<User>> getAllUsersInfo() async {
-    try {
-      var res = await _networkService.getRequest(url: 'user');
-      List<dynamic> decodedData = jsonDecode(res.toString());
-      List<User> result = List.from(decodedData.map((e) => User.fromMap(e)));
+  UserService(this._apiService);
 
 
-      return result;
+  Future<List<UserResponseDto>?> getAllUsersInfo({required bool isAll}) async {
+    try {
+      return await _apiService.userApi.getAllUsers(isAll);
     } catch (e) {
     } catch (e) {
-      debugPrint("Error getAllUsersInfo  ${e.toString()}");
+      debugPrint("Error [getAllUsersInfo]  ${e.toString()}");
+      return null;
     }
     }
-
-    return [];
   }
   }
 
 
-  Future<UploadProfileImageResponse?> uploadProfileImage(XFile image) async {
-    var dio = Dio();
-    dio.interceptors.add(AuthenticatedRequestInterceptor());
-    String savedEndpoint = Hive.box(userInfoBox).get(serverEndpointKey);
-    var mimeType = FileHelper.getMimeType(image.path);
-
-    final imageData = MultipartFile.fromBytes(
-      await image.readAsBytes(),
-      filename: image.name,
-      contentType: MediaType(
-        mimeType["type"],
-        mimeType["subType"],
-      ),
-    );
-
-    final formData = FormData.fromMap({'file': imageData});
-
+  Future<CreateProfileImageResponseDto?> uploadProfileImage(XFile image) async {
     try {
     try {
-      Response res = await dio.post(
-        '$savedEndpoint/user/profile-image',
-        data: formData,
+      var mimeType = FileHelper.getMimeType(image.path);
+
+      return await _apiService.userApi.createProfileImage(
+        MultipartFile.fromBytes(
+          'file',
+          await image.readAsBytes(),
+          filename: image.name,
+          contentType: MediaType(
+            mimeType["type"],
+            mimeType["subType"],
+          ),
+        ),
       );
       );
-
-      var payload = UploadProfileImageResponse.fromJson(res.toString());
-
-      return payload;
-    } on DioError catch (e) {
-      debugPrint("Error uploading file: ${e.response}");
-      return null;
     } catch (e) {
     } catch (e) {
-      debugPrint("Error uploading file: $e");
+      debugPrint("Error [uploadProfileImage] ${e.toString()}");
       return null;
       return null;
     }
     }
   }
   }

+ 4 - 1
mobile/lib/shared/ui/immich_sliver_persistent_app_bar_delegate.dart

@@ -22,7 +22,10 @@ class ImmichSliverPersistentAppBarDelegate
 
 
   @override
   @override
   Widget build(
   Widget build(
-      BuildContext context, double shrinkOffset, bool overlapsContent) {
+    BuildContext context,
+    double shrinkOffset,
+    bool overlapsContent,
+  ) {
     return SizedBox.expand(child: child);
     return SizedBox.expand(child: child);
   }
   }
 
 

部分文件因为文件数量过多而无法显示