diff --git a/README.md b/README.md index d2d55073b4..3a3b7f1d78 100644 --- a/README.md +++ b/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 ``` -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 @@ -225,6 +225,15 @@ make dev # required Makefile installed on the system. 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 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. diff --git a/mobile/analysis_options.yaml b/mobile/analysis_options.yaml index ecf2f65ca3..b570c2b2e9 100644 --- a/mobile/analysis_options.yaml +++ b/mobile/analysis_options.yaml @@ -21,10 +21,18 @@ linter: # 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 # producing the lint. + rules: # 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 + require_trailing_commas: true + unrelated_type_equality_checks: true # Additional information about this file can be found at # https://dart.dev/guides/language/analysis-options +analyzer: + exclude: + - openapi/ + - openapi/test/ + - lib/generated_plugin_registrant.dart diff --git a/mobile/assets/i18n/en-US.json b/mobile/assets/i18n/en-US.json index 7d568f60fb..4a239b09fa 100644 --- a/mobile/assets/i18n/en-US.json +++ b/mobile/assets/i18n/en-US.json @@ -71,6 +71,7 @@ "login_form_label_password": "Password", "login_form_password_hint": "password", "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", "profile_drawer_client_server_up_to_date": "Client and Server are up-to-date", "profile_drawer_sign_out": "Sign Out", @@ -81,6 +82,7 @@ "search_result_page_new_search_hint": "New Search", "select_additional_user_for_sharing_page_suggestions": "Suggestions", "select_user_for_sharing_page_err_album": "Failed to create album", + "select_user_for_sharing_page_share_suggestions": "Suggestions", "share_add": "Add", "share_add_photos": "Add photos", "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_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" -} \ No newline at end of file +} diff --git a/mobile/lib/main.dart b/mobile/lib/main.dart index 5e0e51a77d..c14f2f39f6 100644 --- a/mobile/lib/main.dart +++ b/mobile/lib/main.dart @@ -46,12 +46,15 @@ void main() async { Locale('de', 'DE') ]; - runApp(EasyLocalization( + runApp( + EasyLocalization( supportedLocales: locales, path: 'assets/i18n', useFallbackTranslations: true, fallbackLocale: locales.first, - child: const ProviderScope(child: ImmichApp()))); + child: const ProviderScope(child: ImmichApp()), + ), + ); } class ImmichApp extends ConsumerStatefulWidget { @@ -111,6 +114,7 @@ class ImmichAppState extends ConsumerState @override initState() { super.initState(); + initApp().then((_) => debugPrint("App Init Completed")); } @@ -120,10 +124,9 @@ class ImmichAppState extends ConsumerState super.dispose(); } - final _immichRouter = AppRouter(); - @override Widget build(BuildContext context) { + var router = ref.watch(appRouterProvider); ref.watch(releaseInfoProvider.notifier).checkGithubReleaseInfo(); return MaterialApp( @@ -142,7 +145,8 @@ class ImmichAppState extends ConsumerState primarySwatch: Colors.indigo, fontFamily: 'WorkSans', snackBarTheme: const SnackBarThemeData( - contentTextStyle: TextStyle(fontFamily: 'WorkSans')), + contentTextStyle: TextStyle(fontFamily: 'WorkSans'), + ), scaffoldBackgroundColor: immichBackgroundColor, appBarTheme: const AppBarTheme( backgroundColor: immichBackgroundColor, @@ -152,9 +156,10 @@ class ImmichAppState extends ConsumerState 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 VersionAnnouncementOverlay(), diff --git a/mobile/lib/modules/asset_viewer/providers/image_viewer_page_state.provider.dart b/mobile/lib/modules/asset_viewer/providers/image_viewer_page_state.provider.dart index a9e12b848e..4bed6eb5f1 100644 --- a/mobile/lib/modules/asset_viewer/providers/image_viewer_page_state.provider.dart +++ b/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: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/shared/models/immich_asset.model.dart'; import 'package:immich_mobile/shared/ui/immich_toast.dart'; +import 'package:openapi/api.dart'; class ImageViewerStateNotifier extends StateNotifier { - 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); bool isSuccess = await _imageViewerService.downloadAssetToDevice(asset); @@ -43,4 +46,5 @@ class ImageViewerStateNotifier extends StateNotifier { final imageViewerStateProvider = StateNotifierProvider( - ((ref) => ImageViewerStateNotifier())); + ((ref) => ImageViewerStateNotifier(ref.watch(imageViewerServiceProvider))), +); diff --git a/mobile/lib/modules/asset_viewer/services/image_viewer.service.dart b/mobile/lib/modules/asset_viewer/services/image_viewer.service.dart index 9ee9e8a67a..771b0873d8 100644 --- a/mobile/lib/modules/asset_viewer/services/image_viewer.service.dart +++ b/mobile/lib/modules/asset_viewer/services/image_viewer.service.dart @@ -1,33 +1,35 @@ import 'dart:io'; 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:http/http.dart' as http; import 'package:photo_manager/photo_manager.dart'; import 'package:path_provider/path_provider.dart'; +final imageViewerServiceProvider = + Provider((ref) => ImageViewerService(ref.watch(apiServiceProvider))); + class ImageViewerService { - Future downloadAssetToDevice(ImmichAsset asset) async { + final ApiService _apiService; + ImageViewerService(this._apiService); + + Future downloadAssetToDevice(AssetResponseDto asset) async { try { 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; - if (asset.type == 'IMAGE') { + if (asset.type == AssetTypeEnum.IMAGE) { entity = await PhotoManager.editor.saveImage( res.bodyBytes, title: p.basename(asset.originalPath), diff --git a/mobile/lib/modules/asset_viewer/ui/exif_bottom_sheet.dart b/mobile/lib/modules/asset_viewer/ui/exif_bottom_sheet.dart index 892ef04c88..952be9448e 100644 --- a/mobile/lib/modules/asset_viewer/ui/exif_bottom_sheet.dart +++ b/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_map/flutter_map.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:latlong2/latlong.dart'; class ExifBottomSheet extends ConsumerWidget { - final ImmichAssetWithExif assetDetail; + final AssetResponseDto assetDetail; const ExifBottomSheet({Key? key, required this.assetDetail}) : super(key: key); @@ -26,8 +25,10 @@ class ExifBottomSheet extends ConsumerWidget { ), child: FlutterMap( 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, ), layers: [ @@ -46,10 +47,13 @@ class ExifBottomSheet extends ConsumerWidget { markers: [ Marker( 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( - image: AssetImage('assets/location-pin.png')), + image: AssetImage('assets/location-pin.png'), + ), ), ], ), @@ -63,7 +67,10 @@ class ExifBottomSheet extends ConsumerWidget { return Text( "${assetDetail.exifInfo!.city}, ${assetDetail.exifInfo!.state}", 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) Text( DateFormat('date_format'.tr()).format( - DateTime.parse(assetDetail.exifInfo!.dateTimeOriginal!), + assetDetail.exifInfo!.dateTimeOriginal!, ), style: TextStyle( color: Colors.grey[400], @@ -151,7 +158,8 @@ class ExifBottomSheet extends ConsumerWidget { ), subtitle: assetDetail.exifInfo?.exifImageHeight != null ? Text( - "${assetDetail.exifInfo?.exifImageHeight} x ${assetDetail.exifInfo?.exifImageWidth} ${assetDetail.exifInfo?.fileSizeInByte!}B ") + "${assetDetail.exifInfo?.exifImageHeight} x ${assetDetail.exifInfo?.exifImageWidth} ${assetDetail.exifInfo?.fileSizeInByte!}B ", + ) : null, ), if (assetDetail.exifInfo?.make != null) @@ -166,7 +174,8 @@ class ExifBottomSheet extends ConsumerWidget { style: const TextStyle(fontWeight: FontWeight.bold), ), 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} ", + ), ), ], ), diff --git a/mobile/lib/modules/asset_viewer/ui/remote_photo_view.dart b/mobile/lib/modules/asset_viewer/ui/remote_photo_view.dart index 3a9bce99be..384eae20c4 100644 --- a/mobile/lib/modules/asset_viewer/ui/remote_photo_view.dart +++ b/mobile/lib/modules/asset_viewer/ui/remote_photo_view.dart @@ -17,16 +17,20 @@ class _RemotePhotoViewState extends State { bool allowMoving = _status == _RemoteImageStatus.full; 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 if (_zoomedIn) return; @@ -42,12 +46,17 @@ class _RemotePhotoViewState extends State { } CachedNetworkImageProvider _authorizedImageProvider(String url) { - return CachedNetworkImageProvider(url, - headers: {"Authorization": widget.authToken}, cacheKey: url); + return CachedNetworkImageProvider( + url, + headers: {"Authorization": widget.authToken}, + cacheKey: url, + ); } void _performStateTransition( - _RemoteImageStatus newStatus, CachedNetworkImageProvider provider) { + _RemoteImageStatus newStatus, + CachedNetworkImageProvider provider, + ) { // Transition to same status is forbidden if (_status == newStatus) return; // Transition full -> thumbnail is forbidden @@ -67,19 +76,22 @@ class _RemotePhotoViewState extends State { _authorizedImageProvider(widget.thumbnailUrl); _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 = _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 @@ -90,14 +102,14 @@ class _RemotePhotoViewState extends State { } 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 imageUrl; diff --git a/mobile/lib/modules/asset_viewer/ui/top_control_app_bar.dart b/mobile/lib/modules/asset_viewer/ui/top_control_app_bar.dart index 39e8ecf6a2..eba66a2691 100644 --- a/mobile/lib/modules/asset_viewer/ui/top_control_app_bar.dart +++ b/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:flutter/material.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 { - 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 onDownloadPressed; @@ -54,12 +54,13 @@ class TopControlAppBar extends ConsumerWidget with PreferredSizeWidget { : const Icon(Icons.favorite_border_rounded), ), 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), + ) ], ); } diff --git a/mobile/lib/modules/asset_viewer/views/image_viewer_page.dart b/mobile/lib/modules/asset_viewer/views/image_viewer_page.dart index a59fc825ce..d88b8a9dcf 100644 --- a/mobile/lib/modules/asset_viewer/views/image_viewer_page.dart +++ b/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/top_control_app_bar.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 class ImageViewerPage extends HookConsumerWidget { final String imageUrl; final String heroTag; final String thumbnailUrl; - final ImmichAsset asset; + final AssetResponseDto asset; - ImmichAssetWithExif? assetDetail; + AssetResponseDto? assetDetail; ImageViewerPage({ Key? key, @@ -54,10 +53,13 @@ class ImageViewerPage extends HookConsumerWidget { ); } - useEffect(() { - getAssetExif(); - return null; - }, []); + useEffect( + () { + getAssetExif(); + return null; + }, + [], + ); return Scaffold( backgroundColor: Colors.black, @@ -75,14 +77,15 @@ class ImageViewerPage extends HookConsumerWidget { children: [ Center( 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) const Center( diff --git a/mobile/lib/modules/asset_viewer/views/video_viewer_page.dart b/mobile/lib/modules/asset_viewer/views/video_viewer_page.dart index 99be903dcb..4a2e4908ee 100644 --- a/mobile/lib/modules/asset_viewer/views/video_viewer_page.dart +++ b/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/top_control_app_bar.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'; // ignore: must_be_immutable class VideoViewerPage extends HookConsumerWidget { final String videoUrl; - final ImmichAsset asset; - ImmichAssetWithExif? assetDetail; + final AssetResponseDto asset; + AssetResponseDto? assetDetail; VideoViewerPage({Key? key, required this.videoUrl, required this.asset}) : super(key: key); @@ -49,10 +48,13 @@ class VideoViewerPage extends HookConsumerWidget { await ref.watch(assetServiceProvider).getAssetById(asset.id); } - useEffect(() { - getAssetExif(); - return null; - }, []); + useEffect( + () { + getAssetExif(); + return null; + }, + [], + ); return Scaffold( backgroundColor: Colors.black, @@ -116,8 +118,10 @@ class _VideoThumbnailPlayerState extends State { Future initializePlayer() async { try { - videoPlayerController = VideoPlayerController.network(widget.url, - httpHeaders: {"Authorization": "Bearer ${widget.jwtToken}"}); + videoPlayerController = VideoPlayerController.network( + widget.url, + httpHeaders: {"Authorization": "Bearer ${widget.jwtToken}"}, + ); await videoPlayerController.initialize(); _createChewieController(); diff --git a/mobile/lib/modules/backup/models/backup_state.model.dart b/mobile/lib/modules/backup/models/backup_state.model.dart index c3c66f08c8..8f41b0200a 100644 --- a/mobile/lib/modules/backup/models/backup_state.model.dart +++ b/mobile/lib/modules/backup/models/backup_state.model.dart @@ -1,10 +1,10 @@ import 'package:cancellation_token_http/http.dart'; import 'package:collection/collection.dart'; +import 'package:openapi/api.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/current_upload_asset.model.dart'; -import 'package:immich_mobile/shared/models/server_info.model.dart'; enum BackUpProgressEnum { idle, inProgress, done } @@ -14,7 +14,7 @@ class BackUpState { final List allAssetsInDatabase; final double progressInPercentage; final CancellationToken cancelToken; - final ServerInfo serverInfo; + final ServerInfoResponseDto serverInfo; /// All available albums on the device final List availableAlbums; @@ -49,7 +49,7 @@ class BackUpState { List? allAssetsInDatabase, double? progressInPercentage, CancellationToken? cancelToken, - ServerInfo? serverInfo, + ServerInfoResponseDto? serverInfo, List? availableAlbums, Set? selectedBackupAlbums, Set? excludedBackupAlbums, @@ -93,8 +93,10 @@ class BackUpState { collectionEquals(other.selectedBackupAlbums, selectedBackupAlbums) && collectionEquals(other.excludedBackupAlbums, excludedBackupAlbums) && collectionEquals(other.allUniqueAssets, allUniqueAssets) && - collectionEquals(other.selectedAlbumsBackupAssetsIds, - selectedAlbumsBackupAssetsIds) && + collectionEquals( + other.selectedAlbumsBackupAssetsIds, + selectedAlbumsBackupAssetsIds, + ) && other.currentUploadAsset == currentUploadAsset; } diff --git a/mobile/lib/modules/backup/models/check_duplicate_asset_response.model.dart b/mobile/lib/modules/backup/models/check_duplicate_asset_response.model.dart deleted file mode 100644 index 2fc30ea61f..0000000000 --- a/mobile/lib/modules/backup/models/check_duplicate_asset_response.model.dart +++ /dev/null @@ -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 toMap() { - final result = {}; - - result.addAll({'isExist': isExist}); - - return result; - } - - factory CheckDuplicateAssetResponse.fromMap(Map 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; -} diff --git a/mobile/lib/modules/backup/providers/backup.provider.dart b/mobile/lib/modules/backup/providers/backup.provider.dart index 2f6970f55e..e71a8dbfd8 100644 --- a/mobile/lib/modules/backup/providers/backup.provider.dart +++ b/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/login/models/authentication_state.model.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:openapi/api.dart'; import 'package:photo_manager/photo_manager.dart'; class BackupNotifier extends StateNotifier { @@ -28,12 +28,12 @@ class BackupNotifier extends StateNotifier { allAssetsInDatabase: const [], progressInPercentage: 0, cancelToken: CancellationToken(), - serverInfo: ServerInfo( + serverInfo: ServerInfoResponseDto( diskAvailable: "0", diskAvailableRaw: 0, diskSize: "0", diskSizeRaw: 0, - diskUsagePercentage: 0.0, + diskUsagePercentage: 0, diskUse: "0", diskUseRaw: 0, ), @@ -113,7 +113,9 @@ class BackupNotifier extends StateNotifier { // Get all albums on the device List availableAlbums = []; List albums = await PhotoManager.getAssetPathList( - hasAll: true, type: RequestType.common); + hasAll: true, + type: RequestType.common, + ); for (AssetPathEntity album in albums) { AvailableAlbum availableAlbum = AvailableAlbum(albumEntity: album); @@ -156,7 +158,10 @@ class BackupNotifier extends StateNotifier { // Get album that contains all assets var list = await PhotoManager.getAssetPathList( - hasAll: true, onlyAll: true, type: RequestType.common); + hasAll: true, + onlyAll: true, + type: RequestType.common, + ); AssetPathEntity albumHasAllAssets = list.first; backupAlbumInfoBox.put( @@ -175,13 +180,15 @@ class BackupNotifier extends StateNotifier { for (var selectedAlbumId in backupAlbumInfo!.selectedAlbumIds) { var albumAsset = await AssetPathEntity.fromId(selectedAlbumId); state = state.copyWith( - selectedBackupAlbums: {...state.selectedBackupAlbums, albumAsset}); + selectedBackupAlbums: {...state.selectedBackupAlbums, albumAsset}, + ); } for (var excludedAlbumId in backupAlbumInfo.excludedAlbumsIds) { var albumAsset = await AssetPathEntity.fromId(excludedAlbumId); state = state.copyWith( - excludedBackupAlbums: {...state.excludedBackupAlbums, albumAsset}); + excludedBackupAlbums: {...state.excludedBackupAlbums, albumAsset}, + ); } } catch (e) { debugPrint("[ERROR] Failed to generate album from id $e"); @@ -211,8 +218,11 @@ class BackupNotifier extends StateNotifier { Set allUniqueAssets = assetsFromSelectedAlbums.difference(assetsFromExcludedAlbums); - List allAssetsInDatabase = - await _backupService.getDeviceBackupAsset(); + var allAssetsInDatabase = await _backupService.getDeviceBackupAsset(); + + if (allAssetsInDatabase == null) { + return; + } // Find asset that were backup from selected albums Set selectedAlbumsBackupAssets = @@ -328,23 +338,27 @@ class BackupNotifier extends StateNotifier { void cancelBackup() { state.cancelToken.cancel(); state = state.copyWith( - backupProgress: BackUpProgressEnum.idle, progressInPercentage: 0.0); + backupProgress: BackUpProgressEnum.idle, + progressInPercentage: 0.0, + ); } 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 - state.selectedAlbumsBackupAssetsIds.length == 0) { state = state.copyWith( - backupProgress: BackUpProgressEnum.done, progressInPercentage: 0.0); + backupProgress: BackUpProgressEnum.done, + progressInPercentage: 0.0, + ); } _updateServerInfo(); @@ -352,24 +366,19 @@ class BackupNotifier extends StateNotifier { void _onUploadProgress(int sent, int total) { state = state.copyWith( - progressInPercentage: (sent.toDouble() / total.toDouble() * 100)); + progressInPercentage: (sent.toDouble() / total.toDouble() * 100), + ); } Future _updateServerInfo() async { var serverInfo = await _serverInfoService.getServerInfo(); // 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() { diff --git a/mobile/lib/modules/backup/services/backup.service.dart b/mobile/lib/modules/backup/services/backup.service.dart index c1b1576ab8..59696028f4 100644 --- a/mobile/lib/modules/backup/services/backup.service.dart +++ b/mobile/lib/modules/backup/services/backup.service.dart @@ -2,59 +2,38 @@ import 'dart:async'; import 'dart:convert'; import 'dart:io'; -import 'package:dio/dio.dart'; import 'package:flutter/material.dart'; import 'package:hive/hive.dart'; import 'package:hooks_riverpod/hooks_riverpod.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/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:openapi/api.dart'; import 'package:photo_manager/photo_manager.dart'; import 'package:http_parser/http_parser.dart'; import 'package:path/path.dart' as p; 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 { - final NetworkService _networkService; + final ApiService _apiService; + BackupService(this._apiService); - BackupService(this._networkService); - - Future> getDeviceBackupAsset() async { - String deviceId = Hive.box(userInfoBox).get(deviceIdKey); - - Response response = - await _networkService.getRequest(url: "asset/$deviceId"); - List result = jsonDecode(response.toString()); - - return result.cast(); - } - - Future checkDuplicateAsset(String deviceAssetId) async { + Future?> getDeviceBackupAsset() async { String deviceId = Hive.box(userInfoBox).get(deviceIdKey); 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) { - return false; + debugPrint('Error [getDeviceBackupAsset] ${e.toString()}'); + return null; } } @@ -99,9 +78,11 @@ class BackupService { var box = Hive.box(userInfoBox); 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.fields['deviceAssetId'] = entity.id; @@ -133,16 +114,19 @@ class BackupService { var error = jsonDecode(data); debugPrint( - "Error(${error['statusCode']}) uploading ${entity.id} | $originalFileName | Created on ${entity.createDateTime} | ${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'], - )); + errorCb( + ErrorUploadAsset( + asset: entity, + id: entity.id, + createdAt: entity.createDateTime, + fileName: originalFileName, + fileType: _getAssetType(entity.type), + errorMessage: error['error'], + ), + ); continue; } } @@ -160,8 +144,6 @@ class BackupService { } } - void sendBackupRequest(AssetEntity entity) {} - String _getAssetType(AssetType assetType) { switch (assetType) { case AssetType.audio: @@ -175,15 +157,29 @@ class BackupService { } } - Future setAutoBackup( - bool status, String deviceId, String deviceType) async { - var res = await _networkService.patchRequest(url: 'device-info', data: { - "isAutoBackup": status, - "deviceId": deviceId, - "deviceType": deviceType, - }); + Future setAutoBackup( + bool status, + String deviceId, + DeviceTypeEnum deviceType, + ) async { + try { + var updatedDeviceInfo = await _apiService.deviceInfoApi.updateDeviceInfo( + UpdateDeviceInfoDto( + deviceId: deviceId, + deviceType: deviceType, + isAutoBackup: status, + ), + ); - return DeviceInfoRemote.fromJson(res.toString()); + if (updatedDeviceInfo == null) { + throw Exception("Error updating device info"); + } + + return updatedDeviceInfo; + } catch (e) { + debugPrint("Error setAutoBackup: ${e.toString()}"); + throw Error(); + } } } diff --git a/mobile/lib/modules/backup/ui/album_info_card.dart b/mobile/lib/modules/backup/ui/album_info_card.dart index 90a1e8976d..370db0c3b2 100644 --- a/mobile/lib/modules/backup/ui/album_info_card.dart +++ b/mobile/lib/modules/backup/ui/album_info_card.dart @@ -26,7 +26,9 @@ class AlbumInfoCard extends HookConsumerWidget { ref.watch(backupProvider).excludedBackupAlbums.contains(albumInfo); ColorFilter selectedFilter = ColorFilter.mode( - Theme.of(context).primaryColor.withAlpha(100), BlendMode.darken); + Theme.of(context).primaryColor.withAlpha(100), + BlendMode.darken, + ); ColorFilter excludedFilter = ColorFilter.mode(Colors.red.withAlpha(75), BlendMode.darken); ColorFilter unselectedFilter = @@ -40,7 +42,10 @@ class AlbumInfoCard extends HookConsumerWidget { label: const Text( "album_info_card_backup_album_included", style: TextStyle( - fontSize: 10, color: Colors.white, fontWeight: FontWeight.bold), + fontSize: 10, + color: Colors.white, + fontWeight: FontWeight.bold, + ), ).tr(), backgroundColor: Theme.of(context).primaryColor, ); @@ -51,7 +56,10 @@ class AlbumInfoCard extends HookConsumerWidget { label: const Text( "album_info_card_backup_album_excluded", style: TextStyle( - fontSize: 10, color: Colors.white, fontWeight: FontWeight.bold), + fontSize: 10, + color: Colors.white, + fontWeight: FontWeight.bold, + ), ).tr(), backgroundColor: Colors.red[300], ); @@ -138,15 +146,16 @@ class AlbumInfoCard extends HookConsumerWidget { height: 200, decoration: BoxDecoration( borderRadius: const BorderRadius.only( - topLeft: Radius.circular(12), - topRight: Radius.circular(12)), + topLeft: Radius.circular(12), + topRight: Radius.circular(12), + ), image: DecorationImage( colorFilter: _buildImageFilter(), image: imageData != null ? MemoryImage(imageData!) : const AssetImage( - 'assets/immich-logo-no-outline.png') - as ImageProvider, + 'assets/immich-logo-no-outline.png', + ) as ImageProvider, fit: BoxFit.cover, ), ), @@ -174,9 +183,10 @@ class AlbumInfoCard extends HookConsumerWidget { Text( albumInfo.name, style: TextStyle( - fontSize: 14, - color: Theme.of(context).primaryColor, - fontWeight: FontWeight.bold), + fontSize: 14, + color: Theme.of(context).primaryColor, + fontWeight: FontWeight.bold, + ), ), Padding( padding: const EdgeInsets.only(top: 2.0), @@ -186,7 +196,9 @@ class AlbumInfoCard extends HookConsumerWidget { ? " (${'backup_all'.tr()})" : ""), style: TextStyle( - fontSize: 12, color: Colors.grey[600]), + fontSize: 12, + color: Colors.grey[600], + ), ), ) ], diff --git a/mobile/lib/modules/backup/ui/backup_info_card.dart b/mobile/lib/modules/backup/ui/backup_info_card.dart index 300fa1feba..6bc832a5d4 100644 --- a/mobile/lib/modules/backup/ui/backup_info_card.dart +++ b/mobile/lib/modules/backup/ui/backup_info_card.dart @@ -5,12 +5,12 @@ class BackupInfoCard extends StatelessWidget { final String title; final String subtitle; 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 Widget build(BuildContext context) { diff --git a/mobile/lib/modules/backup/views/album_preview_page.dart b/mobile/lib/modules/backup/views/album_preview_page.dart index 561d177b7b..2266fe8bcc 100644 --- a/mobile/lib/modules/backup/views/album_preview_page.dart +++ b/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); } - useEffect(() { - _getAssetsInAlbum(); - return null; - }, []); + useEffect( + () { + _getAssetsInAlbum(); + return null; + }, + [], + ); return Scaffold( appBar: AppBar( @@ -39,9 +42,10 @@ class AlbumPreviewPage extends HookConsumerWidget { child: Text( "ID ${album.id}", 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, itemBuilder: (context, index) { - Future thumbData = assets.value[index] - .thumbnailDataWithSize(const ThumbnailSize(200, 200), - quality: 50); + Future thumbData = + assets.value[index].thumbnailDataWithSize( + const ThumbnailSize(200, 200), + quality: 50, + ); return FutureBuilder( future: thumbData, diff --git a/mobile/lib/modules/backup/views/backup_album_selection_page.dart b/mobile/lib/modules/backup/views/backup_album_selection_page.dart index 27cf556842..50a5be8537 100644 --- a/mobile/lib/modules/backup/views/backup_album_selection_page.dart +++ b/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 excludedBackupAlbums = ref.watch(backupProvider).excludedBackupAlbums; - useEffect(() { - ref.read(backupProvider.notifier).getBackupInfo(); - return null; - }, []); + useEffect( + () { + ref.read(backupProvider.notifier).getBackupInfo(); + return null; + }, + [], + ); _buildAlbumSelectionList() { if (availableAlbums.isEmpty) { @@ -42,8 +45,9 @@ class BackupAlbumSelectionPage extends HookConsumerWidget { ? const EdgeInsets.only(left: 16.00) : const EdgeInsets.all(0), child: AlbumInfoCard( - imageData: thumbnailData, - albumInfo: availableAlbums[index].albumEntity), + imageData: thumbnailData, + albumInfo: availableAlbums[index].albumEntity, + ), ); }), ), @@ -73,13 +77,15 @@ class BackupAlbumSelectionPage extends HookConsumerWidget { child: Chip( visualDensity: VisualDensity.compact, shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(5)), + borderRadius: BorderRadius.circular(5), + ), label: Text( album.name, style: const TextStyle( - fontSize: 10, - color: Colors.white, - fontWeight: FontWeight.bold), + fontSize: 10, + color: Colors.white, + fontWeight: FontWeight.bold, + ), ), backgroundColor: Theme.of(context).primaryColor, deleteIconColor: Colors.white, @@ -109,13 +115,15 @@ class BackupAlbumSelectionPage extends HookConsumerWidget { child: Chip( visualDensity: VisualDensity.compact, shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(5)), + borderRadius: BorderRadius.circular(5), + ), label: Text( album.name, style: const TextStyle( - fontSize: 10, - color: Colors.white, - fontWeight: FontWeight.bold), + fontSize: 10, + color: Colors.white, + fontWeight: FontWeight.bold, + ), ), backgroundColor: Colors.red[300], deleteIconColor: Colors.white, @@ -185,9 +193,10 @@ class BackupAlbumSelectionPage extends HookConsumerWidget { title: Text( "backup_album_selection_page_total_assets", style: TextStyle( - fontWeight: FontWeight.bold, - fontSize: 14, - color: Colors.grey[700]), + fontWeight: FontWeight.bold, + fontSize: 14, + color: Colors.grey[700], + ), ).tr(), trailing: Text( ref @@ -234,7 +243,8 @@ class BackupAlbumSelectionPage extends HookConsumerWidget { builder: (BuildContext context) { return AlertDialog( shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(12)), + borderRadius: BorderRadius.circular(12), + ), elevation: 5, title: Text( 'backup_album_selection_page_selection_info', @@ -250,7 +260,9 @@ class BackupAlbumSelectionPage extends HookConsumerWidget { Text( 'backup_album_selection_page_assets_scatter', style: TextStyle( - fontSize: 14, color: Colors.grey[700]), + fontSize: 14, + color: Colors.grey[700], + ), ).tr(), ], ), diff --git a/mobile/lib/modules/backup/views/backup_controller_page.dart b/mobile/lib/modules/backup/views/backup_controller_page.dart index a778e986d4..65377b8538 100644 --- a/mobile/lib/modules/backup/views/backup_controller_page.dart +++ b/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/shared/providers/websocket.provider.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'; class BackupControllerPage extends HookConsumerWidget { @@ -27,16 +26,19 @@ class BackupControllerPage extends HookConsumerWidget { ? false : 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() { return ListTile( @@ -68,10 +70,11 @@ class BackupControllerPage extends HookConsumerWidget { Padding( padding: const EdgeInsets.only(top: 12.0), 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); } }, - 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( text.trim().substring(0, text.length - 2), style: TextStyle( - color: Theme.of(context).primaryColor, - fontSize: 12, - fontWeight: FontWeight.bold), + color: Theme.of(context).primaryColor, + fontSize: 12, + fontWeight: FontWeight.bold, + ), ), ); } else { @@ -168,9 +174,10 @@ class BackupControllerPage extends HookConsumerWidget { child: Text( "backup_controller_page_none_selected".tr(), 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( text.trim().substring(0, text.length - 2), style: TextStyle( - color: Colors.red[300], - fontSize: 12, - fontWeight: FontWeight.bold), + color: Colors.red[300], + fontSize: 12, + fontWeight: FontWeight.bold, + ), ), ); } else { @@ -213,9 +221,10 @@ class BackupControllerPage extends HookConsumerWidget { borderOnForeground: false, child: ListTile( 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( padding: const EdgeInsets.only(top: 8.0), child: Column( @@ -284,9 +293,9 @@ class BackupControllerPage extends HookConsumerWidget { fontWeight: FontWeight.bold, fontSize: 11, ), - ).tr(args: [ - ref.watch(errorBackupListProvider).length.toString() - ]), + ).tr( + args: [ref.watch(errorBackupListProvider).length.toString()], + ), backgroundColor: Colors.white, onPressed: () { AutoRouter.of(context).push(const FailedBackupStatusRoute()); @@ -331,12 +340,16 @@ class BackupControllerPage extends HookConsumerWidget { child: const Text( 'backup_controller_page_filename', 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), child: const Text( "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), ).tr(), 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( padding: const EdgeInsets.only(left: 16.0, right: 16, bottom: 32), diff --git a/mobile/lib/modules/backup/views/failed_backup_status_page.dart b/mobile/lib/modules/backup/views/failed_backup_status_page.dart index 203ea0fad5..c78592fa2c 100644 --- a/mobile/lib/modules/backup/views/failed_backup_status_page.dart +++ b/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), ), 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( shrinkWrap: true, @@ -92,9 +93,10 @@ class FailedBackupStatusPage extends HookConsumerWidget { ), ), style: TextStyle( - fontSize: 12, - fontWeight: FontWeight.w600, - color: Colors.grey[700]), + fontSize: 12, + fontWeight: FontWeight.w600, + color: Colors.grey[700], + ), ), Icon( Icons.error, diff --git a/mobile/lib/modules/home/models/delete_asset_response.model.dart b/mobile/lib/modules/home/models/delete_asset_response.model.dart deleted file mode 100644 index 6e9105941f..0000000000 --- a/mobile/lib/modules/home/models/delete_asset_response.model.dart +++ /dev/null @@ -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 toMap() { - return { - 'id': id, - 'status': status, - }; - } - - factory DeleteAssetResponse.fromMap(Map 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; -} diff --git a/mobile/lib/modules/home/models/get_all_asset_response.model.dart b/mobile/lib/modules/home/models/get_all_asset_response.model.dart index 86b340ae36..f02d830b2a 100644 --- a/mobile/lib/modules/home/models/get_all_asset_response.model.dart +++ b/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:immich_mobile/shared/models/immich_asset.model.dart'; +import 'package:openapi/api.dart'; class ImmichAssetGroupByDate { final String date; - List assets; + List assets; ImmichAssetGroupByDate({ required this.date, required this.assets, @@ -13,7 +11,7 @@ class ImmichAssetGroupByDate { ImmichAssetGroupByDate copyWith({ String? date, - List? assets, + List? assets, }) { return ImmichAssetGroupByDate( date: date ?? this.date, @@ -21,26 +19,6 @@ class ImmichAssetGroupByDate { ); } - Map toMap() { - return { - 'date': date, - 'assets': assets.map((x) => x.toMap()).toList(), - }; - } - - factory ImmichAssetGroupByDate.fromMap(Map map) { - return ImmichAssetGroupByDate( - date: map['date'] ?? '', - assets: List.from( - map['assets']?.map((x) => ImmichAsset.fromMap(x))), - ); - } - - String toJson() => json.encode(toMap()); - - factory ImmichAssetGroupByDate.fromJson(String source) => - ImmichAssetGroupByDate.fromMap(json.decode(source)); - @override String toString() => 'ImmichAssetGroupByDate(date: $date, assets: $assets)'; @@ -79,28 +57,6 @@ class GetAllAssetResponse { ); } - Map toMap() { - return { - 'count': count, - 'data': data.map((x) => x.toMap()).toList(), - 'nextPageKey': nextPageKey, - }; - } - - factory GetAllAssetResponse.fromMap(Map map) { - return GetAllAssetResponse( - count: map['count']?.toInt() ?? 0, - data: List.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 String toString() => 'GetAllAssetResponse(count: $count, data: $data, nextPageKey: $nextPageKey)'; diff --git a/mobile/lib/modules/home/models/home_page_state.model.dart b/mobile/lib/modules/home/models/home_page_state.model.dart index 95621fc991..1701ac3ffb 100644 --- a/mobile/lib/modules/home/models/home_page_state.model.dart +++ b/mobile/lib/modules/home/models/home_page_state.model.dart @@ -1,12 +1,10 @@ -import 'dart:convert'; - import 'package:collection/collection.dart'; -import 'package:immich_mobile/shared/models/immich_asset.model.dart'; +import 'package:openapi/api.dart'; class HomePageState { final bool isMultiSelectEnable; - final Set selectedItems; + final Set selectedItems; final Set selectedDateGroup; HomePageState({ required this.isMultiSelectEnable, @@ -16,7 +14,7 @@ class HomePageState { HomePageState copyWith({ bool? isMultiSelectEnable, - Set? selectedItems, + Set? selectedItems, Set? selectedDateGroup, }) { return HomePageState( @@ -26,28 +24,6 @@ class HomePageState { ); } - Map toMap() { - return { - 'isMultiSelectEnable': isMultiSelectEnable, - 'selectedItems': selectedItems.map((x) => x.toMap()).toList(), - 'selectedDateGroup': selectedDateGroup.toList(), - }; - } - - factory HomePageState.fromMap(Map map) { - return HomePageState( - isMultiSelectEnable: map['isMultiSelectEnable'] ?? false, - selectedItems: Set.from( - map['selectedItems']?.map((x) => ImmichAsset.fromMap(x))), - selectedDateGroup: Set.from(map['selectedDateGroup']), - ); - } - - String toJson() => json.encode(toMap()); - - factory HomePageState.fromJson(String source) => - HomePageState.fromMap(json.decode(source)); - @override String toString() => 'HomePageState(isMultiSelectEnable: $isMultiSelectEnable, selectedItems: $selectedItems, selectedDateGroup: $selectedDateGroup)'; diff --git a/mobile/lib/modules/home/providers/home_page_state.provider.dart b/mobile/lib/modules/home/providers/home_page_state.provider.dart index 6a9fb71760..f81c7199a0 100644 --- a/mobile/lib/modules/home/providers/home_page_state.provider.dart +++ b/mobile/lib/modules/home/providers/home_page_state.provider.dart @@ -1,6 +1,6 @@ import 'package:hooks_riverpod/hooks_riverpod.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 { HomePageStateNotifier() @@ -14,7 +14,8 @@ class HomePageStateNotifier extends StateNotifier { void addSelectedDateGroup(String dateGroupTitle) { state = state.copyWith( - selectedDateGroup: {...state.selectedDateGroup, dateGroupTitle}); + selectedDateGroup: {...state.selectedDateGroup, dateGroupTitle}, + ); } void removeSelectedDateGroup(String dateGroupTitle) { @@ -25,36 +26,39 @@ class HomePageStateNotifier extends StateNotifier { state = state.copyWith(selectedDateGroup: currentDateGroup); } - void enableMultiSelect(Set selectedItems) { + void enableMultiSelect(Set selectedItems) { state = state.copyWith(isMultiSelectEnable: true, selectedItems: selectedItems); } void disableMultiSelect() { 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}); } - void addMultipleSelectedItems(List assets) { + void addMultipleSelectedItems(List assets) { state = state.copyWith(selectedItems: {...state.selectedItems, ...assets}); } - void removeSingleSelectedItem(ImmichAsset asset) { - Set currentList = state.selectedItems; + void removeSingleSelectedItem(AssetResponseDto asset) { + Set currentList = state.selectedItems; currentList.removeWhere((e) => e.id == asset.id); state = state.copyWith(selectedItems: currentList); } - void removeMultipleSelectedItem(List assets) { - Set currentList = state.selectedItems; + void removeMultipleSelectedItem(List assets) { + Set currentList = state.selectedItems; - for (ImmichAsset asset in assets) { + for (AssetResponseDto asset in assets) { currentList.removeWhere((e) => e.id == asset.id); } @@ -64,4 +68,5 @@ class HomePageStateNotifier extends StateNotifier { final homePageStateProvider = StateNotifierProvider( - ((ref) => HomePageStateNotifier())); + ((ref) => HomePageStateNotifier()), +); diff --git a/mobile/lib/modules/home/providers/upload_profile_image.provider.dart b/mobile/lib/modules/home/providers/upload_profile_image.provider.dart index f8d21d782c..66060da383 100644 --- a/mobile/lib/modules/home/providers/upload_profile_image.provider.dart +++ b/mobile/lib/modules/home/providers/upload_profile_image.provider.dart @@ -73,10 +73,12 @@ class UploadProfileImageState { class UploadProfileImageNotifier extends StateNotifier { UploadProfileImageNotifier(this._userSErvice) - : super(UploadProfileImageState( - profileImagePath: '', - status: UploadProfileStatus.idle, - )); + : super( + UploadProfileImageState( + profileImagePath: '', + status: UploadProfileStatus.idle, + ), + ); final UserService _userSErvice; @@ -88,8 +90,9 @@ class UploadProfileImageNotifier if (res != null) { debugPrint("Succesfully upload profile image"); state = state.copyWith( - status: UploadProfileStatus.success, - profileImagePath: res.profileImagePath); + status: UploadProfileStatus.success, + profileImagePath: res.profileImagePath, + ); return true; } @@ -100,4 +103,5 @@ class UploadProfileImageNotifier final uploadProfileImageProvider = StateNotifierProvider( - ((ref) => UploadProfileImageNotifier(ref.watch(userServiceProvider)))); + ((ref) => UploadProfileImageNotifier(ref.watch(userServiceProvider))), +); diff --git a/mobile/lib/modules/home/services/asset.service.dart b/mobile/lib/modules/home/services/asset.service.dart index 16546f1dc7..3e44d8b165 100644 --- a/mobile/lib/modules/home/services/asset.service.dart +++ b/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: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 { - final NetworkService _networkService; - AssetService(this._networkService); + final ApiService _apiService; - Future?> getAllAsset() async { - var res = await _networkService.getRequest(url: "asset/"); + AssetService(this._apiService); + + Future?> getAllAsset() async { try { - List decodedData = jsonDecode(res.toString()); - - List result = - List.from(decodedData.map((a) => ImmichAsset.fromMap(a))); - return result; + return await _apiService.assetApi.getAllAssets(); } catch (e) { - debugPrint("Error getAllAsset ${e.toString()}"); - } - return null; - } - - Future getAllAssetWithPagination() async { - var res = await _networkService.getRequest(url: "asset/all"); - try { - Map decodedData = jsonDecode(res.toString()); - - GetAllAssetResponse result = GetAllAssetResponse.fromMap(decodedData); - return result; - } catch (e) { - debugPrint("Error getAllAsset ${e.toString()}"); - } - return null; - } - - Future getOlderAsset(String? nextPageKey) async { - try { - var res = await _networkService.getRequest( - url: "asset/all?nextPageKey=$nextPageKey", - ); - - Map decodedData = jsonDecode(res.toString()); - - GetAllAssetResponse result = GetAllAssetResponse.fromMap(decodedData); - if (result.count != 0) { - return result; - } - } catch (e) { - debugPrint("Error getAllAsset ${e.toString()}"); - } - return null; - } - - Future> getNewAsset(String latestDate) async { - try { - var res = await _networkService.getRequest( - url: "asset/new?latestDate=$latestDate", - ); - - List decodedData = jsonDecode(res.toString()); - - List result = - List.from(decodedData.map((a) => ImmichAsset.fromMap(a))); - if (result.isNotEmpty) { - return result; - } - - return []; - } catch (e) { - debugPrint("Error getAllAsset ${e.toString()}"); - return []; - } - } - - Future getAssetById(String assetId) async { - try { - var res = await _networkService.getRequest( - url: "asset/assetById/$assetId", - ); - - Map decodedData = jsonDecode(res.toString()); - - ImmichAssetWithExif result = ImmichAssetWithExif.fromMap(decodedData); - return result; - } catch (e) { - debugPrint("Error getAllAsset ${e.toString()}"); + debugPrint("Error [getAllAsset] ${e.toString()}"); return null; } } - Future?> deleteAssets( - Set deleteAssets) async { + Future getAssetById(String assetId) async { try { - var payload = []; + return await _apiService.assetApi.getAssetById(assetId); + } catch (e) { + debugPrint("Error [getAssetById] ${e.toString()}"); + return null; + } + } + + Future?> deleteAssets( + Set deleteAssets, + ) async { + try { + List payload = []; for (var asset in deleteAssets) { payload.add(asset.id); } - var res = await _networkService - .deleteRequest(url: "asset/", data: {"ids": payload}); - - List decodedData = jsonDecode(res.toString()); - - List result = - List.from(decodedData.map((a) => DeleteAssetResponse.fromMap(a))); - - return result; + return await _apiService.assetApi + .deleteAsset(DeleteAssetDto(ids: payload)); } catch (e) { debugPrint("Error getAllAsset ${e.toString()}"); return null; diff --git a/mobile/lib/modules/home/ui/control_bottom_app_bar.dart b/mobile/lib/modules/home/ui/control_bottom_app_bar.dart index 2ff56fb997..87b08e2313 100644 --- a/mobile/lib/modules/home/ui/control_bottom_app_bar.dart +++ b/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, decoration: BoxDecoration( 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), ), child: Column( @@ -48,12 +50,12 @@ class ControlBottomAppBar 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 IconData iconData; diff --git a/mobile/lib/modules/home/ui/daily_title_text.dart b/mobile/lib/modules/home/ui/daily_title_text.dart index 15e941f9a0..ad2532b416 100644 --- a/mobile/lib/modules/home/ui/daily_title_text.dart +++ b/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:hooks_riverpod/hooks_riverpod.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 { const DailyTitleText({ @@ -13,14 +12,15 @@ class DailyTitleText extends ConsumerWidget { }) : super(key: key); final String isoDate; - final List assetGroup; + final List assetGroup; @override Widget build(BuildContext context, WidgetRef ref) { var currentYear = DateTime.now().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 = DateFormat(formatDateTemplate).format(DateTime.parse(isoDate)); var isMultiSelectEnable = @@ -74,7 +74,11 @@ class DailyTitleText extends ConsumerWidget { return SliverToBoxAdapter( child: Padding( 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( children: [ Text( diff --git a/mobile/lib/modules/home/ui/disable_multi_select_button.dart b/mobile/lib/modules/home/ui/disable_multi_select_button.dart index 3a28d7fe86..9a75bd6376 100644 --- a/mobile/lib/modules/home/ui/disable_multi_select_button.dart +++ b/mobile/lib/modules/home/ui/disable_multi_select_button.dart @@ -29,15 +29,18 @@ class DisableMultiSelectButton extends ConsumerWidget { child: Padding( padding: const EdgeInsets.symmetric(horizontal: 4.0), 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, + ), + ), + ), ), ), ), diff --git a/mobile/lib/modules/home/ui/draggable_scrollbar.dart b/mobile/lib/modules/home/ui/draggable_scrollbar.dart index b7e075492c..90e7d85e51 100644 --- a/mobile/lib/modules/home/ui/draggable_scrollbar.dart +++ b/mobile/lib/modules/home/ui/draggable_scrollbar.dart @@ -118,20 +118,24 @@ class DraggableScrollbar extends StatefulWidget { this.labelConstraints, }) : assert(child.scrollDirection == Axis.vertical), scrollThumbBuilder = _thumbSemicircleBuilder( - heightScrollThumb * 0.6, scrollThumbKey, alwaysVisibleScrollThumb), + heightScrollThumb * 0.6, + scrollThumbKey, + alwaysVisibleScrollThumb, + ), super(key: key); @override DraggableScrollbarState createState() => DraggableScrollbarState(); - static buildScrollThumbAndLabel( - {required Widget scrollThumb, - required Color backgroundColor, - required Animation? thumbAnimation, - required Animation? labelAnimation, - required Text? labelText, - required BoxConstraints? labelConstraints, - required bool alwaysVisibleScrollThumb}) { + static buildScrollThumbAndLabel({ + required Widget scrollThumb, + required Color backgroundColor, + required Animation? thumbAnimation, + required Animation? labelAnimation, + required Text? labelText, + required BoxConstraints? labelConstraints, + required bool alwaysVisibleScrollThumb, + }) { var scrollThumbAndLabel = labelText == null ? scrollThumb : Row( @@ -158,7 +162,10 @@ class DraggableScrollbar extends StatefulWidget { } static ScrollThumbBuilder _thumbSemicircleBuilder( - double width, Key? scrollThumbKey, bool alwaysVisibleScrollThumb) { + double width, + Key? scrollThumbKey, + bool alwaysVisibleScrollThumb, + ) { return ( Color backgroundColor, Animation thumbAnimation, @@ -198,7 +205,9 @@ class DraggableScrollbar extends StatefulWidget { } static ScrollThumbBuilder _thumbArrowBuilder( - Key? scrollThumbKey, bool alwaysVisibleScrollThumb) { + Key? scrollThumbKey, + bool alwaysVisibleScrollThumb, + ) { return ( Color backgroundColor, Animation thumbAnimation, @@ -234,7 +243,9 @@ class DraggableScrollbar extends StatefulWidget { } static ScrollThumbBuilder _thumbRRectBuilder( - Key? scrollThumbKey, bool alwaysVisibleScrollThumb) { + Key? scrollThumbKey, + bool alwaysVisibleScrollThumb, + ) { return ( Color backgroundColor, Animation thumbAnimation, @@ -372,42 +383,44 @@ class DraggableScrollbarState extends State } return LayoutBuilder( - builder: (BuildContext context, BoxConstraints constraints) { - //print("LayoutBuilder constraints=$constraints"); + builder: (BuildContext context, BoxConstraints constraints) { + //print("LayoutBuilder constraints=$constraints"); - return NotificationListener( - onNotification: (ScrollNotification notification) { - changePosition(notification); - return false; - }, - child: Stack( - children: [ - RepaintBoundary( - child: widget.child, - ), - RepaintBoundary( + return NotificationListener( + onNotification: (ScrollNotification notification) { + changePosition(notification); + return false; + }, + child: Stack( + children: [ + RepaintBoundary( + child: widget.child, + ), + RepaintBoundary( 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 @@ -498,7 +511,10 @@ class DraggableScrollbarState extends State } double viewDelta = getScrollViewDelta( - details.delta.dy, barMaxScrollExtent, viewMaxScrollExtent); + details.delta.dy, + barMaxScrollExtent, + viewMaxScrollExtent, + ); _viewOffset = widget.controller.position.pixels + viewDelta; if (_viewOffset < widget.controller.position.minScrollExtent) { @@ -579,7 +595,9 @@ class ArrowClipper extends CustomClipper { path.lineTo(startPointX + arrowWidth, startPointY); path.lineTo(startPointX + arrowWidth, startPointY + 1.0); 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.close(); @@ -589,7 +607,9 @@ class ArrowClipper extends CustomClipper { path.lineTo(startPointX, startPointY); path.lineTo(startPointX, startPointY - 1.0); 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.close(); diff --git a/mobile/lib/modules/home/ui/image_grid.dart b/mobile/lib/modules/home/ui/image_grid.dart index 3b8e11adb8..1a7f1b2d71 100644 --- a/mobile/lib/modules/home/ui/image_grid.dart +++ b/mobile/lib/modules/home/ui/image_grid.dart @@ -1,10 +1,10 @@ import 'package:flutter/material.dart'; import 'package:hooks_riverpod/hooks_riverpod.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 { - final List assetGroup; + final List assetGroup; const ImageGrid({Key? key, required this.assetGroup}) : super(key: key); @@ -25,7 +25,7 @@ class ImageGrid extends ConsumerWidget { child: Stack( children: [ ThumbnailImage(asset: assetGroup[index]), - if (assetType != 'IMAGE') + if (assetType != AssetTypeEnum.IMAGE) Positioned( top: 5, right: 5, diff --git a/mobile/lib/modules/home/ui/immich_sliver_appbar.dart b/mobile/lib/modules/home/ui/immich_sliver_appbar.dart index 1f46def56c..7e71cf93f4 100644 --- a/mobile/lib/modules/home/ui/immich_sliver_appbar.dart +++ b/mobile/lib/modules/home/ui/immich_sliver_appbar.dart @@ -31,7 +31,8 @@ class ImmichSliverAppBar extends ConsumerWidget { pinned: false, snap: false, shape: const RoundedRectangleBorder( - borderRadius: BorderRadius.all(Radius.circular(5))), + borderRadius: BorderRadius.all(Radius.circular(5)), + ), leading: Builder( builder: (BuildContext context) { return Stack( @@ -99,7 +100,8 @@ class ImmichSliverAppBar extends ConsumerWidget { child: CircularProgressIndicator( strokeWidth: 1, valueColor: AlwaysStoppedAnimation( - Theme.of(context).primaryColor), + Theme.of(context).primaryColor, + ), ), ), ), @@ -117,7 +119,8 @@ class ImmichSliverAppBar extends ConsumerWidget { Icons.cloud_off_rounded, size: 8, ), - child: const Icon(Icons.backup_rounded)), + child: const Icon(Icons.backup_rounded), + ), onPressed: () async { var onPop = await AutoRouter.of(context) .push(const BackupControllerRoute()); diff --git a/mobile/lib/modules/home/ui/monthly_title_text.dart b/mobile/lib/modules/home/ui/monthly_title_text.dart index ad9998c8f2..f319624604 100644 --- a/mobile/lib/modules/home/ui/monthly_title_text.dart +++ b/mobile/lib/modules/home/ui/monthly_title_text.dart @@ -1,6 +1,5 @@ import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; -import 'package:intl/intl.dart'; class MonthlyTitleText extends StatelessWidget { const MonthlyTitleText({ @@ -12,7 +11,8 @@ class MonthlyTitleText extends StatelessWidget { @override 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( child: Padding( diff --git a/mobile/lib/modules/home/ui/profile_drawer.dart b/mobile/lib/modules/home/ui/profile_drawer.dart index 20ef4d139a..6154fdc315 100644 --- a/mobile/lib/modules/home/ui/profile_drawer.dart +++ b/mobile/lib/modules/home/ui/profile_drawer.dart @@ -55,7 +55,8 @@ class ProfileDrawer extends HookConsumerWidget { return CircleAvatar( radius: 35, backgroundImage: NetworkImage( - '$endpoint/user/profile-image/${authState.userId}?d=${dummmy++}'), + '$endpoint/user/profile-image/${authState.userId}?d=${dummmy++}', + ), backgroundColor: Colors.transparent, ); } else { @@ -71,7 +72,8 @@ class ProfileDrawer extends HookConsumerWidget { return CircleAvatar( radius: 35, backgroundImage: NetworkImage( - '$endpoint/user/profile-image/${authState.userId}?d=${dummmy++}'), + '$endpoint/user/profile-image/${authState.userId}?d=${dummmy++}', + ), backgroundColor: Colors.transparent, ); } @@ -93,7 +95,10 @@ class ProfileDrawer extends HookConsumerWidget { _pickUserProfileImage() async { final XFile? image = await ImagePicker().pickImage( - source: ImageSource.gallery, maxHeight: 1024, maxWidth: 1024); + source: ImageSource.gallery, + maxHeight: 1024, + maxWidth: 1024, + ); if (image != null) { var success = @@ -101,16 +106,20 @@ class ProfileDrawer extends HookConsumerWidget { if (success) { 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( child: Column( mainAxisAlignment: MainAxisAlignment.spaceBetween, @@ -186,9 +195,10 @@ class ProfileDrawer extends HookConsumerWidget { title: const Text( "profile_drawer_sign_out", style: TextStyle( - color: Colors.black54, - fontSize: 14, - fontWeight: FontWeight.bold), + color: Colors.black54, + fontSize: 14, + fontWeight: FontWeight.bold, + ), ).tr(), onTap: () async { bool res = @@ -231,9 +241,10 @@ class ProfileDrawer extends HookConsumerWidget { : "profile_drawer_client_server_up_to_date".tr(), textAlign: TextAlign.center, style: TextStyle( - fontSize: 11, - color: Theme.of(context).primaryColor, - fontWeight: FontWeight.w600), + fontSize: 11, + color: Theme.of(context).primaryColor, + fontWeight: FontWeight.w600, + ), ), ), const Divider(), @@ -271,7 +282,7 @@ class ProfileDrawer extends HookConsumerWidget { ), ), Text( - "${serverInfoState.serverVersion.major}.${serverInfoState.serverVersion.minor}.${serverInfoState.serverVersion.patch}", + "${serverInfoState.serverVersion.major}.${serverInfoState.serverVersion.minor}.${serverInfoState.serverVersion.patch_}", style: TextStyle( fontSize: 11, color: Colors.grey[500], diff --git a/mobile/lib/modules/home/ui/thumbnail_image.dart b/mobile/lib/modules/home/ui/thumbnail_image.dart index ff5af246d6..f86e76505a 100644 --- a/mobile/lib/modules/home/ui/thumbnail_image.dart +++ b/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/modules/home/providers/home_page_state.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:openapi/api.dart'; class ThumbnailImage extends HookConsumerWidget { - final ImmichAsset asset; + final AssetResponseDto asset; const ThumbnailImage({Key? key, required this.asset}) : super(key: key); @@ -22,14 +22,13 @@ class ThumbnailImage extends HookConsumerWidget { var box = Hive.box(userInfoBox); 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 isMultiSelectEnable = ref.watch(homePageStateProvider).isMultiSelectEnable; var deviceId = ref.watch(authenticationProvider).deviceId; - Widget _buildSelectionIcon(ImmichAsset asset) { + Widget _buildSelectionIcon(AssetResponseDto asset) { if (selectedAsset.contains(asset)) { return Icon( Icons.check_circle, @@ -61,7 +60,7 @@ class ThumbnailImage extends HookConsumerWidget { .watch(homePageStateProvider.notifier) .addSingleSelectedItem(asset); } else { - if (asset.type == 'IMAGE') { + if (asset.type == AssetTypeEnum.IMAGE) { AutoRouter.of(context).push( ImageViewerRoute( imageUrl: @@ -74,9 +73,10 @@ class ThumbnailImage extends HookConsumerWidget { } else { AutoRouter.of(context).push( 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( border: isMultiSelectEnable && selectedAsset.contains(asset) ? Border.all( - color: Theme.of(context).primaryColorLight, width: 10) + color: Theme.of(context).primaryColorLight, + width: 10, + ) : const Border(), ), child: CachedNetworkImage( cacheKey: "${asset.id}-${cacheKey.value}", width: 300, height: 300, - memCacheHeight: asset.type == 'IMAGE' ? 250 : 400, + memCacheHeight: asset.type == AssetTypeEnum.IMAGE ? 250 : 400, fit: BoxFit.cover, imageUrl: thumbnailRequestUrl, httpHeaders: { @@ -112,9 +114,11 @@ class ThumbnailImage extends HookConsumerWidget { Transform.scale( scale: 0.2, child: CircularProgressIndicator( - value: downloadProgress.progress), + value: downloadProgress.progress, + ), ), errorWidget: (context, url, error) { + debugPrint("Error getting thumbnail $url = $error"); return Icon( Icons.image_not_supported_outlined, color: Theme.of(context).primaryColor, diff --git a/mobile/lib/modules/home/views/home_page.dart b/mobile/lib/modules/home/views/home_page.dart index 00c1aa8935..66a4834b75 100644 --- a/mobile/lib/modules/home/views/home_page.dart +++ b/mobile/lib/modules/home/views/home_page.dart @@ -26,12 +26,15 @@ class HomePage extends HookConsumerWidget { ref.watch(homePageStateProvider).isMultiSelectEnable; 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() { ref.read(assetProvider.notifier).getAllAsset(); diff --git a/mobile/lib/modules/login/models/authentication_state.model.dart b/mobile/lib/modules/login/models/authentication_state.model.dart index 6603021bb8..4805d499bd 100644 --- a/mobile/lib/modules/login/models/authentication_state.model.dart +++ b/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 { final String deviceId; - final String deviceType; + final DeviceTypeEnum deviceType; final String userId; final String userEmail; final bool isAuthenticated; @@ -13,8 +11,7 @@ class AuthenticationState { final bool isAdmin; final bool shouldChangePassword; final String profileImagePath; - final DeviceInfoRemote deviceInfo; - + final DeviceInfoResponseDto deviceInfo; AuthenticationState({ required this.deviceId, required this.deviceType, @@ -31,7 +28,7 @@ class AuthenticationState { AuthenticationState copyWith({ String? deviceId, - String? deviceType, + DeviceTypeEnum? deviceType, String? userId, String? userEmail, bool? isAuthenticated, @@ -40,7 +37,7 @@ class AuthenticationState { bool? isAdmin, bool? shouldChangePassword, String? profileImagePath, - DeviceInfoRemote? deviceInfo, + DeviceInfoResponseDto? deviceInfo, }) { return AuthenticationState( deviceId: deviceId ?? this.deviceId, @@ -57,45 +54,6 @@ class AuthenticationState { ); } - Map toMap() { - final result = {}; - - 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 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 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)'; diff --git a/mobile/lib/modules/login/models/hive_saved_login_info.model.dart b/mobile/lib/modules/login/models/hive_saved_login_info.model.dart index 471e5df66c..6d367d5978 100644 --- a/mobile/lib/modules/login/models/hive_saved_login_info.model.dart +++ b/mobile/lib/modules/login/models/hive_saved_login_info.model.dart @@ -16,9 +16,10 @@ class HiveSavedLoginInfo { @HiveField(3) 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, + }); } diff --git a/mobile/lib/modules/login/providers/authentication.provider.dart b/mobile/lib/modules/login/providers/authentication.provider.dart index 0634e5731a..885066e220 100644 --- a/mobile/lib/modules/login/providers/authentication.provider.dart +++ b/mobile/lib/modules/login/providers/authentication.provider.dart @@ -1,23 +1,23 @@ -import 'package:dio/dio.dart'; import 'package:flutter/material.dart'; import 'package:hive/hive.dart'; import 'package:hooks_riverpod/hooks_riverpod.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/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/shared/services/api.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 { AuthenticationNotifier( - this._deviceInfoService, this._backupService, this._networkService) - : super( + this._deviceInfoService, + this._backupService, + this._apiService, + ) : super( AuthenticationState( deviceId: "", - deviceType: "", + deviceType: DeviceTypeEnum.ANDROID, userId: "", userEmail: "", firstName: '', @@ -26,12 +26,11 @@ class AuthenticationNotifier extends StateNotifier { isAdmin: false, shouldChangePassword: false, isAuthenticated: false, - deviceInfo: DeviceInfoRemote( + deviceInfo: DeviceInfoResponseDto( id: 0, userId: "", deviceId: "", - deviceType: "", - notificationToken: "", + deviceType: DeviceTypeEnum.ANDROID, createdAt: "", isAutoBackup: false, ), @@ -40,10 +39,14 @@ class AuthenticationNotifier extends StateNotifier { final DeviceInfoService _deviceInfoService; final BackupService _backupService; - final NetworkService _networkService; + final ApiService _apiService; - Future login(String email, String password, String serverEndpoint, - bool isSavedLoginInfo) async { + Future login( + String email, + String password, + String serverEndpoint, + bool isSavedLoginInfo, + ) async { // Store server endpoint to Hive and test endpoint if (serverEndpoint[serverEndpoint.length - 1] == "/") { var validUrl = serverEndpoint.substring(0, serverEndpoint.length - 1); @@ -52,12 +55,12 @@ class AuthenticationNotifier extends StateNotifier { Hive.box(userInfoBox).put(serverEndpointKey, serverEndpoint); } + // Check Server URL validity try { - bool isServerEndpointVerified = await _networkService.pingServer(); - if (!isServerEndpointVerified) { - return false; - } + _apiService.setEndpoint(Hive.box(userInfoBox).get(serverEndpointKey)); + await _apiService.serverInfoApi.pingServer(); } catch (e) { + debugPrint('Invalid Server Endpoint Url $e'); return false; } @@ -72,56 +75,73 @@ class AuthenticationNotifier extends StateNotifier { // Make sign-in request 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( 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) { // Save login info to local storage Hive.box(hiveLoginInfoBox).put( savedLoginInfoKey, 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 { Hive.box(hiveLoginInfoBox) .delete(savedLoginInfoKey); } } catch (e) { + debugPrint("Error logging in $e"); return false; } // Register device info 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); } catch (e) { debugPrint("ERROR Register Device Info: $e"); + return false; } return true; @@ -129,27 +149,7 @@ class AuthenticationNotifier extends StateNotifier { Future logout() async { 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; } @@ -157,11 +157,13 @@ class AuthenticationNotifier extends StateNotifier { setAutoBackup(bool backupState) async { var deviceInfo = await _deviceInfoService.getDeviceInfo(); var deviceId = deviceInfo["deviceId"]; - var deviceType = deviceInfo["deviceType"]; - DeviceInfoRemote deviceInfoRemote = + DeviceTypeEnum deviceType = deviceInfo["deviceType"]; + + DeviceInfoResponseDto updatedDeviceInfo = await _backupService.setAutoBackup(backupState, deviceId, deviceType); - state = state.copyWith(deviceInfo: deviceInfoRemote); + + state = state.copyWith(deviceInfo: updatedDeviceInfo); } updateUserProfileImagePath(String path) { @@ -169,19 +171,20 @@ class AuthenticationNotifier extends StateNotifier { } Future 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); + return true; - } else { + } catch (e) { + debugPrint("Error changing password $e"); return false; } } @@ -192,6 +195,6 @@ final authenticationProvider = return AuthenticationNotifier( ref.watch(deviceInfoServiceProvider), ref.watch(backupServiceProvider), - ref.watch(networkServiceProvider), + ref.watch(apiServiceProvider), ); }); diff --git a/mobile/lib/modules/login/ui/change_password_form.dart b/mobile/lib/modules/login/ui/change_password_form.dart index 4e3632e741..0351803d43 100644 --- a/mobile/lib/modules/login/ui/change_password_form.dart +++ b/mobile/lib/modules/login/ui/change_password_form.dart @@ -140,35 +140,36 @@ class ChangePasswordButton extends ConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { 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), + ), + ); } } diff --git a/mobile/lib/modules/login/ui/login_form.dart b/mobile/lib/modules/login/ui/login_form.dart index b109254a77..dad98505df 100644 --- a/mobile/lib/modules/login/ui/login_form.dart +++ b/mobile/lib/modules/login/ui/login_form.dart @@ -22,22 +22,25 @@ class LoginForm extends HookConsumerWidget { final passwordController = useTextEditingController.fromValue(TextEditingValue.empty); final serverEndpointController = - useTextEditingController(text: 'login_endpoint_hint'.tr()); + useTextEditingController(text: 'login_form_endpoint_hint'.tr()); final isSaveLoginInfo = useState(false); - useEffect(() { - var loginInfo = - Hive.box(hiveLoginInfoBox).get(savedLoginInfoKey); + useEffect( + () { + var loginInfo = Hive.box(hiveLoginInfoBox) + .get(savedLoginInfoKey); - if (loginInfo != null) { - usernameController.text = loginInfo.email; - passwordController.text = loginInfo.password; - serverEndpointController.text = loginInfo.serverUrl; - isSaveLoginInfo.value = loginInfo.isSaveLogin; - } + if (loginInfo != null) { + usernameController.text = loginInfo.email; + passwordController.text = loginInfo.password; + serverEndpointController.text = loginInfo.serverUrl; + isSaveLoginInfo.value = loginInfo.isSaveLogin; + } - return null; - }, []); + return null; + }, + [], + ); return Center( child: ConstrainedBox( @@ -71,14 +74,16 @@ class LoginForm extends HookConsumerWidget { dense: true, side: const BorderSide(color: Colors.grey, width: 1.5), shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(5)), + borderRadius: BorderRadius.circular(5), + ), enableFeedback: true, title: const Text( "login_form_save_login", style: TextStyle( - fontSize: 16, - fontWeight: FontWeight.bold, - color: Colors.grey), + fontSize: 16, + fontWeight: FontWeight.bold, + color: Colors.grey, + ), ).tr(), value: isSaveLoginInfo.value, onChanged: (switchValue) { @@ -108,7 +113,6 @@ class ServerEndpointInput extends StatelessWidget { : super(key: key); String? _validateInput(String? url) { - if (url?.startsWith(RegExp(r'https?://')) == true) { return null; } else { @@ -122,7 +126,7 @@ class ServerEndpointInput extends StatelessWidget { controller: controller, decoration: InputDecoration( labelText: 'login_form_endpoint_url'.tr(), - border: OutlineInputBorder(), + border: const OutlineInputBorder(), hintText: 'login_form_endpoint_hint'.tr(), ), validator: _validateInput, @@ -140,8 +144,9 @@ class EmailInput extends StatelessWidget { if (email == null || email == '') return null; if (email.endsWith(' ')) return 'login_form_err_trailing_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 null; } @@ -151,7 +156,7 @@ class EmailInput extends StatelessWidget { controller: controller, decoration: InputDecoration( labelText: 'login_form_label_email'.tr(), - border: OutlineInputBorder(), + border: const OutlineInputBorder(), hintText: 'login_form_email_hint'.tr(), ), validator: _validateInput, @@ -171,9 +176,10 @@ class PasswordInput extends StatelessWidget { obscureText: true, controller: controller, 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 Widget build(BuildContext context, WidgetRef ref) { 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(); + 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); + var isAuthenticated = + await ref.watch(authenticationProvider.notifier).login( + emailController.text, + passwordController.text, + serverEndpointController.text, + isSavedLoginInfo, + ); - if (isAuthenticated) { - // Resume backup (if enable) then navigate + 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"); - } + if (ref.watch(authenticationProvider).shouldChangePassword && + !ref.watch(authenticationProvider).isAdmin) { + AutoRouter.of(context).push(const ChangePasswordRoute()); } 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(), + ); } } diff --git a/mobile/lib/modules/search/models/curated_location.model.dart b/mobile/lib/modules/search/models/curated_location.model.dart deleted file mode 100644 index a3baebbdf4..0000000000 --- a/mobile/lib/modules/search/models/curated_location.model.dart +++ /dev/null @@ -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 toMap() { - return { - 'id': id, - 'city': city, - 'resizePath': resizePath, - 'deviceAssetId': deviceAssetId, - 'deviceId': deviceId, - }; - } - - factory CuratedLocation.fromMap(Map 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; - } -} diff --git a/mobile/lib/modules/search/models/curated_object.model.dart b/mobile/lib/modules/search/models/curated_object.model.dart deleted file mode 100644 index c389aaf92e..0000000000 --- a/mobile/lib/modules/search/models/curated_object.model.dart +++ /dev/null @@ -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 toMap() { - final result = {}; - - 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 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; - } -} diff --git a/mobile/lib/modules/search/models/search_result_page_state.model.dart b/mobile/lib/modules/search/models/search_result_page_state.model.dart index 4911bb0e6f..1d79dff4b5 100644 --- a/mobile/lib/modules/search/models/search_result_page_state.model.dart +++ b/mobile/lib/modules/search/models/search_result_page_state.model.dart @@ -1,13 +1,13 @@ import 'dart:convert'; import 'package:collection/collection.dart'; -import 'package:immich_mobile/shared/models/immich_asset.model.dart'; +import 'package:openapi/api.dart'; class SearchResultPageState { final bool isLoading; final bool isSuccess; final bool isError; - final List searchResult; + final List searchResult; SearchResultPageState({ required this.isLoading, @@ -20,7 +20,7 @@ class SearchResultPageState { bool? isLoading, bool? isSuccess, bool? isError, - List? searchResult, + List? searchResult, }) { return SearchResultPageState( isLoading: isLoading ?? this.isLoading, @@ -35,7 +35,7 @@ class SearchResultPageState { 'isLoading': isLoading, 'isSuccess': isSuccess, '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, isSuccess: map['isSuccess'] ?? false, isError: map['isError'] ?? false, - searchResult: List.from( - map['searchResult']?.map((x) => ImmichAsset.fromMap(x))), + searchResult: List.from( + map['searchResult']?.map((x) => AssetResponseDto.mapFromJson(x)), + ), ); } diff --git a/mobile/lib/modules/search/providers/search_page_state.provider.dart b/mobile/lib/modules/search/providers/search_page_state.provider.dart index dc61aad1a8..d66b6b280b 100644 --- a/mobile/lib/modules/search/providers/search_page_state.provider.dart +++ b/mobile/lib/modules/search/providers/search_page_state.provider.dart @@ -1,9 +1,8 @@ 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/services/search.service.dart'; +import 'package:openapi/api.dart'; class SearchPageStateNotifier extends StateNotifier { SearchPageStateNotifier(this._searchService) @@ -58,7 +57,7 @@ final searchPageStateProvider = }); final getCuratedLocationProvider = - FutureProvider.autoDispose>((ref) async { + FutureProvider.autoDispose>((ref) async { final SearchService searchService = ref.watch(searchServiceProvider); var curatedLocation = await searchService.getCuratedLocation(); @@ -66,7 +65,7 @@ final getCuratedLocationProvider = }); final getCuratedObjectProvider = - FutureProvider.autoDispose>((ref) async { + FutureProvider.autoDispose>((ref) async { final SearchService searchService = ref.watch(searchServiceProvider); var curatedObject = await searchService.getCuratedObjects(); diff --git a/mobile/lib/modules/search/providers/search_result_page.provider.dart b/mobile/lib/modules/search/providers/search_result_page.provider.dart index eb9f9c3daf..d6984a4b89 100644 --- a/mobile/lib/modules/search/providers/search_result_page.provider.dart +++ b/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/services/search.service.dart'; -import 'package:immich_mobile/shared/models/immich_asset.model.dart'; import 'package:intl/intl.dart'; +import 'package:openapi/api.dart'; class SearchResultPageNotifier extends StateNotifier { SearchResultPageNotifier(this._searchService) @@ -21,19 +21,29 @@ class SearchResultPageNotifier extends StateNotifier { void search(String searchTerm) async { state = state.copyWith( - searchResult: [], isError: false, isLoading: true, isSuccess: false); + searchResult: [], + isError: false, + isLoading: true, + isSuccess: false, + ); - List? assets = await _searchService.searchAsset(searchTerm); + List? assets = + await _searchService.searchAsset(searchTerm); if (assets != null) { state = state.copyWith( - searchResult: assets, - isError: false, - isLoading: false, - isSuccess: true); + searchResult: assets, + isError: false, + isLoading: false, + isSuccess: true, + ); } else { 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; assets.sortByCompare( - (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)), + ); }); diff --git a/mobile/lib/modules/search/services/search.service.dart b/mobile/lib/modules/search/services/search.service.dart index 06e6bab6f1..83e6122a80 100644 --- a/mobile/lib/modules/search/services/search.service.dart +++ b/mobile/lib/modules/search/services/search.service.dart @@ -1,79 +1,54 @@ -import 'dart:convert'; - import 'package:flutter/material.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 { - final NetworkService _networkService; - SearchService(this._networkService); + final ApiService _apiService; + SearchService(this._apiService); Future?> getUserSuggestedSearchTerms() async { try { - var res = await _networkService.getRequest(url: "asset/searchTerm"); - List decodedData = jsonDecode(res.toString()); - - return List.from(decodedData); + return await _apiService.assetApi.getAssetSearchTerms(); } catch (e) { debugPrint("[ERROR] [getUserSuggestedSearchTerms] ${e.toString()}"); return []; } } - Future?> searchAsset(String searchTerm) async { + Future?> searchAsset(String searchTerm) async { try { - var res = await _networkService.postRequest( - url: "asset/search", - data: {"searchTerm": searchTerm}, - ); - - List decodedData = jsonDecode(res.toString()); - - List result = - List.from(decodedData.map((a) => ImmichAsset.fromMap(a))); - - return result; + return await _apiService.assetApi + .searchAsset(SearchAssetDto(searchTerm: searchTerm)); } catch (e) { debugPrint("[ERROR] [searchAsset] ${e.toString()}"); return null; } } - Future?> getCuratedLocation() async { + Future?> getCuratedLocation() async { try { - var res = await _networkService.getRequest(url: "asset/allLocation"); + var locations = await _apiService.assetApi.getCuratedLocations(); - List decodedData = jsonDecode(res.toString()); - - List result = - List.from(decodedData.map((a) => CuratedLocation.fromMap(a))); - - return result; + return locations; } catch (e) { - debugPrint("[ERROR] [getCuratedLocation] ${e.toString()}"); - throw Error(); + debugPrint("Error [getCuratedLocation] ${e.toString()}"); + return []; } } - Future?> getCuratedObjects() async { + Future?> getCuratedObjects() async { try { - var res = await _networkService.getRequest(url: "asset/allObjects"); - - List decodedData = jsonDecode(res.toString()); - - List result = - List.from(decodedData.map((a) => CuratedObject.fromMap(a))); - - return result; + return await _apiService.assetApi.getCuratedObjects(); } catch (e) { - debugPrint("[ERROR] [CuratedObject] ${e.toString()}"); - throw Error(); + debugPrint("Error [getCuratedObjects] ${e.toString()}"); + throw []; } } } diff --git a/mobile/lib/modules/search/ui/search_bar.dart b/mobile/lib/modules/search/ui/search_bar.dart index f1b67749af..f9883f65e0 100644 --- a/mobile/lib/modules/search/ui/search_bar.dart +++ b/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'; 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 Function(String) onSubmitted; @@ -26,7 +28,8 @@ class SearchBar extends HookConsumerWidget with PreferredSizeWidget { ref.watch(searchPageStateProvider.notifier).disableSearch(); searchTermController.clear(); }, - icon: const Icon(Icons.arrow_back_ios_rounded)) + icon: const Icon(Icons.arrow_back_ios_rounded), + ) : const Icon(Icons.search_rounded), title: TextField( controller: searchTermController, @@ -50,10 +53,10 @@ class SearchBar extends HookConsumerWidget with PreferredSizeWidget { }, decoration: InputDecoration( hintText: 'search_bar_hint'.tr(), - enabledBorder: UnderlineInputBorder( + enabledBorder: const UnderlineInputBorder( borderSide: BorderSide(color: Colors.transparent), ), - focusedBorder: UnderlineInputBorder( + focusedBorder: const UnderlineInputBorder( borderSide: BorderSide(color: Colors.transparent), ), ), diff --git a/mobile/lib/modules/search/ui/thumbnail_with_info.dart b/mobile/lib/modules/search/ui/thumbnail_with_info.dart index 36956b9a74..d65db0224c 100644 --- a/mobile/lib/modules/search/ui/thumbnail_with_info.dart +++ b/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:hive_flutter/hive_flutter.dart'; import 'package:immich_mobile/constants/hive_box.dart'; -import 'package:immich_mobile/utils/capitalize_first_letter.dart'; 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 imageUrl; diff --git a/mobile/lib/modules/search/views/search_page.dart b/mobile/lib/modules/search/views/search_page.dart index 9a2a5fb54d..832c641781 100644 --- a/mobile/lib/modules/search/views/search_page.dart +++ b/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:hooks_riverpod/hooks_riverpod.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/ui/search_bar.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/shared/ui/immich_loading_indicator.dart'; import 'package:immich_mobile/utils/capitalize_first_letter.dart'; +import 'package:openapi/api.dart'; // ignore: must_be_immutable class SearchPage extends HookConsumerWidget { @@ -25,15 +24,18 @@ class SearchPage extends HookConsumerWidget { Widget build(BuildContext context, WidgetRef ref) { var box = Hive.box(userInfoBox); final isSearchEnabled = ref.watch(searchPageStateProvider).isSearchEnabled; - AsyncValue> curatedLocation = + AsyncValue> curatedLocation = ref.watch(getCuratedLocationProvider); - AsyncValue> curatedObjects = + AsyncValue> curatedObjects = ref.watch(getCuratedObjectProvider); - useEffect(() { - searchFocusNode = FocusNode(); - return () => searchFocusNode.dispose(); - }, []); + useEffect( + () { + searchFocusNode = FocusNode(); + return () => searchFocusNode.dispose(); + }, + [], + ); _onSearchSubmitted(String searchTerm) async { searchFocusNode.unfocus(); @@ -58,16 +60,16 @@ class SearchPage extends HookConsumerWidget { scrollDirection: Axis.horizontal, itemCount: curatedLocation.value?.length, itemBuilder: ((context, index) { - CuratedLocation locationInfo = curatedLocations[index]; + var locationInfo = curatedLocations[index]; var thumbnailRequestUrl = - '${box.get(serverEndpointKey)}/asset/file?aid=${locationInfo.deviceAssetId}&did=${locationInfo.deviceId}&isThumb=true'; - + '${box.get(serverEndpointKey)}/asset/thumbnail/${locationInfo.id}'; return ThumbnailWithInfo( imageUrl: thumbnailRequestUrl, textInfo: locationInfo.city, onTap: () { AutoRouter.of(context).push( - SearchResultRoute(searchTerm: locationInfo.city)); + SearchResultRoute(searchTerm: locationInfo.city), + ); }, ); }), @@ -109,7 +111,7 @@ class SearchPage extends HookConsumerWidget { scrollDirection: Axis.horizontal, itemCount: curatedObjects.value?.length, itemBuilder: ((context, index) { - CuratedObject curatedObjectInfo = objects[index]; + var curatedObjectInfo = objects[index]; var thumbnailRequestUrl = '${box.get(serverEndpointKey)}/asset/file?aid=${curatedObjectInfo.deviceAssetId}&did=${curatedObjectInfo.deviceId}&isThumb=true'; @@ -117,9 +119,12 @@ class SearchPage extends HookConsumerWidget { imageUrl: thumbnailRequestUrl, textInfo: curatedObjectInfo.object, onTap: () { - AutoRouter.of(context).push(SearchResultRoute( + AutoRouter.of(context).push( + SearchResultRoute( searchTerm: curatedObjectInfo.object - .capitalizeFirstLetter())); + .capitalizeFirstLetter(), + ), + ); }, ); }), @@ -160,7 +165,7 @@ class SearchPage extends HookConsumerWidget { ListView( children: [ Padding( - padding: EdgeInsets.all(16.0), + padding: const EdgeInsets.all(16.0), child: const Text( "search_page_places", style: TextStyle(fontWeight: FontWeight.bold, fontSize: 16), @@ -168,8 +173,8 @@ class SearchPage extends HookConsumerWidget { ), _buildPlaces(), Padding( - padding: EdgeInsets.all(16.0), - child: const Text( + padding: const EdgeInsets.all(16.0), + child: const Text( "search_page_things", style: TextStyle(fontWeight: FontWeight.bold, fontSize: 16), ).tr(), diff --git a/mobile/lib/modules/search/views/search_result_page.dart b/mobile/lib/modules/search/views/search_result_page.dart index 7fd14abfa9..ffcd7c80b5 100644 --- a/mobile/lib/modules/search/views/search_result_page.dart +++ b/mobile/lib/modules/search/views/search_result_page.dart @@ -29,13 +29,18 @@ class SearchResultPage extends HookConsumerWidget { late FocusNode searchFocusNode; - useEffect(() { - searchFocusNode = FocusNode(); + useEffect( + () { + searchFocusNode = FocusNode(); - Future.delayed(Duration.zero, - () => ref.read(searchResultPageProvider.notifier).search(searchTerm)); - return () => searchFocusNode.dispose(); - }, []); + Future.delayed( + Duration.zero, + () => ref.read(searchResultPageProvider.notifier).search(searchTerm), + ); + return () => searchFocusNode.dispose(); + }, + [], + ); _onSearchSubmitted(String newSearchTerm) { debugPrint("Re-Search with $newSearchTerm"); @@ -69,10 +74,10 @@ class SearchResultPage extends HookConsumerWidget { }, decoration: InputDecoration( hintText: 'search_result_page_new_search_hint'.tr(), - enabledBorder: UnderlineInputBorder( + enabledBorder: const UnderlineInputBorder( borderSide: BorderSide(color: Colors.transparent), ), - focusedBorder: UnderlineInputBorder( + focusedBorder: const UnderlineInputBorder( borderSide: BorderSide(color: Colors.transparent), ), ), @@ -90,9 +95,10 @@ class SearchResultPage extends HookConsumerWidget { Text( currentSearchTerm.value, style: TextStyle( - color: Theme.of(context).primaryColor, - fontSize: 13, - fontWeight: FontWeight.bold), + color: Theme.of(context).primaryColor, + fontSize: 13, + fontWeight: FontWeight.bold, + ), maxLines: 1, ), Icon( @@ -116,9 +122,10 @@ class SearchResultPage extends HookConsumerWidget { if (searchResultPageState.isLoading) { return Center( - child: SpinKitDancingSquare( - color: Theme.of(context).primaryColor, - )); + child: SpinKitDancingSquare( + color: Theme.of(context).primaryColor, + ), + ); } if (searchResultPageState.isSuccess) { @@ -184,11 +191,12 @@ class SearchResultPage extends HookConsumerWidget { icon: const Icon(Icons.arrow_back_ios_rounded), ), 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, ), body: GestureDetector( diff --git a/mobile/lib/modules/sharing/models/asset_selection_page_result.model.dart b/mobile/lib/modules/sharing/models/asset_selection_page_result.model.dart index faea8d0eca..e2fd8e3d12 100644 --- a/mobile/lib/modules/sharing/models/asset_selection_page_result.model.dart +++ b/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:immich_mobile/shared/models/immich_asset.model.dart'; +import 'package:openapi/api.dart'; class AssetSelectionPageResult { - final Set selectedNewAsset; - final Set selectedAdditionalAsset; + final Set selectedNewAsset; + final Set selectedAdditionalAsset; final bool isAlbumExist; AssetSelectionPageResult({ @@ -16,8 +14,8 @@ class AssetSelectionPageResult { }); AssetSelectionPageResult copyWith({ - Set? selectedNewAsset, - Set? selectedAdditionalAsset, + Set? selectedNewAsset, + Set? selectedAdditionalAsset, bool? isAlbumExist, }) { return AssetSelectionPageResult( @@ -28,35 +26,6 @@ class AssetSelectionPageResult { ); } - Map toMap() { - final result = {}; - - 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 map) { - return AssetSelectionPageResult( - selectedNewAsset: Set.from( - map['selectedNewAsset']?.map((x) => ImmichAsset.fromMap(x))), - selectedAdditionalAsset: Set.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 String toString() => 'AssetSelectionPageResult(selectedNewAsset: $selectedNewAsset, selectedAdditionalAsset: $selectedAdditionalAsset, isAlbumExist: $isAlbumExist)'; diff --git a/mobile/lib/modules/sharing/models/asset_selection_state.model.dart b/mobile/lib/modules/sharing/models/asset_selection_state.model.dart index 7520bc2a59..6a0a9160a1 100644 --- a/mobile/lib/modules/sharing/models/asset_selection_state.model.dart +++ b/mobile/lib/modules/sharing/models/asset_selection_state.model.dart @@ -1,14 +1,12 @@ -import 'dart:convert'; - import 'package:collection/collection.dart'; -import 'package:immich_mobile/shared/models/immich_asset.model.dart'; +import 'package:openapi/api.dart'; class AssetSelectionState { final Set selectedMonths; - final Set selectedNewAssetsForAlbum; - final Set selectedAdditionalAssetsForAlbum; - final Set selectedAssetsInAlbumViewer; + final Set selectedNewAssetsForAlbum; + final Set selectedAdditionalAssetsForAlbum; + final Set selectedAssetsInAlbumViewer; final bool isMultiselectEnable; /// Indicate the asset selection page is navigated from existing album @@ -24,9 +22,9 @@ class AssetSelectionState { AssetSelectionState copyWith({ Set? selectedMonths, - Set? selectedNewAssetsForAlbum, - Set? selectedAdditionalAssetsForAlbum, - Set? selectedAssetsInAlbumViewer, + Set? selectedNewAssetsForAlbum, + Set? selectedAdditionalAssetsForAlbum, + Set? selectedAssetsInAlbumViewer, bool? isMultiselectEnable, bool? isAlbumExist, }) { @@ -43,49 +41,6 @@ class AssetSelectionState { ); } - Map toMap() { - final result = {}; - - 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 map) { - return AssetSelectionState( - selectedMonths: Set.from(map['selectedMonths']), - selectedNewAssetsForAlbum: Set.from( - map['selectedNewAssetsForAlbum']?.map((x) => ImmichAsset.fromMap(x))), - selectedAdditionalAssetsForAlbum: Set.from( - map['selectedAdditionalAssetsForAlbum'] - ?.map((x) => ImmichAsset.fromMap(x))), - selectedAssetsInAlbumViewer: Set.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 String toString() { return 'AssetSelectionState(selectedMonths: $selectedMonths, selectedNewAssetsForAlbum: $selectedNewAssetsForAlbum, selectedAdditionalAssetsForAlbum: $selectedAdditionalAssetsForAlbum, selectedAssetsInAlbumViewer: $selectedAssetsInAlbumViewer, isMultiselectEnable: $isMultiselectEnable, isAlbumExist: $isAlbumExist)'; @@ -99,10 +54,14 @@ class AssetSelectionState { return other is AssetSelectionState && setEquals(other.selectedMonths, selectedMonths) && setEquals(other.selectedNewAssetsForAlbum, selectedNewAssetsForAlbum) && - setEquals(other.selectedAdditionalAssetsForAlbum, - selectedAdditionalAssetsForAlbum) && setEquals( - other.selectedAssetsInAlbumViewer, selectedAssetsInAlbumViewer) && + other.selectedAdditionalAssetsForAlbum, + selectedAdditionalAssetsForAlbum, + ) && + setEquals( + other.selectedAssetsInAlbumViewer, + selectedAssetsInAlbumViewer, + ) && other.isMultiselectEnable == isMultiselectEnable && other.isAlbumExist == isAlbumExist; } diff --git a/mobile/lib/modules/sharing/models/shared_album.model.dart b/mobile/lib/modules/sharing/models/shared_album.model.dart deleted file mode 100644 index d8f1468927..0000000000 --- a/mobile/lib/modules/sharing/models/shared_album.model.dart +++ /dev/null @@ -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 sharedUsers; - final List? 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? sharedUsers, - List? 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 toMap() { - final result = {}; - - 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 map) { - return SharedAlbum( - id: map['id'] ?? '', - ownerId: map['ownerId'] ?? '', - albumName: map['albumName'] ?? '', - createdAt: map['createdAt'] ?? '', - albumThumbnailAssetId: map['albumThumbnailAssetId'], - sharedUsers: - List.from(map['sharedUsers']?.map((x) => User.fromMap(x))), - assets: map['assets'] != null - ? List.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; - } -} diff --git a/mobile/lib/modules/sharing/providers/album_title.provider.dart b/mobile/lib/modules/sharing/providers/album_title.provider.dart index 5ccc82d401..126b3499a3 100644 --- a/mobile/lib/modules/sharing/providers/album_title.provider.dart +++ b/mobile/lib/modules/sharing/providers/album_title.provider.dart @@ -13,4 +13,5 @@ class AlbumTitleNotifier extends StateNotifier { } final albumTitleProvider = StateNotifierProvider( - (ref) => AlbumTitleNotifier()); + (ref) => AlbumTitleNotifier(), +); diff --git a/mobile/lib/modules/sharing/providers/album_viewer.provider.dart b/mobile/lib/modules/sharing/providers/album_viewer.provider.dart index 4e64b8eb5b..561f937102 100644 --- a/mobile/lib/modules/sharing/providers/album_viewer.provider.dart +++ b/mobile/lib/modules/sharing/providers/album_viewer.provider.dart @@ -30,7 +30,10 @@ class AlbumViewerNotifier extends StateNotifier { } Future changeAlbumTitle( - String albumId, String ownerId, String newAlbumTitle) async { + String albumId, + String ownerId, + String newAlbumTitle, + ) async { SharedAlbumService service = ref.watch(sharedAlbumServiceProvider); bool isSuccess = diff --git a/mobile/lib/modules/sharing/providers/asset_selection.provider.dart b/mobile/lib/modules/sharing/providers/asset_selection.provider.dart index c87a59aa25..809e3d18c1 100644 --- a/mobile/lib/modules/sharing/providers/asset_selection.provider.dart +++ b/mobile/lib/modules/sharing/providers/asset_selection.provider.dart @@ -1,41 +1,46 @@ import 'package:hooks_riverpod/hooks_riverpod.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 { 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) { state = state.copyWith(isAlbumExist: isAlbumExist); } void removeAssetsInMonth( - String removedMonth, List assetsInMonth) { - Set currentAssetList = state.selectedNewAssetsForAlbum; + String removedMonth, + List assetsInMonth, + ) { + Set currentAssetList = state.selectedNewAssetsForAlbum; Set currentMonthList = state.selectedMonths; currentMonthList .removeWhere((selectedMonth) => selectedMonth == removedMonth); - for (ImmichAsset asset in assetsInMonth) { + for (AssetResponseDto asset in assetsInMonth) { currentAssetList.removeWhere((e) => e.id == asset.id); } state = state.copyWith( - selectedNewAssetsForAlbum: currentAssetList, - selectedMonths: currentMonthList); + selectedNewAssetsForAlbum: currentAssetList, + selectedMonths: currentMonthList, + ); } - void addAdditionalAssets(List assets) { + void addAdditionalAssets(List assets) { state = state.copyWith( selectedAdditionalAssetsForAlbum: { ...state.selectedAdditionalAssetsForAlbum, @@ -44,7 +49,7 @@ class AssetSelectionNotifier extends StateNotifier { ); } - void addAllAssetsInMonth(String month, List assetsInMonth) { + void addAllAssetsInMonth(String month, List assetsInMonth) { state = state.copyWith( selectedMonths: {...state.selectedMonths, month}, selectedNewAssetsForAlbum: { @@ -54,7 +59,7 @@ class AssetSelectionNotifier extends StateNotifier { ); } - void addNewAssets(List assets) { + void addNewAssets(List assets) { state = state.copyWith( selectedNewAssetsForAlbum: { ...state.selectedNewAssetsForAlbum, @@ -63,20 +68,20 @@ class AssetSelectionNotifier extends StateNotifier { ); } - void removeSelectedNewAssets(List assets) { - Set currentList = state.selectedNewAssetsForAlbum; + void removeSelectedNewAssets(List assets) { + Set currentList = state.selectedNewAssetsForAlbum; - for (ImmichAsset asset in assets) { + for (AssetResponseDto asset in assets) { currentList.removeWhere((e) => e.id == asset.id); } state = state.copyWith(selectedNewAssetsForAlbum: currentList); } - void removeSelectedAdditionalAssets(List assets) { - Set currentList = state.selectedAdditionalAssetsForAlbum; + void removeSelectedAdditionalAssets(List assets) { + Set currentList = state.selectedAdditionalAssetsForAlbum; - for (ImmichAsset asset in assets) { + for (AssetResponseDto asset in assets) { currentList.removeWhere((e) => e.id == asset.id); } @@ -104,7 +109,7 @@ class AssetSelectionNotifier extends StateNotifier { ); } - void addAssetsInAlbumViewer(List assets) { + void addAssetsInAlbumViewer(List assets) { state = state.copyWith( selectedAssetsInAlbumViewer: { ...state.selectedAssetsInAlbumViewer, @@ -113,10 +118,10 @@ class AssetSelectionNotifier extends StateNotifier { ); } - void removeAssetsInAlbumViewer(List assets) { - Set currentList = state.selectedAssetsInAlbumViewer; + void removeAssetsInAlbumViewer(List assets) { + Set currentList = state.selectedAssetsInAlbumViewer; - for (ImmichAsset asset in assets) { + for (AssetResponseDto asset in assets) { currentList.removeWhere((e) => e.id == asset.id); } diff --git a/mobile/lib/modules/sharing/providers/shared_album.provider.dart b/mobile/lib/modules/sharing/providers/shared_album.provider.dart index efb3c090a7..792db47aa7 100644 --- a/mobile/lib/modules/sharing/providers/shared_album.provider.dart +++ b/mobile/lib/modules/sharing/providers/shared_album.provider.dart @@ -1,17 +1,19 @@ 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:openapi/api.dart'; -class SharedAlbumNotifier extends StateNotifier> { +class SharedAlbumNotifier extends StateNotifier> { SharedAlbumNotifier(this._sharedAlbumService) : super([]); final SharedAlbumService _sharedAlbumService; getAllSharedAlbums() async { - List sharedAlbums = + List? sharedAlbums = await _sharedAlbumService.getAllSharedAlbum(); - state = sharedAlbums; + if (sharedAlbums != null) { + state = sharedAlbums; + } } Future deleteAlbum(String albumId) async { @@ -37,7 +39,9 @@ class SharedAlbumNotifier extends StateNotifier> { } Future removeAssetFromAlbum( - String albumId, List assetIds) async { + String albumId, + List assetIds, + ) async { var res = await _sharedAlbumService.removeAssetFromAlbum(albumId, assetIds); if (res) { @@ -49,12 +53,12 @@ class SharedAlbumNotifier extends StateNotifier> { } final sharedAlbumProvider = - StateNotifierProvider>((ref) { + StateNotifierProvider>((ref) { return SharedAlbumNotifier(ref.watch(sharedAlbumServiceProvider)); }); final sharedAlbumDetailProvider = FutureProvider.autoDispose - .family((ref, albumId) async { + .family((ref, albumId) async { final SharedAlbumService sharedAlbumService = ref.watch(sharedAlbumServiceProvider); diff --git a/mobile/lib/modules/sharing/providers/suggested_shared_users.provider.dart b/mobile/lib/modules/sharing/providers/suggested_shared_users.provider.dart index 8ddc122b26..0c4a2a3503 100644 --- a/mobile/lib/modules/sharing/providers/suggested_shared_users.provider.dart +++ b/mobile/lib/modules/sharing/providers/suggested_shared_users.provider.dart @@ -1,10 +1,10 @@ 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:openapi/api.dart'; final suggestedSharedUsersProvider = - FutureProvider.autoDispose>((ref) async { + FutureProvider.autoDispose>((ref) async { UserService userService = ref.watch(userServiceProvider); - return await userService.getAllUsersInfo(); + return await userService.getAllUsersInfo(isAll: false) ?? []; }); diff --git a/mobile/lib/modules/sharing/services/shared_album.service.dart b/mobile/lib/modules/sharing/services/shared_album.service.dart index 9183dcb8d8..92a995fb91 100644 --- a/mobile/lib/modules/sharing/services/shared_album.service.dart +++ b/mobile/lib/modules/sharing/services/shared_album.service.dart @@ -1,73 +1,69 @@ import 'dart:async'; -import 'dart:convert'; -import 'package:dio/dio.dart'; import 'package:flutter/foundation.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 { - final NetworkService _networkService; - SharedAlbumService(this._networkService); + final ApiService _apiService; + SharedAlbumService(this._apiService); - Future> getAllSharedAlbum() async { + Future?> getAllSharedAlbum() async { try { - var res = await _networkService.getRequest(url: 'album?shared=true'); - List decodedData = jsonDecode(res.toString()); - List result = - List.from(decodedData.map((e) => SharedAlbum.fromMap(e))); - - return result; + return await _apiService.albumApi.getAllAlbums(shared: true); } catch (e) { debugPrint("Error getAllSharedAlbum ${e.toString()}"); + return null; } - - return []; } - Future createSharedAlbum(String albumName, Set assets, - List sharedUserIds) async { + Future createSharedAlbum( + String albumName, + Set assets, + List sharedUserIds, + ) async { 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) { debugPrint("Error createSharedAlbum ${e.toString()}"); return false; } } - Future getAlbumDetail(String albumId) async { + Future getAlbumDetail(String albumId) async { 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) { - throw Exception('Error getAllSharedAlbum ${e.toString()}'); + debugPrint('Error [getAlbumDetail] ${e.toString()}'); + return null; } } Future addAdditionalAssetToAlbum( - Set assets, String albumId) async { + Set assets, + String albumId, + ) async { 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) { debugPrint("Error addAdditionalAssetToAlbum ${e.toString()}"); return false; @@ -75,14 +71,16 @@ class SharedAlbumService { } Future addAdditionalUserToAlbum( - List sharedUserIds, String albumId) async { + List sharedUserIds, + String albumId, + ) async { 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) { debugPrint("Error addAdditionalUserToAlbum ${e.toString()}"); return false; @@ -91,12 +89,7 @@ class SharedAlbumService { Future deleteAlbum(String albumId) async { try { - Response res = await _networkService.deleteRequest(url: 'album/$albumId'); - - if (res.statusCode != 200) { - return false; - } - + await _apiService.albumApi.deleteAlbum(albumId); return true; } catch (e) { debugPrint("Error deleteAlbum ${e.toString()}"); @@ -106,12 +99,7 @@ class SharedAlbumService { Future leaveAlbum(String albumId) async { 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; } catch (e) { @@ -121,16 +109,14 @@ class SharedAlbumService { } Future removeAssetFromAlbum( - String albumId, List assetIds) async { + String albumId, + List assetIds, + ) async { 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; } catch (e) { @@ -140,17 +126,18 @@ class SharedAlbumService { } Future changeTitleAlbum( - String albumId, String ownerId, String newAlbumTitle) async { + String albumId, + String ownerId, + String newAlbumTitle, + ) async { 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; } catch (e) { diff --git a/mobile/lib/modules/sharing/ui/album_action_outlined_button.dart b/mobile/lib/modules/sharing/ui/album_action_outlined_button.dart index 3a4a351cfb..fdfab59768 100644 --- a/mobile/lib/modules/sharing/ui/album_action_outlined_button.dart +++ b/mobile/lib/modules/sharing/ui/album_action_outlined_button.dart @@ -5,12 +5,12 @@ class AlbumActionOutlinedButton extends StatelessWidget { final String labelText; 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 Widget build(BuildContext context) { @@ -31,7 +31,10 @@ class AlbumActionOutlinedButton extends StatelessWidget { label: Text( labelText, style: const TextStyle( - fontSize: 12, fontWeight: FontWeight.bold, color: Colors.black87), + fontSize: 12, + fontWeight: FontWeight.bold, + color: Colors.black87, + ), ), onPressed: onPressed, ), diff --git a/mobile/lib/modules/sharing/ui/album_title_text_field.dart b/mobile/lib/modules/sharing/ui/album_title_text_field.dart index 1556c87fad..61dbad01a6 100644 --- a/mobile/lib/modules/sharing/ui/album_title_text_field.dart +++ b/mobile/lib/modules/sharing/ui/album_title_text_field.dart @@ -31,7 +31,10 @@ class AlbumTitleTextField extends ConsumerWidget { }, focusNode: albumTitleTextFieldFocusNode, style: TextStyle( - fontSize: 28, color: Colors.grey[700], fontWeight: FontWeight.bold), + fontSize: 28, + color: Colors.grey[700], + fontWeight: FontWeight.bold, + ), controller: albumTitleController, onTap: () { isAlbumTitleTextFieldFocus.value = true; diff --git a/mobile/lib/modules/sharing/ui/album_viewer_appbar.dart b/mobile/lib/modules/sharing/ui/album_viewer_appbar.dart index bcb0690a24..dd85021e5b 100644 --- a/mobile/lib/modules/sharing/ui/album_viewer_appbar.dart +++ b/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:hooks_riverpod/hooks_riverpod.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/asset_selection.provider.dart'; import 'package:immich_mobile/modules/sharing/providers/shared_album.provider.dart'; import 'package:immich_mobile/routing/router.dart'; import 'package:immich_mobile/shared/ui/immich_toast.dart'; import 'package:immich_mobile/shared/views/immich_loading_overlay.dart'; +import 'package:openapi/api.dart'; class AlbumViewerAppbar extends HookConsumerWidget with PreferredSizeWidget { const AlbumViewerAppbar({ Key? key, - required AsyncValue albumInfo, + required AsyncValue albumInfo, required this.userId, required this.albumId, }) : _albumInfo = albumInfo, super(key: key); - final AsyncValue _albumInfo; + final AsyncValue _albumInfo; final String userId; final String albumId; @@ -105,7 +105,7 @@ class AlbumViewerAppbar extends HookConsumerWidget with PreferredSizeWidget { _buildBottomSheetActionButton() { if (isMultiSelectionEnable) { - if (_albumInfo.asData?.value.ownerId == userId) { + if (_albumInfo.asData?.value?.ownerId == userId) { return ListTile( leading: const Icon(Icons.delete_sweep_rounded), title: const Text( @@ -118,7 +118,7 @@ class AlbumViewerAppbar extends HookConsumerWidget with PreferredSizeWidget { return const SizedBox(); } } else { - if (_albumInfo.asData?.value.ownerId == userId) { + if (_albumInfo.asData?.value?.ownerId == userId) { return ListTile( leading: const Icon(Icons.delete_forever_rounded), title: const Text( diff --git a/mobile/lib/modules/sharing/ui/album_viewer_editable_title.dart b/mobile/lib/modules/sharing/ui/album_viewer_editable_title.dart index 52d0938680..79f87d5b27 100644 --- a/mobile/lib/modules/sharing/ui/album_viewer_editable_title.dart +++ b/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_hooks/flutter_hooks.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:openapi/api.dart'; class AlbumViewerEditableTitle extends HookConsumerWidget { - final SharedAlbum albumInfo; + final AlbumResponseDto albumInfo; 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 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( onChanged: (value) { diff --git a/mobile/lib/modules/sharing/ui/album_viewer_thumbnail.dart b/mobile/lib/modules/sharing/ui/album_viewer_thumbnail.dart index 95a2b1bf78..c5f3a6bd99 100644 --- a/mobile/lib/modules/sharing/ui/album_viewer_thumbnail.dart +++ b/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/modules/login/providers/authentication.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:openapi/api.dart'; class AlbumViewerThumbnail extends HookConsumerWidget { - final ImmichAsset asset; + final AssetResponseDto asset; const AlbumViewerThumbnail({Key? key, required this.asset}) : super(key: key); @@ -20,7 +20,7 @@ class AlbumViewerThumbnail extends HookConsumerWidget { final cacheKey = useState(1); var box = Hive.box(userInfoBox); 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; final selectedAssetsInAlbumViewer = ref.watch(assetSelectionProvider).selectedAssetsInAlbumViewer; @@ -28,7 +28,7 @@ class AlbumViewerThumbnail extends HookConsumerWidget { ref.watch(assetSelectionProvider).isMultiselectEnable; _viewAsset() { - if (asset.type == 'IMAGE') { + if (asset.type == AssetTypeEnum.IMAGE) { AutoRouter.of(context).push( ImageViewerRoute( imageUrl: @@ -41,9 +41,10 @@ class AlbumViewerThumbnail extends HookConsumerWidget { } else { AutoRouter.of(context).push( 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( onTap: isMultiSelectionEnable ? _handleSelectionGesture : _viewAsset, 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(), + ], ), ); } diff --git a/mobile/lib/modules/sharing/ui/asset_grid_by_month.dart b/mobile/lib/modules/sharing/ui/asset_grid_by_month.dart index 8d719bac57..3586487621 100644 --- a/mobile/lib/modules/sharing/ui/asset_grid_by_month.dart +++ b/mobile/lib/modules/sharing/ui/asset_grid_by_month.dart @@ -1,10 +1,10 @@ import 'package:flutter/material.dart'; import 'package:hooks_riverpod/hooks_riverpod.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 { - final List assetGroup; + final List assetGroup; const AssetGridByMonth({Key? key, required this.assetGroup}) : super(key: key); @override diff --git a/mobile/lib/modules/sharing/ui/month_group_title.dart b/mobile/lib/modules/sharing/ui/month_group_title.dart index 71e160664c..ba766721aa 100644 --- a/mobile/lib/modules/sharing/ui/month_group_title.dart +++ b/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:hooks_riverpod/hooks_riverpod.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 { final String month; - final List assetGroup; + final List 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 Widget build(BuildContext context, WidgetRef ref) { @@ -75,7 +77,11 @@ class MonthGroupTitle extends HookConsumerWidget { return SliverToBoxAdapter( child: Padding( 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( children: [ GestureDetector( diff --git a/mobile/lib/modules/sharing/ui/selection_thumbnail_image.dart b/mobile/lib/modules/sharing/ui/selection_thumbnail_image.dart index 13e6ce2035..24be821066 100644 --- a/mobile/lib/modules/sharing/ui/selection_thumbnail_image.dart +++ b/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:immich_mobile/constants/hive_box.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 { - final ImmichAsset asset; + final AssetResponseDto asset; const SelectionThumbnailImage({Key? key, required this.asset}) : super(key: key); @@ -18,14 +18,14 @@ class SelectionThumbnailImage extends HookConsumerWidget { final cacheKey = useState(1); var box = Hive.box(userInfoBox); 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(assetSelectionProvider).selectedNewAssetsForAlbum; var newAssetsForAlbum = ref.watch(assetSelectionProvider).selectedAdditionalAssetsForAlbum; var isAlbumExist = ref.watch(assetSelectionProvider).isAlbumExist; - Widget _buildSelectionIcon(ImmichAsset asset) { + Widget _buildSelectionIcon(AssetResponseDto asset) { if (selectedAsset.contains(asset) && !isAlbumExist) { return Icon( Icons.check_circle, @@ -103,7 +103,7 @@ class SelectionThumbnailImage extends HookConsumerWidget { cacheKey: "${asset.id}-${cacheKey.value}", width: 150, height: 150, - memCacheHeight: asset.type == 'IMAGE' ? 150 : 150, + memCacheHeight: asset.type == AssetTypeEnum.IMAGE ? 150 : 150, fit: BoxFit.cover, imageUrl: thumbnailRequestUrl, httpHeaders: { @@ -131,14 +131,14 @@ class SelectionThumbnailImage extends HookConsumerWidget { child: _buildSelectionIcon(asset), ), ), - if (asset.type != 'IMAGE') + if (asset.type != AssetTypeEnum.IMAGE) Positioned( bottom: 5, right: 5, child: Row( children: [ Text( - '${asset.duration?.substring(0, 7)}', + asset.duration.substring(0, 7), style: const TextStyle( color: Colors.white, fontSize: 10, diff --git a/mobile/lib/modules/sharing/ui/shared_album_thumbnail_image.dart b/mobile/lib/modules/sharing/ui/shared_album_thumbnail_image.dart index b6b75d65ab..d62f1e097b 100644 --- a/mobile/lib/modules/sharing/ui/shared_album_thumbnail_image.dart +++ b/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:hooks_riverpod/hooks_riverpod.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 { - final ImmichAsset asset; + final AssetResponseDto asset; const SharedAlbumThumbnailImage({Key? key, required this.asset}) : super(key: key); @@ -18,7 +18,7 @@ class SharedAlbumThumbnailImage extends HookConsumerWidget { var box = Hive.box(userInfoBox); var thumbnailRequestUrl = - '${box.get(serverEndpointKey)}/asset/file?aid=${asset.deviceAssetId}&did=${asset.deviceId}&isThumb=true'; + '${box.get(serverEndpointKey)}/asset/thumbnail/${asset.id}'; return GestureDetector( onTap: () { @@ -30,7 +30,7 @@ class SharedAlbumThumbnailImage extends HookConsumerWidget { cacheKey: "${asset.id}-${cacheKey.value}", width: 500, height: 500, - memCacheHeight: asset.type == 'IMAGE' ? 500 : 500, + memCacheHeight: 500, fit: BoxFit.cover, imageUrl: thumbnailRequestUrl, httpHeaders: {"Authorization": "Bearer ${box.get(accessTokenKey)}"}, diff --git a/mobile/lib/modules/sharing/ui/sharing_sliver_appbar.dart b/mobile/lib/modules/sharing/ui/sharing_sliver_appbar.dart index 32adf2138c..e39bcf115e 100644 --- a/mobile/lib/modules/sharing/ui/sharing_sliver_appbar.dart +++ b/mobile/lib/modules/sharing/ui/sharing_sliver_appbar.dart @@ -40,7 +40,8 @@ class SharingSliverAppBar extends StatelessWidget { child: TextButton.icon( style: ButtonStyle( backgroundColor: MaterialStateProperty.all( - Theme.of(context).primaryColor.withAlpha(20)), + Theme.of(context).primaryColor.withAlpha(20), + ), // foregroundColor: MaterialStateProperty.all(Colors.white), ), onPressed: () { @@ -65,7 +66,8 @@ class SharingSliverAppBar extends StatelessWidget { child: TextButton.icon( style: ButtonStyle( backgroundColor: MaterialStateProperty.all( - Theme.of(context).primaryColor.withAlpha(20)), + Theme.of(context).primaryColor.withAlpha(20), + ), // foregroundColor: MaterialStateProperty.all(Colors.white), ), onPressed: null, diff --git a/mobile/lib/modules/sharing/views/album_viewer_page.dart b/mobile/lib/modules/sharing/views/album_viewer_page.dart index 1f26a643ec..c10268d677 100644 --- a/mobile/lib/modules/sharing/views/album_viewer_page.dart +++ b/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/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/shared_album.model.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/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_sliver_persistent_app_bar_delegate.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 { final String albumId; @@ -30,18 +29,18 @@ class AlbumViewerPage extends HookConsumerWidget { Widget build(BuildContext context, WidgetRef ref) { FocusNode titleFocusNode = useFocusNode(); ScrollController scrollController = useScrollController(); - AsyncValue albumInfo = + AsyncValue albumInfo = ref.watch(sharedAlbumDetailProvider(albumId)); final userId = ref.watch(authenticationProvider).userId; /// 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. - void _onAddPhotosPressed(SharedAlbum albumInfo) async { - if (albumInfo.assets?.isNotEmpty == true) { + void _onAddPhotosPressed(AlbumResponseDto albumInfo) async { + if (albumInfo.assets.isNotEmpty == true) { ref .watch(assetSelectionProvider.notifier) - .addNewAssets(albumInfo.assets!.toList()); + .addNewAssets(albumInfo.assets.toList()); } ref.watch(assetSelectionProvider.notifier).setIsAlbumExist(true); @@ -57,7 +56,9 @@ class AlbumViewerPage extends HookConsumerWidget { var isSuccess = await ref .watch(sharedAlbumServiceProvider) .addAdditionalAssetToAlbum( - returnPayload.selectedAdditionalAsset, albumId); + returnPayload.selectedAdditionalAsset, + albumId, + ); if (isSuccess) { ref.refresh(sharedAlbumDetailProvider(albumId)); @@ -72,10 +73,11 @@ class AlbumViewerPage extends HookConsumerWidget { } } - void _onAddUsersPressed(SharedAlbum albumInfo) async { - List? sharedUserIds = await AutoRouter.of(context) - .push?>( - SelectAdditionalUserForSharingRoute(albumInfo: albumInfo)); + void _onAddUsersPressed(AlbumResponseDto albumInfo) async { + List? sharedUserIds = + await AutoRouter.of(context).push?>( + SelectAdditionalUserForSharingRoute(albumInfo: albumInfo), + ); if (sharedUserIds != null) { ImmichLoadingOverlayController.appLoader.show(); @@ -92,7 +94,7 @@ class AlbumViewerPage extends HookConsumerWidget { } } - Widget _buildTitle(SharedAlbum albumInfo) { + Widget _buildTitle(AlbumResponseDto albumInfo) { return Padding( padding: const EdgeInsets.only(left: 8, right: 8, top: 16), child: userId == albumInfo.ownerId @@ -102,19 +104,24 @@ class AlbumViewerPage extends HookConsumerWidget { ) : Padding( 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 = ""; DateTime parsedStartDate = - DateTime.parse(albumInfo.assets!.first.createdAt); + DateTime.parse(albumInfo.assets.first.createdAt); DateTime parsedEndDate = DateTime.parse( - albumInfo.assets?.last.createdAt ?? '11111111'); //Need default. + albumInfo.assets.last.createdAt, + ); //Need default. if (parsedStartDate.year == parsedEndDate.year) { startDate = DateFormat('LLL d').format(parsedStartDate); @@ -129,18 +136,21 @@ class AlbumViewerPage extends HookConsumerWidget { child: Text( "$startDate-$endDate", 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( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ _buildTitle(albumInfo), - if (albumInfo.assets?.isNotEmpty == true) + if (albumInfo.assets.isNotEmpty == true) _buildAlbumDateRange(albumInfo), SizedBox( 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( padding: const EdgeInsets.only(top: 10.0), sliver: SliverGrid( @@ -184,9 +194,9 @@ class AlbumViewerPage extends HookConsumerWidget { ), delegate: SliverChildBuilderDelegate( (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(); } - Widget _buildControlButton(SharedAlbum albumInfo) { + Widget _buildControlButton(AlbumResponseDto albumInfo) { return Padding( padding: const EdgeInsets.only(left: 16.0, top: 8, bottom: 8), child: SizedBox( @@ -219,7 +229,7 @@ class AlbumViewerPage extends HookConsumerWidget { ); } - Widget _buildBody(SharedAlbum albumInfo) { + Widget _buildBody(AlbumResponseDto albumInfo) { return GestureDetector( onTap: () { titleFocusNode.unfocus(); @@ -252,9 +262,16 @@ class AlbumViewerPage extends HookConsumerWidget { return Scaffold( appBar: AlbumViewerAppbar( - albumInfo: albumInfo, userId: userId, albumId: albumId), + albumInfo: albumInfo, + userId: userId, + albumId: albumId, + ), 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")), loading: () => const Center( child: ImmichLoadingIndicator(), diff --git a/mobile/lib/modules/sharing/views/create_shared_album_page.dart b/mobile/lib/modules/sharing/views/create_shared_album_page.dart index 2da6eae0a9..6cc6b1faaf 100644 --- a/mobile/lib/modules/sharing/views/create_shared_album_page.dart +++ b/mobile/lib/modules/sharing/views/create_shared_album_page.dart @@ -56,10 +56,11 @@ class CreateSharedAlbumPage extends HookConsumerWidget { left: 10, ), 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) { return SliverToBoxAdapter( 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', style: TextStyle(fontSize: 12), ).tr(), @@ -86,13 +87,16 @@ class CreateSharedAlbumPage extends HookConsumerWidget { padding: const EdgeInsets.only(top: 16, left: 18, right: 18), child: OutlinedButton.icon( 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, icon: const Icon(Icons.add_rounded), label: Padding( @@ -100,9 +104,10 @@ class CreateSharedAlbumPage extends HookConsumerWidget { child: Text( 'create_shared_album_page_share_select_photos', style: TextStyle( - fontSize: 16, - color: Colors.grey[700], - fontWeight: FontWeight.bold), + fontSize: 16, + color: Colors.grey[700], + fontWeight: FontWeight.bold, + ), ).tr(), ), ), @@ -147,7 +152,8 @@ class CreateSharedAlbumPage extends HookConsumerWidget { return GestureDetector( onTap: _onBackgroundTapped, child: SharedAlbumThumbnailImage( - asset: selectedAssets.toList()[index]), + asset: selectedAssets.toList()[index], + ), ); }, childCount: selectedAssets.length, @@ -160,58 +166,60 @@ class CreateSharedAlbumPage extends HookConsumerWidget { } 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(), - ], - ), - )); + ), + ); } } diff --git a/mobile/lib/modules/sharing/views/select_additional_user_for_sharing_page.dart b/mobile/lib/modules/sharing/views/select_additional_user_for_sharing_page.dart index 59ebc7e009..21fd7acb72 100644 --- a/mobile/lib/modules/sharing/views/select_additional_user_for_sharing_page.dart +++ b/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_hooks/flutter_hooks.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/shared/models/user.model.dart'; import 'package:immich_mobile/shared/ui/immich_loading_indicator.dart'; +import 'package:openapi/api.dart'; class SelectAdditionalUserForSharingPage extends HookConsumerWidget { - final SharedAlbum albumInfo; + final AlbumResponseDto albumInfo; const SelectAdditionalUserForSharingPage({Key? key, required this.albumInfo}) : super(key: key); @override Widget build(BuildContext context, WidgetRef ref) { - AsyncValue> suggestedShareUsers = + AsyncValue> suggestedShareUsers = ref.watch(suggestedSharedUsersProvider); - final sharedUsersList = useState>({}); + final sharedUsersList = useState>({}); _addNewUsersHandler() { AutoRouter.of(context) .pop(sharedUsersList.value.map((e) => e.id).toList()); } - _buildTileIcon(User user) { + _buildTileIcon(UserResponseDto user) { if (sharedUsersList.value.contains(user)) { return CircleAvatar( backgroundColor: Theme.of(context).primaryColor, @@ -43,7 +42,7 @@ class SelectAdditionalUserForSharingPage extends HookConsumerWidget { } } - _buildUserList(List users) { + _buildUserList(List users) { List usersChip = []; for (var user in sharedUsersList.value) { @@ -55,9 +54,10 @@ class SelectAdditionalUserForSharingPage extends HookConsumerWidget { label: Text( user.email, 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], ), Padding( - padding: EdgeInsets.all(16.0), + padding: const EdgeInsets.all(16.0), child: Text( '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( @@ -87,13 +88,16 @@ class SelectAdditionalUserForSharingPage extends HookConsumerWidget { title: Text( users[index].email, style: const TextStyle( - fontSize: 14, fontWeight: FontWeight.bold), + fontSize: 14, + fontWeight: FontWeight.bold, + ), ), onTap: () { if (sharedUsersList.value.contains(users[index])) { sharedUsersList.value = sharedUsersList.value - .where((selectedUser) => - selectedUser.id != users[index].id) + .where( + (selectedUser) => selectedUser.id != users[index].id, + ) .toSet(); } else { sharedUsersList.value = { @@ -139,7 +143,8 @@ class SelectAdditionalUserForSharingPage extends HookConsumerWidget { data: (users) { for (var sharedUsers in albumInfo.sharedUsers) { users.removeWhere( - (u) => u.id == sharedUsers.id || u.id == albumInfo.ownerId); + (u) => u.id == sharedUsers.id || u.id == albumInfo.ownerId, + ); } return _buildUserList(users); diff --git a/mobile/lib/modules/sharing/views/select_user_for_sharing_page.dart b/mobile/lib/modules/sharing/views/select_user_for_sharing_page.dart index 0e0efc4878..74c5c59df6 100644 --- a/mobile/lib/modules/sharing/views/select_user_for_sharing_page.dart +++ b/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/services/shared_album.service.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:openapi/api.dart'; class SelectUserForSharingPage extends HookConsumerWidget { const SelectUserForSharingPage({Key? key}) : super(key: key); + @override Widget build(BuildContext context, WidgetRef ref) { - final sharedUsersList = useState>({}); - AsyncValue> suggestedShareUsers = + final sharedUsersList = useState>({}); + AsyncValue> suggestedShareUsers = ref.watch(suggestedSharedUsersProvider); _createSharedAlbum() async { @@ -37,10 +38,14 @@ class SelectUserForSharingPage extends HookConsumerWidget { .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)) { return CircleAvatar( backgroundColor: Theme.of(context).primaryColor, @@ -58,7 +63,7 @@ class SelectUserForSharingPage extends HookConsumerWidget { } } - _buildUserList(List users) { + _buildUserList(List users) { List usersChip = []; for (var user in sharedUsersList.value) { @@ -70,9 +75,10 @@ class SelectUserForSharingPage extends HookConsumerWidget { label: Text( user.email, 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], ), 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( - fontSize: 14, - color: Colors.grey, - fontWeight: FontWeight.bold), + fontSize: 14, + color: Colors.grey, + fontWeight: FontWeight.bold, + ), ).tr(), ), ListView.builder( @@ -102,13 +109,16 @@ class SelectUserForSharingPage extends HookConsumerWidget { title: Text( users[index].email, style: const TextStyle( - fontSize: 14, fontWeight: FontWeight.bold), + fontSize: 14, + fontWeight: FontWeight.bold, + ), ), onTap: () { if (sharedUsersList.value.contains(users[index])) { sharedUsersList.value = sharedUsersList.value - .where((selectedUser) => - selectedUser.id != users[index].id) + .where( + (selectedUser) => selectedUser.id != users[index].id, + ) .toSet(); } else { sharedUsersList.value = { @@ -141,12 +151,13 @@ class SelectUserForSharingPage extends HookConsumerWidget { ), actions: [ 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( diff --git a/mobile/lib/modules/sharing/views/sharing_page.dart b/mobile/lib/modules/sharing/views/sharing_page.dart index 616b55b4b9..728d52673c 100644 --- a/mobile/lib/modules/sharing/views/sharing_page.dart +++ b/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:hooks_riverpod/hooks_riverpod.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/ui/sharing_sliver_appbar.dart'; import 'package:immich_mobile/routing/router.dart'; +import 'package:openapi/api.dart'; import 'package:transparent_image/transparent_image.dart'; class SharingPage extends HookConsumerWidget { @@ -18,13 +18,16 @@ class SharingPage extends HookConsumerWidget { Widget build(BuildContext context, WidgetRef ref) { var box = Hive.box(userInfoBox); var thumbnailRequestUrl = '${box.get(serverEndpointKey)}/asset/thumbnail'; - final List sharedAlbums = ref.watch(sharedAlbumProvider); + final List sharedAlbums = ref.watch(sharedAlbumProvider); - useEffect(() { - ref.read(sharedAlbumProvider.notifier).getAllSharedAlbums(); + useEffect( + () { + ref.read(sharedAlbumProvider.notifier).getAllSharedAlbums(); - return null; - }, []); + return null; + }, + [], + ); _buildAlbumList() { return SliverList( @@ -60,9 +63,10 @@ class SharingPage extends HookConsumerWidget { maxLines: 1, overflow: TextOverflow.ellipsis, style: TextStyle( - fontSize: 16, - fontWeight: FontWeight.bold, - color: Colors.grey.shade800), + fontSize: 16, + fontWeight: FontWeight.bold, + color: Colors.grey.shade800, + ), ), onTap: () { AutoRouter.of(context) @@ -133,9 +137,9 @@ class SharingPage extends HookConsumerWidget { slivers: [ const SharingSliverAppBar(), SliverPadding( - padding: EdgeInsets.symmetric(horizontal: 12, vertical: 12), + padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 12), sliver: SliverToBoxAdapter( - child: Text( + child: const Text( "sharing_page_album", style: TextStyle( fontWeight: FontWeight.bold, diff --git a/mobile/lib/routing/auth_guard.dart b/mobile/lib/routing/auth_guard.dart index 3677a4f631..692cd4e38c 100644 --- a/mobile/lib/routing/auth_guard.dart +++ b/mobile/lib/routing/auth_guard.dart @@ -1,21 +1,25 @@ -import 'dart:convert'; - 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 { - final NetworkService _networkService = NetworkService(); - + final ApiService _apiService; + AuthGuard(this._apiService); @override void onNavigation(NavigationResolver resolver, StackRouter router) async { 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); + } else { + router.replaceAll([const LoginRoute()]); } } catch (e) { - router.removeUntil((route) => route.name == "LoginRoute"); + debugPrint("Error [onNavigation] ${e.toString()}"); + router.replaceAll([const LoginRoute()]); + return; } } } diff --git a/mobile/lib/routing/router.dart b/mobile/lib/routing/router.dart index 6bce788af9..a964b46951 100644 --- a/mobile/lib/routing/router.dart +++ b/mobile/lib/routing/router.dart @@ -1,5 +1,6 @@ import 'package:auto_route/auto_route.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/backup_album_selection_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_result_page.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/asset_selection_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/sharing_page.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/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/tab_controller_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'; part 'router.gr.dart'; @@ -74,5 +75,9 @@ part 'router.gr.dart'; ], ) 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))); diff --git a/mobile/lib/routing/router.gr.dart b/mobile/lib/routing/router.gr.dart index 16042b45a3..79ce762d01 100644 --- a/mobile/lib/routing/router.gr.dart +++ b/mobile/lib/routing/router.gr.dart @@ -234,7 +234,7 @@ class ImageViewerRoute extends PageRouteInfo { required String imageUrl, required String heroTag, required String thumbnailUrl, - required ImmichAsset asset}) + required AssetResponseDto asset}) : super(ImageViewerRoute.name, path: '/image-viewer-page', args: ImageViewerRouteArgs( @@ -263,7 +263,7 @@ class ImageViewerRouteArgs { final String thumbnailUrl; - final ImmichAsset asset; + final AssetResponseDto asset; @override String toString() { @@ -275,7 +275,7 @@ class ImageViewerRouteArgs { /// [VideoViewerPage] class VideoViewerRoute extends PageRouteInfo { VideoViewerRoute( - {Key? key, required String videoUrl, required ImmichAsset asset}) + {Key? key, required String videoUrl, required AssetResponseDto asset}) : super(VideoViewerRoute.name, path: '/video-viewer-page', args: VideoViewerRouteArgs( @@ -292,7 +292,7 @@ class VideoViewerRouteArgs { final String videoUrl; - final ImmichAsset asset; + final AssetResponseDto asset; @override String toString() { @@ -390,7 +390,7 @@ class AlbumViewerRouteArgs { class SelectAdditionalUserForSharingRoute extends PageRouteInfo { SelectAdditionalUserForSharingRoute( - {Key? key, required SharedAlbum albumInfo}) + {Key? key, required AlbumResponseDto albumInfo}) : super(SelectAdditionalUserForSharingRoute.name, path: '/select-additional-user-for-sharing-page', args: SelectAdditionalUserForSharingRouteArgs( @@ -405,7 +405,7 @@ class SelectAdditionalUserForSharingRouteArgs { final Key? key; - final SharedAlbum albumInfo; + final AlbumResponseDto albumInfo; @override String toString() { diff --git a/mobile/lib/routing/tab_navigation_observer.dart b/mobile/lib/routing/tab_navigation_observer.dart index 5eecc7ec37..bac868cf30 100644 --- a/mobile/lib/routing/tab_navigation_observer.dart +++ b/mobile/lib/routing/tab_navigation_observer.dart @@ -23,7 +23,9 @@ class TabNavigationObserver extends AutoRouterObserver { @override Future didChangeTabRoute( - TabPageRoute route, TabPageRoute previousRoute) async { + TabPageRoute route, + TabPageRoute previousRoute, + ) async { // Perform tasks on re-visit to SearchRoute if (route.name == 'SearchRoute') { // Refresh Location State diff --git a/mobile/lib/shared/models/device_info.model.dart b/mobile/lib/shared/models/device_info.model.dart deleted file mode 100644 index 0792e9109e..0000000000 --- a/mobile/lib/shared/models/device_info.model.dart +++ /dev/null @@ -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 toMap() { - return { - 'id': id, - 'userId': userId, - 'deviceId': deviceId, - 'deviceType': deviceType, - 'notificationToken': notificationToken, - 'createdAt': createdAt, - 'isAutoBackup': isAutoBackup, - }; - } - - factory DeviceInfoRemote.fromMap(Map 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; - } -} diff --git a/mobile/lib/shared/models/exif.model.dart b/mobile/lib/shared/models/exif.model.dart deleted file mode 100644 index 7d8528b363..0000000000 --- a/mobile/lib/shared/models/exif.model.dart +++ /dev/null @@ -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 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 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; - } -} diff --git a/mobile/lib/shared/models/immich_asset.model.dart b/mobile/lib/shared/models/immich_asset.model.dart deleted file mode 100644 index 558c19f2b5..0000000000 --- a/mobile/lib/shared/models/immich_asset.model.dart +++ /dev/null @@ -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 toMap() { - final result = {}; - - 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 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 get props { - return [id]; - } -} diff --git a/mobile/lib/shared/models/immich_asset_with_exif.model.dart b/mobile/lib/shared/models/immich_asset_with_exif.model.dart deleted file mode 100644 index 88ff974373..0000000000 --- a/mobile/lib/shared/models/immich_asset_with_exif.model.dart +++ /dev/null @@ -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 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 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; - } -} diff --git a/mobile/lib/shared/models/mapbox_info.model.dart b/mobile/lib/shared/models/mapbox_info.model.dart deleted file mode 100644 index f3407bab32..0000000000 --- a/mobile/lib/shared/models/mapbox_info.model.dart +++ /dev/null @@ -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 toMap() { - return { - 'isEnable': isEnable, - 'mapboxSecret': mapboxSecret, - }; - } - - factory MapboxInfo.fromMap(Map 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; -} diff --git a/mobile/lib/shared/models/server_info.model.dart b/mobile/lib/shared/models/server_info.model.dart deleted file mode 100644 index 7343fbe961..0000000000 --- a/mobile/lib/shared/models/server_info.model.dart +++ /dev/null @@ -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 toMap() { - return { - 'diskSize': diskSize, - 'diskUse': diskUse, - 'diskAvailable': diskAvailable, - 'diskSizeRaw': diskSizeRaw, - 'diskUseRaw': diskUseRaw, - 'diskAvailableRaw': diskAvailableRaw, - 'diskUsagePercentage': diskUsagePercentage, - }; - } - - factory ServerInfo.fromMap(Map 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; - } -} diff --git a/mobile/lib/shared/models/server_info_state.model.dart b/mobile/lib/shared/models/server_info_state.model.dart index f82beb7949..7b1ea9c918 100644 --- a/mobile/lib/shared/models/server_info_state.model.dart +++ b/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 { - final MapboxInfo mapboxInfo; - final ServerVersion serverVersion; + final ServerVersionReponseDto serverVersion; final bool isVersionMismatch; final String versionMismatchErrorMessage; ServerInfoState({ - required this.mapboxInfo, required this.serverVersion, required this.isVersionMismatch, required this.versionMismatchErrorMessage, }); ServerInfoState copyWith({ - MapboxInfo? mapboxInfo, - ServerVersion? serverVersion, + ServerVersionReponseDto? serverVersion, bool? isVersionMismatch, String? versionMismatchErrorMessage, }) { return ServerInfoState( - mapboxInfo: mapboxInfo ?? this.mapboxInfo, serverVersion: serverVersion ?? this.serverVersion, isVersionMismatch: isVersionMismatch ?? this.isVersionMismatch, versionMismatchErrorMessage: @@ -31,32 +24,9 @@ class ServerInfoState { ); } - Map toMap() { - return { - 'mapboxInfo': mapboxInfo.toMap(), - 'serverVersion': serverVersion.toMap(), - 'isVersionMismatch': isVersionMismatch, - 'versionMismatchErrorMessage': versionMismatchErrorMessage, - }; - } - - factory ServerInfoState.fromMap(Map 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 String toString() { - return 'ServerInfoState(mapboxInfo: $mapboxInfo, serverVersion: $serverVersion, isVersionMismatch: $isVersionMismatch, versionMismatchErrorMessage: $versionMismatchErrorMessage)'; + return 'ServerInfoState( serverVersion: $serverVersion, isVersionMismatch: $isVersionMismatch, versionMismatchErrorMessage: $versionMismatchErrorMessage)'; } @override @@ -64,7 +34,6 @@ class ServerInfoState { if (identical(this, other)) return true; return other is ServerInfoState && - other.mapboxInfo == mapboxInfo && other.serverVersion == serverVersion && other.isVersionMismatch == isVersionMismatch && other.versionMismatchErrorMessage == versionMismatchErrorMessage; @@ -72,8 +41,7 @@ class ServerInfoState { @override int get hashCode { - return mapboxInfo.hashCode ^ - serverVersion.hashCode ^ + return serverVersion.hashCode ^ isVersionMismatch.hashCode ^ versionMismatchErrorMessage.hashCode; } diff --git a/mobile/lib/shared/models/server_version.model.dart b/mobile/lib/shared/models/server_version.model.dart deleted file mode 100644 index d41fb1e133..0000000000 --- a/mobile/lib/shared/models/server_version.model.dart +++ /dev/null @@ -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 toMap() { - return { - 'major': major, - 'minor': minor, - 'patch': patch, - 'build': build, - }; - } - - factory ServerVersion.fromMap(Map 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; - } -} diff --git a/mobile/lib/shared/models/user.model.dart b/mobile/lib/shared/models/user.model.dart deleted file mode 100644 index 99b0ce6cc0..0000000000 --- a/mobile/lib/shared/models/user.model.dart +++ /dev/null @@ -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 toMap() { - final result = {}; - - result.addAll({'id': id}); - result.addAll({'email': email}); - result.addAll({'createdAt': createdAt}); - - return result; - } - - factory User.fromMap(Map 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; -} diff --git a/mobile/lib/shared/providers/asset.provider.dart b/mobile/lib/shared/providers/asset.provider.dart index bb42fbd1eb..84ddc2dc98 100644 --- a/mobile/lib/shared/providers/asset.provider.dart +++ b/mobile/lib/shared/providers/asset.provider.dart @@ -1,21 +1,20 @@ import 'package:flutter/foundation.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/shared/models/immich_asset.model.dart'; import 'package:immich_mobile/shared/services/device_info.service.dart'; import 'package:collection/collection.dart'; import 'package:intl/intl.dart'; +import 'package:openapi/api.dart'; import 'package:photo_manager/photo_manager.dart'; -class AssetNotifier extends StateNotifier> { +class AssetNotifier extends StateNotifier> { final AssetService _assetService; final DeviceInfoService _deviceInfoService = DeviceInfoService(); AssetNotifier(this._assetService) : super([]); getAllAsset() async { - List? allAssets = await _assetService.getAllAsset(); + var allAssets = await _assetService.getAllAsset(); if (allAssets != null) { state = allAssets; @@ -26,11 +25,11 @@ class AssetNotifier extends StateNotifier> { state = []; } - onNewAssetUploaded(ImmichAsset newAsset) { + onNewAssetUploaded(AssetResponseDto newAsset) { state = [...state, newAsset]; } - deleteAssets(Set deleteAssets) async { + deleteAssets(Set deleteAssets) async { var deviceInfo = await _deviceInfoService.getDeviceInfo(); var deviceId = deviceInfo["deviceId"]; var deleteIdList = []; @@ -53,14 +52,15 @@ class AssetNotifier extends StateNotifier> { } // Delete asset on server - List? deleteAssetResult = + List? deleteAssetResult = await _assetService.deleteAssets(deleteAssets); + if (deleteAssetResult == null) { return; } for (var asset in deleteAssetResult) { - if (asset.status == 'success') { + if (asset.status == DeleteAssetStatus.SUCCESS) { state = state.where((immichAsset) => immichAsset.id != asset.id).toList(); } @@ -69,7 +69,7 @@ class AssetNotifier extends StateNotifier> { } final assetProvider = - StateNotifierProvider>((ref) { + StateNotifierProvider>((ref) { return AssetNotifier(ref.watch(assetServiceProvider)); }); @@ -77,17 +77,25 @@ final assetGroupByDateTimeProvider = StateProvider((ref) { var assets = ref.watch(assetProvider); assets.sortByCompare( - (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) { var assets = ref.watch(assetProvider); assets.sortByCompare( - (e) => DateTime.parse(e.createdAt), (a, b) => b.compareTo(a)); + (e) => DateTime.parse(e.createdAt), + (a, b) => b.compareTo(a), + ); - return assets.groupListsBy((element) => - DateFormat('MMMM, y').format(DateTime.parse(element.createdAt))); + return assets.groupListsBy( + (element) => + DateFormat('MMMM, y').format(DateTime.parse(element.createdAt)), + ); }); diff --git a/mobile/lib/shared/providers/release_info.provider.dart b/mobile/lib/shared/providers/release_info.provider.dart index 65276dd737..c9f91ec847 100644 --- a/mobile/lib/shared/providers/release_info.provider.dart +++ b/mobile/lib/shared/providers/release_info.provider.dart @@ -56,4 +56,5 @@ class ReleaseInfoNotifier extends StateNotifier { } final releaseInfoProvider = StateNotifierProvider( - (ref) => ReleaseInfoNotifier()); + (ref) => ReleaseInfoNotifier(), +); diff --git a/mobile/lib/shared/providers/server_info.provider.dart b/mobile/lib/shared/providers/server_info.provider.dart index 2eb2eedfc9..0794e73cd3 100644 --- a/mobile/lib/shared/providers/server_info.provider.dart +++ b/mobile/lib/shared/providers/server_info.provider.dart @@ -1,18 +1,20 @@ 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_version.model.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'; class ServerInfoNotifier extends StateNotifier { ServerInfoNotifier(this._serverInfoService) : super( 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, versionMismatchErrorMessage: "", ), @@ -21,7 +23,8 @@ class ServerInfoNotifier extends StateNotifier { final ServerInfoService _serverInfoService; getServerVersion() async { - ServerVersion? serverVersion = await _serverInfoService.getServerVersion(); + ServerVersionReponseDto? serverVersion = + await _serverInfoService.getServerVersion(); if (serverVersion == null) { state = state.copyWith( @@ -59,7 +62,9 @@ class ServerInfoNotifier extends StateNotifier { } state = state.copyWith( - isVersionMismatch: false, versionMismatchErrorMessage: ""); + isVersionMismatch: false, + versionMismatchErrorMessage: "", + ); } Map _getDetailVersion(String version) { diff --git a/mobile/lib/shared/providers/websocket.provider.dart b/mobile/lib/shared/providers/websocket.provider.dart index 60d13f76d1..bc48762768 100644 --- a/mobile/lib/shared/providers/websocket.provider.dart +++ b/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:immich_mobile/constants/hive_box.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:openapi/api.dart'; import 'package:socket_io_client/socket_io_client.dart'; class WebscoketState { @@ -92,8 +92,11 @@ class WebsocketNotifier extends StateNotifier { socket.on('on_upload_success', (data) { 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) { debugPrint("[WEBSOCKET] Catch Websocket Error - ${e.toString()}"); @@ -119,8 +122,11 @@ class WebsocketNotifier extends StateNotifier { debugPrint("[Websocket] Start listening to event on_upload_success"); state.socket?.on('on_upload_success', (data) { 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); + } }); } } diff --git a/mobile/lib/shared/services/api.service.dart b/mobile/lib/shared/services/api.service.dart new file mode 100644 index 0000000000..37630beb9c --- /dev/null +++ b/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'); + } +} diff --git a/mobile/lib/shared/services/device_info.service.dart b/mobile/lib/shared/services/device_info.service.dart index e1cd8a91fe..429d927fc8 100644 --- a/mobile/lib/shared/services/device_info.service.dart +++ b/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 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:openapi/api.dart'; final deviceInfoServiceProvider = Provider((_) => DeviceInfoService()); @@ -9,12 +10,12 @@ class DeviceInfoService { Future> getDeviceInfo() async { // Get device info var deviceId = await FlutterUdid.consistentUdid; - var deviceType = ""; + var deviceType = DeviceTypeEnum.ANDROID; if (Platform.isAndroid) { - deviceType = "ANDROID"; + deviceType = DeviceTypeEnum.ANDROID; } else if (Platform.isIOS) { - deviceType = "IOS"; + deviceType = DeviceTypeEnum.IOS; } return {"deviceId": deviceId, "deviceType": deviceType}; diff --git a/mobile/lib/shared/services/local_storage.service.dart b/mobile/lib/shared/services/local_storage.service.dart deleted file mode 100644 index f409b2d7c0..0000000000 --- a/mobile/lib/shared/services/local_storage.service.dart +++ /dev/null @@ -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(String key) { - return _box.get(key); - } - - put(String key, T value) { - return _box.put(key, value); - } -} diff --git a/mobile/lib/shared/services/network.service.dart b/mobile/lib/shared/services/network.service.dart index 24ad7649de..f8a502d0a1 100644 --- a/mobile/lib/shared/services/network.service.dart +++ b/mobile/lib/shared/services/network.service.dart @@ -33,10 +33,11 @@ class NetworkService { } } - Future getRequest( - {required String url, - bool isByteResponse = false, - bool isStreamReponse = false}) async { + Future getRequest({ + required String url, + bool isByteResponse = false, + bool isStreamReponse = false, + }) async { try { var savedEndpoint = Hive.box(userInfoBox).get(serverEndpointKey); diff --git a/mobile/lib/shared/services/server_info.service.dart b/mobile/lib/shared/services/server_info.service.dart index df25fe256e..92ea9d89d4 100644 --- a/mobile/lib/shared/services/server_info.service.dart +++ b/mobile/lib/shared/services/server_info.service.dart @@ -1,33 +1,33 @@ -import 'package:dio/dio.dart'; import 'package:flutter/material.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 { - final NetworkService _networkService; - ServerInfoService(this._networkService); + final ApiService _apiService; + ServerInfoService(this._apiService); - Future getServerInfo() async { - Response response = await _networkService.getRequest(url: 'server-info'); - - return ServerInfo.fromJson(response.toString()); + Future getServerInfo() async { + try { + return await _apiService.serverInfoApi.getServerInfo(); + } catch (e) { + debugPrint("Error [getServerInfo] ${e.toString()}"); + return null; + } } - Future getServerVersion() async { + Future getServerVersion() async { try { - Response response = - await _networkService.getRequest(url: 'server-info/version'); - - return ServerVersion.fromJson(response.toString()); + return await _apiService.serverInfoApi.getServerVersion(); } catch (e) { debugPrint("Error getting server info"); + return null; } - - return null; } } diff --git a/mobile/lib/shared/services/user.service.dart b/mobile/lib/shared/services/user.service.dart index 737cd97fc4..482f08635f 100644 --- a/mobile/lib/shared/services/user.service.dart +++ b/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:hive/hive.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:http/http.dart'; import 'package:http_parser/http_parser.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:openapi/api.dart'; -final userServiceProvider = - Provider((ref) => UserService(ref.watch(networkServiceProvider))); +final userServiceProvider = Provider( + (ref) => UserService( + ref.watch(apiServiceProvider), + ), +); class UserService { - final NetworkService _networkService; - UserService(this._networkService); + final ApiService _apiService; - Future> getAllUsersInfo() async { + UserService(this._apiService); + + Future?> getAllUsersInfo({required bool isAll}) async { try { - var res = await _networkService.getRequest(url: 'user'); - List decodedData = jsonDecode(res.toString()); - List result = List.from(decodedData.map((e) => User.fromMap(e))); - - return result; + return await _apiService.userApi.getAllUsers(isAll); } catch (e) { - debugPrint("Error getAllUsersInfo ${e.toString()}"); + debugPrint("Error [getAllUsersInfo] ${e.toString()}"); + return null; } - - return []; } - Future 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 uploadProfileImage(XFile image) async { 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) { - debugPrint("Error uploading file: $e"); + debugPrint("Error [uploadProfileImage] ${e.toString()}"); return null; } } diff --git a/mobile/lib/shared/ui/immich_sliver_persistent_app_bar_delegate.dart b/mobile/lib/shared/ui/immich_sliver_persistent_app_bar_delegate.dart index 85a67daa2c..d9c43cd353 100644 --- a/mobile/lib/shared/ui/immich_sliver_persistent_app_bar_delegate.dart +++ b/mobile/lib/shared/ui/immich_sliver_persistent_app_bar_delegate.dart @@ -22,7 +22,10 @@ class ImmichSliverPersistentAppBarDelegate @override Widget build( - BuildContext context, double shrinkOffset, bool overlapsContent) { + BuildContext context, + double shrinkOffset, + bool overlapsContent, + ) { return SizedBox.expand(child: child); } diff --git a/mobile/lib/shared/views/splash_screen.dart b/mobile/lib/shared/views/splash_screen.dart index fd3828ee4a..23a5456023 100644 --- a/mobile/lib/shared/views/splash_screen.dart +++ b/mobile/lib/shared/views/splash_screen.dart @@ -19,10 +19,13 @@ class SplashScreenPage extends HookConsumerWidget { Hive.box(hiveLoginInfoBox).get(savedLoginInfoKey); void performLoggingIn() async { - var isAuthenticated = await ref - .read(authenticationProvider.notifier) - .login( - loginInfo!.email, loginInfo.password, loginInfo.serverUrl, true); + var isAuthenticated = + await ref.read(authenticationProvider.notifier).login( + loginInfo!.email, + loginInfo.password, + loginInfo.serverUrl, + true, + ); if (isAuthenticated) { // Resume backup (if enable) then navigate @@ -33,14 +36,17 @@ class SplashScreenPage extends HookConsumerWidget { } } - useEffect(() { - if (loginInfo?.isSaveLogin == true) { - performLoggingIn(); - } else { - AutoRouter.of(context).push(const LoginRoute()); - } - return null; - }, []); + useEffect( + () { + if (loginInfo?.isSaveLogin == true) { + performLoggingIn(); + } else { + AutoRouter.of(context).push(const LoginRoute()); + } + return null; + }, + [], + ); return Scaffold( backgroundColor: immichBackgroundColor, diff --git a/mobile/lib/shared/views/tab_controller_page.dart b/mobile/lib/shared/views/tab_controller_page.dart index b32baf33bf..9da202f089 100644 --- a/mobile/lib/shared/views/tab_controller_page.dart +++ b/mobile/lib/shared/views/tab_controller_page.dart @@ -35,23 +35,30 @@ class TabControllerPage extends ConsumerWidget { ? null : BottomNavigationBar( selectedLabelStyle: const TextStyle( - fontSize: 15, fontWeight: FontWeight.w600), + fontSize: 15, + fontWeight: FontWeight.w600, + ), unselectedLabelStyle: const TextStyle( - fontSize: 15, fontWeight: FontWeight.w600), + fontSize: 15, + fontWeight: FontWeight.w600, + ), currentIndex: tabsRouter.activeIndex, onTap: (index) { tabsRouter.setActiveIndex(index); }, items: [ BottomNavigationBarItem( - label: 'tab_controller_nav_photos'.tr(), - icon: const Icon(Icons.photo)), + label: 'tab_controller_nav_photos'.tr(), + icon: const Icon(Icons.photo), + ), BottomNavigationBarItem( - label: 'tab_controller_nav_search'.tr(), - icon: const Icon(Icons.search)), + label: 'tab_controller_nav_search'.tr(), + icon: const Icon(Icons.search), + ), BottomNavigationBarItem( - label: 'tab_controller_nav_sharing'.tr(), - icon: const Icon(Icons.group_outlined)), + label: 'tab_controller_nav_sharing'.tr(), + icon: const Icon(Icons.group_outlined), + ), ], ), ), diff --git a/mobile/lib/shared/views/version_announcement_overlay.dart b/mobile/lib/shared/views/version_announcement_overlay.dart index 90095ef27f..aa56bd8fbd 100644 --- a/mobile/lib/shared/views/version_announcement_overlay.dart +++ b/mobile/lib/shared/views/version_announcement_overlay.dart @@ -54,13 +54,16 @@ class VersionAnnouncementOverlay extends HookConsumerWidget { child: RichText( text: TextSpan( style: const TextStyle( - fontSize: 14, - fontFamily: 'WorkSans', - color: Colors.black87, - height: 1.2), + fontSize: 14, + fontFamily: 'WorkSans', + color: Colors.black87, + height: 1.2, + ), children: [ TextSpan( - text: 'version_announcement_overlay_text_1'.tr(), + text: + 'version_announcement_overlay_text_1' + .tr(), ), const TextSpan( text: ' Immich ', @@ -71,11 +74,14 @@ class VersionAnnouncementOverlay extends HookConsumerWidget { ), ), TextSpan( - text: "version_announcement_overlay_text_2".tr(), + text: + "version_announcement_overlay_text_2" + .tr(), ), TextSpan( - text: "version_announcement_overlay_release_notes" - .tr(), + text: + "version_announcement_overlay_release_notes" + .tr(), style: const TextStyle( decoration: TextDecoration.underline, ), @@ -83,7 +89,9 @@ class VersionAnnouncementOverlay extends HookConsumerWidget { ..onTap = goToReleaseNote, ), TextSpan( - text: "version_announcement_overlay_text_3".tr(), + text: + "version_announcement_overlay_text_3" + .tr(), ) ], ), @@ -92,22 +100,25 @@ class VersionAnnouncementOverlay extends HookConsumerWidget { Padding( padding: const EdgeInsets.only(top: 16.0), child: ElevatedButton( - style: ElevatedButton.styleFrom( - shape: const StadiumBorder(), - visualDensity: VisualDensity.standard, - primary: Colors.indigo, - onPrimary: Colors.grey[50], - elevation: 2, - padding: const EdgeInsets.symmetric( - vertical: 10, horizontal: 25), + style: ElevatedButton.styleFrom( + shape: const StadiumBorder(), + visualDensity: VisualDensity.standard, + primary: Colors.indigo, + onPrimary: Colors.grey[50], + elevation: 2, + padding: const EdgeInsets.symmetric( + vertical: 10, + horizontal: 25, ), - onPressed: onAcknowledgeTapped, - child: const Text( - "version_announcement_overlay_ack", - style: TextStyle( - fontSize: 14, - ), - ).tr()), + ), + onPressed: onAcknowledgeTapped, + child: const Text( + "version_announcement_overlay_ack", + style: TextStyle( + fontSize: 14, + ), + ).tr(), + ), ) ], ), diff --git a/mobile/openapi/.gitignore b/mobile/openapi/.gitignore new file mode 100644 index 0000000000..1be28ced09 --- /dev/null +++ b/mobile/openapi/.gitignore @@ -0,0 +1,17 @@ +# See https://dart.dev/guides/libraries/private-files + +.dart_tool/ +.packages +build/ +pubspec.lock # Except for application packages + +doc/api/ + +# IntelliJ +*.iml +*.ipr +*.iws +.idea/ + +# Mac +.DS_Store diff --git a/mobile/openapi/.openapi-generator-ignore b/mobile/openapi/.openapi-generator-ignore new file mode 100644 index 0000000000..7484ee590a --- /dev/null +++ b/mobile/openapi/.openapi-generator-ignore @@ -0,0 +1,23 @@ +# OpenAPI Generator Ignore +# Generated by openapi-generator https://github.com/openapitools/openapi-generator + +# Use this file to prevent files from being overwritten by the generator. +# The patterns follow closely to .gitignore or .dockerignore. + +# As an example, the C# client generator defines ApiClient.cs. +# You can make changes and tell OpenAPI Generator to ignore just this file by uncommenting the following line: +#ApiClient.cs + +# You can match any string of characters against a directory, file or extension with a single asterisk (*): +#foo/*/qux +# The above matches foo/bar/qux and foo/baz/qux, but not foo/bar/baz/qux + +# You can recursively match patterns against a directory, file or extension with a double asterisk (**): +#foo/**/qux +# This matches foo/bar/qux, foo/baz/qux, and foo/bar/baz/qux + +# You can also negate patterns with an exclamation (!). +# For example, you can ignore all files in a docs folder with the file extension .md: +#docs/*.md +# Then explicitly reverse the ignore rule for a single file: +#!docs/README.md diff --git a/mobile/openapi/.openapi-generator/FILES b/mobile/openapi/.openapi-generator/FILES new file mode 100644 index 0000000000..9818927be1 --- /dev/null +++ b/mobile/openapi/.openapi-generator/FILES @@ -0,0 +1,100 @@ +.gitignore +.travis.yml +README.md +analysis_options.yaml +doc/AddAssetsDto.md +doc/AddUsersDto.md +doc/AdminSignupResponseDto.md +doc/AlbumApi.md +doc/AlbumResponseDto.md +doc/AssetApi.md +doc/AssetFileUploadResponseDto.md +doc/AssetResponseDto.md +doc/AssetTypeEnum.md +doc/AuthenticationApi.md +doc/CheckDuplicateAssetDto.md +doc/CheckDuplicateAssetResponseDto.md +doc/CreateAlbumDto.md +doc/CreateDeviceInfoDto.md +doc/CreateProfileImageResponseDto.md +doc/CreateUserDto.md +doc/CuratedLocationsResponseDto.md +doc/CuratedObjectsResponseDto.md +doc/DeleteAssetDto.md +doc/DeleteAssetResponseDto.md +doc/DeleteAssetStatus.md +doc/DeviceInfoApi.md +doc/DeviceInfoResponseDto.md +doc/DeviceTypeEnum.md +doc/ExifResponseDto.md +doc/LoginCredentialDto.md +doc/LoginResponseDto.md +doc/RemoveAssetsDto.md +doc/SearchAssetDto.md +doc/ServerInfoApi.md +doc/ServerInfoResponseDto.md +doc/ServerPingResponse.md +doc/ServerVersionReponseDto.md +doc/SignUpDto.md +doc/SmartInfoResponseDto.md +doc/UpdateAlbumDto.md +doc/UpdateDeviceInfoDto.md +doc/UpdateUserDto.md +doc/UserApi.md +doc/UserCountResponseDto.md +doc/UserResponseDto.md +doc/ValidateAccessTokenResponseDto.md +git_push.sh +lib/api.dart +lib/api/album_api.dart +lib/api/asset_api.dart +lib/api/authentication_api.dart +lib/api/device_info_api.dart +lib/api/server_info_api.dart +lib/api/user_api.dart +lib/api_client.dart +lib/api_exception.dart +lib/api_helper.dart +lib/auth/api_key_auth.dart +lib/auth/authentication.dart +lib/auth/http_basic_auth.dart +lib/auth/http_bearer_auth.dart +lib/auth/oauth.dart +lib/model/add_assets_dto.dart +lib/model/add_users_dto.dart +lib/model/admin_signup_response_dto.dart +lib/model/album_response_dto.dart +lib/model/asset_file_upload_response_dto.dart +lib/model/asset_response_dto.dart +lib/model/asset_type_enum.dart +lib/model/check_duplicate_asset_dto.dart +lib/model/check_duplicate_asset_response_dto.dart +lib/model/create_album_dto.dart +lib/model/create_device_info_dto.dart +lib/model/create_profile_image_response_dto.dart +lib/model/create_user_dto.dart +lib/model/curated_locations_response_dto.dart +lib/model/curated_objects_response_dto.dart +lib/model/delete_asset_dto.dart +lib/model/delete_asset_response_dto.dart +lib/model/delete_asset_status.dart +lib/model/device_info_response_dto.dart +lib/model/device_type_enum.dart +lib/model/exif_response_dto.dart +lib/model/login_credential_dto.dart +lib/model/login_response_dto.dart +lib/model/remove_assets_dto.dart +lib/model/search_asset_dto.dart +lib/model/server_info_response_dto.dart +lib/model/server_ping_response.dart +lib/model/server_version_reponse_dto.dart +lib/model/sign_up_dto.dart +lib/model/smart_info_response_dto.dart +lib/model/update_album_dto.dart +lib/model/update_device_info_dto.dart +lib/model/update_user_dto.dart +lib/model/user_count_response_dto.dart +lib/model/user_response_dto.dart +lib/model/validate_access_token_response_dto.dart +pubspec.yaml +test/validate_access_token_response_dto_test.dart diff --git a/mobile/openapi/.openapi-generator/VERSION b/mobile/openapi/.openapi-generator/VERSION new file mode 100644 index 0000000000..6d54bbd775 --- /dev/null +++ b/mobile/openapi/.openapi-generator/VERSION @@ -0,0 +1 @@ +6.0.1 \ No newline at end of file diff --git a/mobile/openapi/.travis.yml b/mobile/openapi/.travis.yml new file mode 100644 index 0000000000..2774ccbba0 --- /dev/null +++ b/mobile/openapi/.travis.yml @@ -0,0 +1,14 @@ +# +# AUTO-GENERATED FILE, DO NOT MODIFY! +# +# https://docs.travis-ci.com/user/languages/dart/ +# +language: dart +dart: +# Install a specific stable release +- "2.12" +install: +- pub get + +script: +- pub run test diff --git a/mobile/openapi/README.md b/mobile/openapi/README.md new file mode 100644 index 0000000000..1e9edfc0a0 Binary files /dev/null and b/mobile/openapi/README.md differ diff --git a/mobile/openapi/analysis_options.yaml b/mobile/openapi/analysis_options.yaml new file mode 100644 index 0000000000..e69de29bb2 diff --git a/mobile/openapi/doc/AddAssetsDto.md b/mobile/openapi/doc/AddAssetsDto.md new file mode 100644 index 0000000000..b74211d6b7 Binary files /dev/null and b/mobile/openapi/doc/AddAssetsDto.md differ diff --git a/mobile/openapi/doc/AddUsersDto.md b/mobile/openapi/doc/AddUsersDto.md new file mode 100644 index 0000000000..9f7770d604 Binary files /dev/null and b/mobile/openapi/doc/AddUsersDto.md differ diff --git a/mobile/openapi/doc/AdminSignupResponseDto.md b/mobile/openapi/doc/AdminSignupResponseDto.md new file mode 100644 index 0000000000..ff9890de5d Binary files /dev/null and b/mobile/openapi/doc/AdminSignupResponseDto.md differ diff --git a/mobile/openapi/doc/AlbumApi.md b/mobile/openapi/doc/AlbumApi.md new file mode 100644 index 0000000000..1a0fe61ed4 Binary files /dev/null and b/mobile/openapi/doc/AlbumApi.md differ diff --git a/mobile/openapi/doc/AlbumResponseDto.md b/mobile/openapi/doc/AlbumResponseDto.md new file mode 100644 index 0000000000..79dfd84904 Binary files /dev/null and b/mobile/openapi/doc/AlbumResponseDto.md differ diff --git a/mobile/openapi/doc/AssetApi.md b/mobile/openapi/doc/AssetApi.md new file mode 100644 index 0000000000..89455a9e6a Binary files /dev/null and b/mobile/openapi/doc/AssetApi.md differ diff --git a/mobile/openapi/doc/AssetFileUploadResponseDto.md b/mobile/openapi/doc/AssetFileUploadResponseDto.md new file mode 100644 index 0000000000..1b4f599e17 Binary files /dev/null and b/mobile/openapi/doc/AssetFileUploadResponseDto.md differ diff --git a/mobile/openapi/doc/AssetResponseDto.md b/mobile/openapi/doc/AssetResponseDto.md new file mode 100644 index 0000000000..e693549f6b Binary files /dev/null and b/mobile/openapi/doc/AssetResponseDto.md differ diff --git a/mobile/openapi/doc/AssetTypeEnum.md b/mobile/openapi/doc/AssetTypeEnum.md new file mode 100644 index 0000000000..8d514b090e Binary files /dev/null and b/mobile/openapi/doc/AssetTypeEnum.md differ diff --git a/mobile/openapi/doc/AuthenticationApi.md b/mobile/openapi/doc/AuthenticationApi.md new file mode 100644 index 0000000000..46590ff5ad Binary files /dev/null and b/mobile/openapi/doc/AuthenticationApi.md differ diff --git a/mobile/openapi/doc/CheckDuplicateAssetDto.md b/mobile/openapi/doc/CheckDuplicateAssetDto.md new file mode 100644 index 0000000000..259cdfc81c Binary files /dev/null and b/mobile/openapi/doc/CheckDuplicateAssetDto.md differ diff --git a/mobile/openapi/doc/CheckDuplicateAssetResponseDto.md b/mobile/openapi/doc/CheckDuplicateAssetResponseDto.md new file mode 100644 index 0000000000..736d862243 Binary files /dev/null and b/mobile/openapi/doc/CheckDuplicateAssetResponseDto.md differ diff --git a/mobile/openapi/doc/CreateAlbumDto.md b/mobile/openapi/doc/CreateAlbumDto.md new file mode 100644 index 0000000000..2f91dba103 Binary files /dev/null and b/mobile/openapi/doc/CreateAlbumDto.md differ diff --git a/mobile/openapi/doc/CreateDeviceInfoDto.md b/mobile/openapi/doc/CreateDeviceInfoDto.md new file mode 100644 index 0000000000..d0629bb2d4 Binary files /dev/null and b/mobile/openapi/doc/CreateDeviceInfoDto.md differ diff --git a/mobile/openapi/doc/CreateProfileImageResponseDto.md b/mobile/openapi/doc/CreateProfileImageResponseDto.md new file mode 100644 index 0000000000..3323e7df43 Binary files /dev/null and b/mobile/openapi/doc/CreateProfileImageResponseDto.md differ diff --git a/mobile/openapi/doc/CreateUserDto.md b/mobile/openapi/doc/CreateUserDto.md new file mode 100644 index 0000000000..c7b8b4ca28 Binary files /dev/null and b/mobile/openapi/doc/CreateUserDto.md differ diff --git a/mobile/openapi/doc/CuratedLocationsResponseDto.md b/mobile/openapi/doc/CuratedLocationsResponseDto.md new file mode 100644 index 0000000000..abd86ea535 Binary files /dev/null and b/mobile/openapi/doc/CuratedLocationsResponseDto.md differ diff --git a/mobile/openapi/doc/CuratedObjectsResponseDto.md b/mobile/openapi/doc/CuratedObjectsResponseDto.md new file mode 100644 index 0000000000..559f25148f Binary files /dev/null and b/mobile/openapi/doc/CuratedObjectsResponseDto.md differ diff --git a/mobile/openapi/doc/DeleteAssetDto.md b/mobile/openapi/doc/DeleteAssetDto.md new file mode 100644 index 0000000000..3b60d4eced Binary files /dev/null and b/mobile/openapi/doc/DeleteAssetDto.md differ diff --git a/mobile/openapi/doc/DeleteAssetResponseDto.md b/mobile/openapi/doc/DeleteAssetResponseDto.md new file mode 100644 index 0000000000..4cd44e030e Binary files /dev/null and b/mobile/openapi/doc/DeleteAssetResponseDto.md differ diff --git a/mobile/openapi/doc/DeleteAssetStatus.md b/mobile/openapi/doc/DeleteAssetStatus.md new file mode 100644 index 0000000000..4616d0ae06 Binary files /dev/null and b/mobile/openapi/doc/DeleteAssetStatus.md differ diff --git a/mobile/openapi/doc/DeviceInfoApi.md b/mobile/openapi/doc/DeviceInfoApi.md new file mode 100644 index 0000000000..d1c032e65a Binary files /dev/null and b/mobile/openapi/doc/DeviceInfoApi.md differ diff --git a/mobile/openapi/doc/DeviceInfoResponseDto.md b/mobile/openapi/doc/DeviceInfoResponseDto.md new file mode 100644 index 0000000000..95edd86ab7 Binary files /dev/null and b/mobile/openapi/doc/DeviceInfoResponseDto.md differ diff --git a/mobile/openapi/doc/DeviceTypeEnum.md b/mobile/openapi/doc/DeviceTypeEnum.md new file mode 100644 index 0000000000..ab8f99acbe Binary files /dev/null and b/mobile/openapi/doc/DeviceTypeEnum.md differ diff --git a/mobile/openapi/doc/ExifResponseDto.md b/mobile/openapi/doc/ExifResponseDto.md new file mode 100644 index 0000000000..0e96bdcbe9 Binary files /dev/null and b/mobile/openapi/doc/ExifResponseDto.md differ diff --git a/mobile/openapi/doc/LoginCredentialDto.md b/mobile/openapi/doc/LoginCredentialDto.md new file mode 100644 index 0000000000..bf8eb3d21e Binary files /dev/null and b/mobile/openapi/doc/LoginCredentialDto.md differ diff --git a/mobile/openapi/doc/LoginResponseDto.md b/mobile/openapi/doc/LoginResponseDto.md new file mode 100644 index 0000000000..b4033fe52c Binary files /dev/null and b/mobile/openapi/doc/LoginResponseDto.md differ diff --git a/mobile/openapi/doc/RemoveAssetsDto.md b/mobile/openapi/doc/RemoveAssetsDto.md new file mode 100644 index 0000000000..d2ab847324 Binary files /dev/null and b/mobile/openapi/doc/RemoveAssetsDto.md differ diff --git a/mobile/openapi/doc/SearchAssetDto.md b/mobile/openapi/doc/SearchAssetDto.md new file mode 100644 index 0000000000..1eadd2a0c1 Binary files /dev/null and b/mobile/openapi/doc/SearchAssetDto.md differ diff --git a/mobile/openapi/doc/ServerInfoApi.md b/mobile/openapi/doc/ServerInfoApi.md new file mode 100644 index 0000000000..7d2840f8ca Binary files /dev/null and b/mobile/openapi/doc/ServerInfoApi.md differ diff --git a/mobile/openapi/doc/ServerInfoResponseDto.md b/mobile/openapi/doc/ServerInfoResponseDto.md new file mode 100644 index 0000000000..75694d60de Binary files /dev/null and b/mobile/openapi/doc/ServerInfoResponseDto.md differ diff --git a/mobile/openapi/doc/ServerPingResponse.md b/mobile/openapi/doc/ServerPingResponse.md new file mode 100644 index 0000000000..97a79869fc Binary files /dev/null and b/mobile/openapi/doc/ServerPingResponse.md differ diff --git a/mobile/openapi/doc/ServerVersionReponseDto.md b/mobile/openapi/doc/ServerVersionReponseDto.md new file mode 100644 index 0000000000..e40920e506 Binary files /dev/null and b/mobile/openapi/doc/ServerVersionReponseDto.md differ diff --git a/mobile/openapi/doc/SignUpDto.md b/mobile/openapi/doc/SignUpDto.md new file mode 100644 index 0000000000..6ca6759a93 Binary files /dev/null and b/mobile/openapi/doc/SignUpDto.md differ diff --git a/mobile/openapi/doc/SmartInfoResponseDto.md b/mobile/openapi/doc/SmartInfoResponseDto.md new file mode 100644 index 0000000000..84a273768a Binary files /dev/null and b/mobile/openapi/doc/SmartInfoResponseDto.md differ diff --git a/mobile/openapi/doc/UpdateAlbumDto.md b/mobile/openapi/doc/UpdateAlbumDto.md new file mode 100644 index 0000000000..e31c8b41e5 Binary files /dev/null and b/mobile/openapi/doc/UpdateAlbumDto.md differ diff --git a/mobile/openapi/doc/UpdateDeviceInfoDto.md b/mobile/openapi/doc/UpdateDeviceInfoDto.md new file mode 100644 index 0000000000..75c98bf1b0 Binary files /dev/null and b/mobile/openapi/doc/UpdateDeviceInfoDto.md differ diff --git a/mobile/openapi/doc/UpdateUserDto.md b/mobile/openapi/doc/UpdateUserDto.md new file mode 100644 index 0000000000..1bdb496c5a Binary files /dev/null and b/mobile/openapi/doc/UpdateUserDto.md differ diff --git a/mobile/openapi/doc/UserApi.md b/mobile/openapi/doc/UserApi.md new file mode 100644 index 0000000000..9a6aea5da8 Binary files /dev/null and b/mobile/openapi/doc/UserApi.md differ diff --git a/mobile/openapi/doc/UserCountResponseDto.md b/mobile/openapi/doc/UserCountResponseDto.md new file mode 100644 index 0000000000..65dab56866 Binary files /dev/null and b/mobile/openapi/doc/UserCountResponseDto.md differ diff --git a/mobile/openapi/doc/UserResponseDto.md b/mobile/openapi/doc/UserResponseDto.md new file mode 100644 index 0000000000..d56b444ee0 Binary files /dev/null and b/mobile/openapi/doc/UserResponseDto.md differ diff --git a/mobile/openapi/doc/ValidateAccessTokenResponseDto.md b/mobile/openapi/doc/ValidateAccessTokenResponseDto.md new file mode 100644 index 0000000000..60e1e02cbf Binary files /dev/null and b/mobile/openapi/doc/ValidateAccessTokenResponseDto.md differ diff --git a/mobile/openapi/git_push.sh b/mobile/openapi/git_push.sh new file mode 100644 index 0000000000..f53a75d4fa --- /dev/null +++ b/mobile/openapi/git_push.sh @@ -0,0 +1,57 @@ +#!/bin/sh +# ref: https://help.github.com/articles/adding-an-existing-project-to-github-using-the-command-line/ +# +# Usage example: /bin/sh ./git_push.sh wing328 openapi-petstore-perl "minor update" "gitlab.com" + +git_user_id=$1 +git_repo_id=$2 +release_note=$3 +git_host=$4 + +if [ "$git_host" = "" ]; then + git_host="github.com" + echo "[INFO] No command line input provided. Set \$git_host to $git_host" +fi + +if [ "$git_user_id" = "" ]; then + git_user_id="GIT_USER_ID" + echo "[INFO] No command line input provided. Set \$git_user_id to $git_user_id" +fi + +if [ "$git_repo_id" = "" ]; then + git_repo_id="GIT_REPO_ID" + echo "[INFO] No command line input provided. Set \$git_repo_id to $git_repo_id" +fi + +if [ "$release_note" = "" ]; then + release_note="Minor update" + echo "[INFO] No command line input provided. Set \$release_note to $release_note" +fi + +# Initialize the local directory as a Git repository +git init + +# Adds the files in the local repository and stages them for commit. +git add . + +# Commits the tracked changes and prepares them to be pushed to a remote repository. +git commit -m "$release_note" + +# Sets the new remote +git_remote=$(git remote) +if [ "$git_remote" = "" ]; then # git remote not defined + + if [ "$GIT_TOKEN" = "" ]; then + echo "[INFO] \$GIT_TOKEN (environment variable) is not set. Using the git credential in your environment." + git remote add origin https://${git_host}/${git_user_id}/${git_repo_id}.git + else + git remote add origin https://${git_user_id}:"${GIT_TOKEN}"@${git_host}/${git_user_id}/${git_repo_id}.git + fi + +fi + +git pull origin master + +# Pushes (Forces) the changes in the local repository up to the remote repository +echo "Git pushing to https://${git_host}/${git_user_id}/${git_repo_id}.git" +git push origin master 2>&1 | grep -v 'To https' diff --git a/mobile/openapi/lib/api.dart b/mobile/openapi/lib/api.dart new file mode 100644 index 0000000000..9d632532f1 Binary files /dev/null and b/mobile/openapi/lib/api.dart differ diff --git a/mobile/openapi/lib/api/album_api.dart b/mobile/openapi/lib/api/album_api.dart new file mode 100644 index 0000000000..b0d7f5b8ca Binary files /dev/null and b/mobile/openapi/lib/api/album_api.dart differ diff --git a/mobile/openapi/lib/api/asset_api.dart b/mobile/openapi/lib/api/asset_api.dart new file mode 100644 index 0000000000..7d541c69f7 Binary files /dev/null and b/mobile/openapi/lib/api/asset_api.dart differ diff --git a/mobile/openapi/lib/api/authentication_api.dart b/mobile/openapi/lib/api/authentication_api.dart new file mode 100644 index 0000000000..5fda658eae Binary files /dev/null and b/mobile/openapi/lib/api/authentication_api.dart differ diff --git a/mobile/openapi/lib/api/device_info_api.dart b/mobile/openapi/lib/api/device_info_api.dart new file mode 100644 index 0000000000..c365ac60d2 Binary files /dev/null and b/mobile/openapi/lib/api/device_info_api.dart differ diff --git a/mobile/openapi/lib/api/server_info_api.dart b/mobile/openapi/lib/api/server_info_api.dart new file mode 100644 index 0000000000..8af914b1ce Binary files /dev/null and b/mobile/openapi/lib/api/server_info_api.dart differ diff --git a/mobile/openapi/lib/api/user_api.dart b/mobile/openapi/lib/api/user_api.dart new file mode 100644 index 0000000000..69bd819de8 Binary files /dev/null and b/mobile/openapi/lib/api/user_api.dart differ diff --git a/mobile/openapi/lib/api_client.dart b/mobile/openapi/lib/api_client.dart new file mode 100644 index 0000000000..ae695c9698 Binary files /dev/null and b/mobile/openapi/lib/api_client.dart differ diff --git a/mobile/openapi/lib/api_exception.dart b/mobile/openapi/lib/api_exception.dart new file mode 100644 index 0000000000..796f7f7ee7 Binary files /dev/null and b/mobile/openapi/lib/api_exception.dart differ diff --git a/mobile/openapi/lib/api_helper.dart b/mobile/openapi/lib/api_helper.dart new file mode 100644 index 0000000000..325446498a Binary files /dev/null and b/mobile/openapi/lib/api_helper.dart differ diff --git a/mobile/openapi/lib/auth/api_key_auth.dart b/mobile/openapi/lib/auth/api_key_auth.dart new file mode 100644 index 0000000000..e304eda321 Binary files /dev/null and b/mobile/openapi/lib/auth/api_key_auth.dart differ diff --git a/mobile/openapi/lib/auth/authentication.dart b/mobile/openapi/lib/auth/authentication.dart new file mode 100644 index 0000000000..49baf7c46a Binary files /dev/null and b/mobile/openapi/lib/auth/authentication.dart differ diff --git a/mobile/openapi/lib/auth/http_basic_auth.dart b/mobile/openapi/lib/auth/http_basic_auth.dart new file mode 100644 index 0000000000..81abd7185f Binary files /dev/null and b/mobile/openapi/lib/auth/http_basic_auth.dart differ diff --git a/mobile/openapi/lib/auth/http_bearer_auth.dart b/mobile/openapi/lib/auth/http_bearer_auth.dart new file mode 100644 index 0000000000..213f3483b8 Binary files /dev/null and b/mobile/openapi/lib/auth/http_bearer_auth.dart differ diff --git a/mobile/openapi/lib/auth/oauth.dart b/mobile/openapi/lib/auth/oauth.dart new file mode 100644 index 0000000000..e9b87cffb0 Binary files /dev/null and b/mobile/openapi/lib/auth/oauth.dart differ diff --git a/mobile/openapi/lib/model/add_assets_dto.dart b/mobile/openapi/lib/model/add_assets_dto.dart new file mode 100644 index 0000000000..bebbaeb083 Binary files /dev/null and b/mobile/openapi/lib/model/add_assets_dto.dart differ diff --git a/mobile/openapi/lib/model/add_users_dto.dart b/mobile/openapi/lib/model/add_users_dto.dart new file mode 100644 index 0000000000..6e0b5a88f8 Binary files /dev/null and b/mobile/openapi/lib/model/add_users_dto.dart differ diff --git a/mobile/openapi/lib/model/admin_signup_response_dto.dart b/mobile/openapi/lib/model/admin_signup_response_dto.dart new file mode 100644 index 0000000000..8757449690 Binary files /dev/null and b/mobile/openapi/lib/model/admin_signup_response_dto.dart differ diff --git a/mobile/openapi/lib/model/album_response_dto.dart b/mobile/openapi/lib/model/album_response_dto.dart new file mode 100644 index 0000000000..80ee699e49 Binary files /dev/null and b/mobile/openapi/lib/model/album_response_dto.dart differ diff --git a/mobile/openapi/lib/model/asset_file_upload_response_dto.dart b/mobile/openapi/lib/model/asset_file_upload_response_dto.dart new file mode 100644 index 0000000000..40583b6ce5 Binary files /dev/null and b/mobile/openapi/lib/model/asset_file_upload_response_dto.dart differ diff --git a/mobile/openapi/lib/model/asset_response_dto.dart b/mobile/openapi/lib/model/asset_response_dto.dart new file mode 100644 index 0000000000..cd1e83c5f2 Binary files /dev/null and b/mobile/openapi/lib/model/asset_response_dto.dart differ diff --git a/mobile/openapi/lib/model/asset_type_enum.dart b/mobile/openapi/lib/model/asset_type_enum.dart new file mode 100644 index 0000000000..bf1d3ba193 Binary files /dev/null and b/mobile/openapi/lib/model/asset_type_enum.dart differ diff --git a/mobile/openapi/lib/model/check_duplicate_asset_dto.dart b/mobile/openapi/lib/model/check_duplicate_asset_dto.dart new file mode 100644 index 0000000000..8644381cbf Binary files /dev/null and b/mobile/openapi/lib/model/check_duplicate_asset_dto.dart differ diff --git a/mobile/openapi/lib/model/check_duplicate_asset_response_dto.dart b/mobile/openapi/lib/model/check_duplicate_asset_response_dto.dart new file mode 100644 index 0000000000..5c5453fd8e Binary files /dev/null and b/mobile/openapi/lib/model/check_duplicate_asset_response_dto.dart differ diff --git a/mobile/openapi/lib/model/create_album_dto.dart b/mobile/openapi/lib/model/create_album_dto.dart new file mode 100644 index 0000000000..202a474be7 Binary files /dev/null and b/mobile/openapi/lib/model/create_album_dto.dart differ diff --git a/mobile/openapi/lib/model/create_device_info_dto.dart b/mobile/openapi/lib/model/create_device_info_dto.dart new file mode 100644 index 0000000000..1c2cf46e16 Binary files /dev/null and b/mobile/openapi/lib/model/create_device_info_dto.dart differ diff --git a/mobile/openapi/lib/model/create_profile_image_response_dto.dart b/mobile/openapi/lib/model/create_profile_image_response_dto.dart new file mode 100644 index 0000000000..fc7321cbb8 Binary files /dev/null and b/mobile/openapi/lib/model/create_profile_image_response_dto.dart differ diff --git a/mobile/openapi/lib/model/create_user_dto.dart b/mobile/openapi/lib/model/create_user_dto.dart new file mode 100644 index 0000000000..b8977d46d9 Binary files /dev/null and b/mobile/openapi/lib/model/create_user_dto.dart differ diff --git a/mobile/openapi/lib/model/curated_locations_response_dto.dart b/mobile/openapi/lib/model/curated_locations_response_dto.dart new file mode 100644 index 0000000000..5117aa9908 Binary files /dev/null and b/mobile/openapi/lib/model/curated_locations_response_dto.dart differ diff --git a/mobile/openapi/lib/model/curated_objects_response_dto.dart b/mobile/openapi/lib/model/curated_objects_response_dto.dart new file mode 100644 index 0000000000..00662c5f17 Binary files /dev/null and b/mobile/openapi/lib/model/curated_objects_response_dto.dart differ diff --git a/mobile/openapi/lib/model/delete_asset_dto.dart b/mobile/openapi/lib/model/delete_asset_dto.dart new file mode 100644 index 0000000000..4830dd40c3 Binary files /dev/null and b/mobile/openapi/lib/model/delete_asset_dto.dart differ diff --git a/mobile/openapi/lib/model/delete_asset_response_dto.dart b/mobile/openapi/lib/model/delete_asset_response_dto.dart new file mode 100644 index 0000000000..b71336aca8 Binary files /dev/null and b/mobile/openapi/lib/model/delete_asset_response_dto.dart differ diff --git a/mobile/openapi/lib/model/delete_asset_status.dart b/mobile/openapi/lib/model/delete_asset_status.dart new file mode 100644 index 0000000000..045115aa52 Binary files /dev/null and b/mobile/openapi/lib/model/delete_asset_status.dart differ diff --git a/mobile/openapi/lib/model/device_info_response_dto.dart b/mobile/openapi/lib/model/device_info_response_dto.dart new file mode 100644 index 0000000000..ad831c4691 Binary files /dev/null and b/mobile/openapi/lib/model/device_info_response_dto.dart differ diff --git a/mobile/openapi/lib/model/device_type_enum.dart b/mobile/openapi/lib/model/device_type_enum.dart new file mode 100644 index 0000000000..15f81c3d44 Binary files /dev/null and b/mobile/openapi/lib/model/device_type_enum.dart differ diff --git a/mobile/openapi/lib/model/exif_response_dto.dart b/mobile/openapi/lib/model/exif_response_dto.dart new file mode 100644 index 0000000000..199c955e93 Binary files /dev/null and b/mobile/openapi/lib/model/exif_response_dto.dart differ diff --git a/mobile/openapi/lib/model/login_credential_dto.dart b/mobile/openapi/lib/model/login_credential_dto.dart new file mode 100644 index 0000000000..b8a23be593 Binary files /dev/null and b/mobile/openapi/lib/model/login_credential_dto.dart differ diff --git a/mobile/openapi/lib/model/login_response_dto.dart b/mobile/openapi/lib/model/login_response_dto.dart new file mode 100644 index 0000000000..4eec0475aa Binary files /dev/null and b/mobile/openapi/lib/model/login_response_dto.dart differ diff --git a/mobile/openapi/lib/model/remove_assets_dto.dart b/mobile/openapi/lib/model/remove_assets_dto.dart new file mode 100644 index 0000000000..1457bac388 Binary files /dev/null and b/mobile/openapi/lib/model/remove_assets_dto.dart differ diff --git a/mobile/openapi/lib/model/search_asset_dto.dart b/mobile/openapi/lib/model/search_asset_dto.dart new file mode 100644 index 0000000000..d90682750b Binary files /dev/null and b/mobile/openapi/lib/model/search_asset_dto.dart differ diff --git a/mobile/openapi/lib/model/server_info_response_dto.dart b/mobile/openapi/lib/model/server_info_response_dto.dart new file mode 100644 index 0000000000..0bc23439ca Binary files /dev/null and b/mobile/openapi/lib/model/server_info_response_dto.dart differ diff --git a/mobile/openapi/lib/model/server_ping_response.dart b/mobile/openapi/lib/model/server_ping_response.dart new file mode 100644 index 0000000000..56af4fa5ba Binary files /dev/null and b/mobile/openapi/lib/model/server_ping_response.dart differ diff --git a/mobile/openapi/lib/model/server_version_reponse_dto.dart b/mobile/openapi/lib/model/server_version_reponse_dto.dart new file mode 100644 index 0000000000..72caaf1940 Binary files /dev/null and b/mobile/openapi/lib/model/server_version_reponse_dto.dart differ diff --git a/mobile/openapi/lib/model/sign_up_dto.dart b/mobile/openapi/lib/model/sign_up_dto.dart new file mode 100644 index 0000000000..dc027a3ce0 Binary files /dev/null and b/mobile/openapi/lib/model/sign_up_dto.dart differ diff --git a/mobile/openapi/lib/model/smart_info_response_dto.dart b/mobile/openapi/lib/model/smart_info_response_dto.dart new file mode 100644 index 0000000000..e291cf13e3 Binary files /dev/null and b/mobile/openapi/lib/model/smart_info_response_dto.dart differ diff --git a/mobile/openapi/lib/model/update_album_dto.dart b/mobile/openapi/lib/model/update_album_dto.dart new file mode 100644 index 0000000000..74f680b185 Binary files /dev/null and b/mobile/openapi/lib/model/update_album_dto.dart differ diff --git a/mobile/openapi/lib/model/update_device_info_dto.dart b/mobile/openapi/lib/model/update_device_info_dto.dart new file mode 100644 index 0000000000..c11a2df567 Binary files /dev/null and b/mobile/openapi/lib/model/update_device_info_dto.dart differ diff --git a/mobile/openapi/lib/model/update_user_dto.dart b/mobile/openapi/lib/model/update_user_dto.dart new file mode 100644 index 0000000000..d91de3aa04 Binary files /dev/null and b/mobile/openapi/lib/model/update_user_dto.dart differ diff --git a/mobile/openapi/lib/model/user_count_response_dto.dart b/mobile/openapi/lib/model/user_count_response_dto.dart new file mode 100644 index 0000000000..ba11a4c362 Binary files /dev/null and b/mobile/openapi/lib/model/user_count_response_dto.dart differ diff --git a/mobile/openapi/lib/model/user_response_dto.dart b/mobile/openapi/lib/model/user_response_dto.dart new file mode 100644 index 0000000000..740808ce25 Binary files /dev/null and b/mobile/openapi/lib/model/user_response_dto.dart differ diff --git a/mobile/openapi/lib/model/validate_access_token_response_dto.dart b/mobile/openapi/lib/model/validate_access_token_response_dto.dart new file mode 100644 index 0000000000..b3f9f97359 Binary files /dev/null and b/mobile/openapi/lib/model/validate_access_token_response_dto.dart differ diff --git a/mobile/openapi/pubspec.lock b/mobile/openapi/pubspec.lock new file mode 100644 index 0000000000..e4c9e2004b --- /dev/null +++ b/mobile/openapi/pubspec.lock @@ -0,0 +1,355 @@ +# Generated by pub +# See https://dart.dev/tools/pub/glossary#lockfile +packages: + _fe_analyzer_shared: + dependency: transitive + description: + name: _fe_analyzer_shared + url: "https://pub.dartlang.org" + source: hosted + version: "31.0.0" + analyzer: + dependency: transitive + description: + name: analyzer + url: "https://pub.dartlang.org" + source: hosted + version: "2.8.0" + args: + dependency: transitive + description: + name: args + url: "https://pub.dartlang.org" + source: hosted + version: "2.3.1" + async: + dependency: transitive + description: + name: async + url: "https://pub.dartlang.org" + source: hosted + version: "2.9.0" + boolean_selector: + dependency: transitive + description: + name: boolean_selector + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.0" + cli_util: + dependency: transitive + description: + name: cli_util + url: "https://pub.dartlang.org" + source: hosted + version: "0.3.5" + clock: + dependency: transitive + description: + name: clock + url: "https://pub.dartlang.org" + source: hosted + version: "1.1.1" + collection: + dependency: transitive + description: + name: collection + url: "https://pub.dartlang.org" + source: hosted + version: "1.16.0" + convert: + dependency: transitive + description: + name: convert + url: "https://pub.dartlang.org" + source: hosted + version: "3.0.2" + coverage: + dependency: transitive + description: + name: coverage + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.3" + crypto: + dependency: transitive + description: + name: crypto + url: "https://pub.dartlang.org" + source: hosted + version: "3.0.2" + file: + dependency: transitive + description: + name: file + url: "https://pub.dartlang.org" + source: hosted + version: "6.1.2" + frontend_server_client: + dependency: transitive + description: + name: frontend_server_client + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.3" + glob: + dependency: transitive + description: + name: glob + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.0" + http: + dependency: "direct main" + description: + name: http + url: "https://pub.dartlang.org" + source: hosted + version: "0.13.4" + http_multi_server: + dependency: transitive + description: + name: http_multi_server + url: "https://pub.dartlang.org" + source: hosted + version: "3.2.1" + http_parser: + dependency: transitive + description: + name: http_parser + url: "https://pub.dartlang.org" + source: hosted + version: "4.0.1" + intl: + dependency: "direct main" + description: + name: intl + url: "https://pub.dartlang.org" + source: hosted + version: "0.17.0" + io: + dependency: transitive + description: + name: io + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.3" + js: + dependency: transitive + description: + name: js + url: "https://pub.dartlang.org" + source: hosted + version: "0.6.4" + logging: + dependency: transitive + description: + name: logging + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.2" + matcher: + dependency: transitive + description: + name: matcher + url: "https://pub.dartlang.org" + source: hosted + version: "0.12.11" + meta: + dependency: "direct main" + description: + name: meta + url: "https://pub.dartlang.org" + source: hosted + version: "1.8.0" + mime: + dependency: transitive + description: + name: mime + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.2" + node_preamble: + dependency: transitive + description: + name: node_preamble + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.1" + package_config: + dependency: transitive + description: + name: package_config + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.0" + path: + dependency: transitive + description: + name: path + url: "https://pub.dartlang.org" + source: hosted + version: "1.8.2" + pedantic: + dependency: transitive + description: + name: pedantic + url: "https://pub.dartlang.org" + source: hosted + version: "1.11.1" + pool: + dependency: transitive + description: + name: pool + url: "https://pub.dartlang.org" + source: hosted + version: "1.5.1" + pub_semver: + dependency: transitive + description: + name: pub_semver + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.1" + shelf: + dependency: transitive + description: + name: shelf + url: "https://pub.dartlang.org" + source: hosted + version: "1.3.1" + shelf_packages_handler: + dependency: transitive + description: + name: shelf_packages_handler + url: "https://pub.dartlang.org" + source: hosted + version: "3.0.1" + shelf_static: + dependency: transitive + description: + name: shelf_static + url: "https://pub.dartlang.org" + source: hosted + version: "1.1.1" + shelf_web_socket: + dependency: transitive + description: + name: shelf_web_socket + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.2" + source_map_stack_trace: + dependency: transitive + description: + name: source_map_stack_trace + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.0" + source_maps: + dependency: transitive + description: + name: source_maps + url: "https://pub.dartlang.org" + source: hosted + version: "0.10.10" + source_span: + dependency: transitive + description: + name: source_span + url: "https://pub.dartlang.org" + source: hosted + version: "1.9.0" + stack_trace: + dependency: transitive + description: + name: stack_trace + url: "https://pub.dartlang.org" + source: hosted + version: "1.10.0" + stream_channel: + dependency: transitive + description: + name: stream_channel + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.0" + string_scanner: + dependency: transitive + description: + name: string_scanner + url: "https://pub.dartlang.org" + source: hosted + version: "1.1.1" + term_glyph: + dependency: transitive + description: + name: term_glyph + url: "https://pub.dartlang.org" + source: hosted + version: "1.2.1" + test: + dependency: "direct dev" + description: + name: test + url: "https://pub.dartlang.org" + source: hosted + version: "1.17.12" + test_api: + dependency: transitive + description: + name: test_api + url: "https://pub.dartlang.org" + source: hosted + version: "0.4.3" + test_core: + dependency: transitive + description: + name: test_core + url: "https://pub.dartlang.org" + source: hosted + version: "0.4.2" + typed_data: + dependency: transitive + description: + name: typed_data + url: "https://pub.dartlang.org" + source: hosted + version: "1.3.1" + vm_service: + dependency: transitive + description: + name: vm_service + url: "https://pub.dartlang.org" + source: hosted + version: "7.5.0" + watcher: + dependency: transitive + description: + name: watcher + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.1" + web_socket_channel: + dependency: transitive + description: + name: web_socket_channel + url: "https://pub.dartlang.org" + source: hosted + version: "2.2.0" + webkit_inspection_protocol: + dependency: transitive + description: + name: webkit_inspection_protocol + url: "https://pub.dartlang.org" + source: hosted + version: "1.1.0" + yaml: + dependency: transitive + description: + name: yaml + url: "https://pub.dartlang.org" + source: hosted + version: "3.1.1" +sdks: + dart: ">=2.16.0 <3.0.0" diff --git a/mobile/openapi/pubspec.yaml b/mobile/openapi/pubspec.yaml new file mode 100644 index 0000000000..00043ba660 --- /dev/null +++ b/mobile/openapi/pubspec.yaml @@ -0,0 +1,16 @@ +# +# AUTO-GENERATED FILE, DO NOT MODIFY! +# + +name: 'openapi' +version: '1.0.0' +description: 'OpenAPI API client' +homepage: 'homepage' +environment: + sdk: '>=2.12.0 <3.0.0' +dependencies: + http: '>=0.13.0 <0.14.0' + intl: '^0.17.0' + meta: '^1.1.8' +dev_dependencies: + test: '>=1.16.0 <1.18.0' diff --git a/mobile/openapi/test/add_assets_dto_test.dart b/mobile/openapi/test/add_assets_dto_test.dart new file mode 100644 index 0000000000..ec660c4000 Binary files /dev/null and b/mobile/openapi/test/add_assets_dto_test.dart differ diff --git a/mobile/openapi/test/add_users_dto_test.dart b/mobile/openapi/test/add_users_dto_test.dart new file mode 100644 index 0000000000..3dadfd8b4d Binary files /dev/null and b/mobile/openapi/test/add_users_dto_test.dart differ diff --git a/mobile/openapi/test/admin_signup_response_dto_test.dart b/mobile/openapi/test/admin_signup_response_dto_test.dart new file mode 100644 index 0000000000..d71763a327 Binary files /dev/null and b/mobile/openapi/test/admin_signup_response_dto_test.dart differ diff --git a/mobile/openapi/test/album_api_test.dart b/mobile/openapi/test/album_api_test.dart new file mode 100644 index 0000000000..f13e7fa762 Binary files /dev/null and b/mobile/openapi/test/album_api_test.dart differ diff --git a/mobile/openapi/test/album_response_dto_test.dart b/mobile/openapi/test/album_response_dto_test.dart new file mode 100644 index 0000000000..d1ad631e9a Binary files /dev/null and b/mobile/openapi/test/album_response_dto_test.dart differ diff --git a/mobile/openapi/test/asset_api_test.dart b/mobile/openapi/test/asset_api_test.dart new file mode 100644 index 0000000000..b855f19ea8 Binary files /dev/null and b/mobile/openapi/test/asset_api_test.dart differ diff --git a/mobile/openapi/test/asset_file_upload_response_dto_test.dart b/mobile/openapi/test/asset_file_upload_response_dto_test.dart new file mode 100644 index 0000000000..c53ca650ca Binary files /dev/null and b/mobile/openapi/test/asset_file_upload_response_dto_test.dart differ diff --git a/mobile/openapi/test/asset_response_dto_test.dart b/mobile/openapi/test/asset_response_dto_test.dart new file mode 100644 index 0000000000..95e9527bda Binary files /dev/null and b/mobile/openapi/test/asset_response_dto_test.dart differ diff --git a/mobile/openapi/test/asset_type_enum_test.dart b/mobile/openapi/test/asset_type_enum_test.dart new file mode 100644 index 0000000000..a826ee679f Binary files /dev/null and b/mobile/openapi/test/asset_type_enum_test.dart differ diff --git a/mobile/openapi/test/authentication_api_test.dart b/mobile/openapi/test/authentication_api_test.dart new file mode 100644 index 0000000000..2ab9205f81 Binary files /dev/null and b/mobile/openapi/test/authentication_api_test.dart differ diff --git a/mobile/openapi/test/check_duplicate_asset_dto_test.dart b/mobile/openapi/test/check_duplicate_asset_dto_test.dart new file mode 100644 index 0000000000..2e8572da45 Binary files /dev/null and b/mobile/openapi/test/check_duplicate_asset_dto_test.dart differ diff --git a/mobile/openapi/test/check_duplicate_asset_response_dto_test.dart b/mobile/openapi/test/check_duplicate_asset_response_dto_test.dart new file mode 100644 index 0000000000..fbe09b7761 Binary files /dev/null and b/mobile/openapi/test/check_duplicate_asset_response_dto_test.dart differ diff --git a/mobile/openapi/test/create_album_dto_test.dart b/mobile/openapi/test/create_album_dto_test.dart new file mode 100644 index 0000000000..48263d393a Binary files /dev/null and b/mobile/openapi/test/create_album_dto_test.dart differ diff --git a/mobile/openapi/test/create_device_info_dto_test.dart b/mobile/openapi/test/create_device_info_dto_test.dart new file mode 100644 index 0000000000..fe250e7e7f Binary files /dev/null and b/mobile/openapi/test/create_device_info_dto_test.dart differ diff --git a/mobile/openapi/test/create_profile_image_response_dto_test.dart b/mobile/openapi/test/create_profile_image_response_dto_test.dart new file mode 100644 index 0000000000..83fee5b295 Binary files /dev/null and b/mobile/openapi/test/create_profile_image_response_dto_test.dart differ diff --git a/mobile/openapi/test/create_user_dto_test.dart b/mobile/openapi/test/create_user_dto_test.dart new file mode 100644 index 0000000000..8e2e7d445a Binary files /dev/null and b/mobile/openapi/test/create_user_dto_test.dart differ diff --git a/mobile/openapi/test/curated_locations_response_dto_test.dart b/mobile/openapi/test/curated_locations_response_dto_test.dart new file mode 100644 index 0000000000..da4cccf3da Binary files /dev/null and b/mobile/openapi/test/curated_locations_response_dto_test.dart differ diff --git a/mobile/openapi/test/curated_objects_response_dto_test.dart b/mobile/openapi/test/curated_objects_response_dto_test.dart new file mode 100644 index 0000000000..8ef640bd92 Binary files /dev/null and b/mobile/openapi/test/curated_objects_response_dto_test.dart differ diff --git a/mobile/openapi/test/delete_asset_dto_test.dart b/mobile/openapi/test/delete_asset_dto_test.dart new file mode 100644 index 0000000000..4e04b65e9b Binary files /dev/null and b/mobile/openapi/test/delete_asset_dto_test.dart differ diff --git a/mobile/openapi/test/delete_asset_response_dto_test.dart b/mobile/openapi/test/delete_asset_response_dto_test.dart new file mode 100644 index 0000000000..de09a5b31a Binary files /dev/null and b/mobile/openapi/test/delete_asset_response_dto_test.dart differ diff --git a/mobile/openapi/test/delete_asset_status_test.dart b/mobile/openapi/test/delete_asset_status_test.dart new file mode 100644 index 0000000000..0dfc82ba61 Binary files /dev/null and b/mobile/openapi/test/delete_asset_status_test.dart differ diff --git a/mobile/openapi/test/device_info_api_test.dart b/mobile/openapi/test/device_info_api_test.dart new file mode 100644 index 0000000000..ce297de5da Binary files /dev/null and b/mobile/openapi/test/device_info_api_test.dart differ diff --git a/mobile/openapi/test/device_info_response_dto_test.dart b/mobile/openapi/test/device_info_response_dto_test.dart new file mode 100644 index 0000000000..748f95d09d Binary files /dev/null and b/mobile/openapi/test/device_info_response_dto_test.dart differ diff --git a/mobile/openapi/test/device_type_enum_test.dart b/mobile/openapi/test/device_type_enum_test.dart new file mode 100644 index 0000000000..27b29a53e0 Binary files /dev/null and b/mobile/openapi/test/device_type_enum_test.dart differ diff --git a/mobile/openapi/test/exif_response_dto_test.dart b/mobile/openapi/test/exif_response_dto_test.dart new file mode 100644 index 0000000000..bf5164773d Binary files /dev/null and b/mobile/openapi/test/exif_response_dto_test.dart differ diff --git a/mobile/openapi/test/login_credential_dto_test.dart b/mobile/openapi/test/login_credential_dto_test.dart new file mode 100644 index 0000000000..9995af7304 Binary files /dev/null and b/mobile/openapi/test/login_credential_dto_test.dart differ diff --git a/mobile/openapi/test/login_response_dto_test.dart b/mobile/openapi/test/login_response_dto_test.dart new file mode 100644 index 0000000000..f707799798 Binary files /dev/null and b/mobile/openapi/test/login_response_dto_test.dart differ diff --git a/mobile/openapi/test/remove_assets_dto_test.dart b/mobile/openapi/test/remove_assets_dto_test.dart new file mode 100644 index 0000000000..4e5b48bcbc Binary files /dev/null and b/mobile/openapi/test/remove_assets_dto_test.dart differ diff --git a/mobile/openapi/test/search_asset_dto_test.dart b/mobile/openapi/test/search_asset_dto_test.dart new file mode 100644 index 0000000000..60021265cd Binary files /dev/null and b/mobile/openapi/test/search_asset_dto_test.dart differ diff --git a/mobile/openapi/test/server_info_api_test.dart b/mobile/openapi/test/server_info_api_test.dart new file mode 100644 index 0000000000..540f1280f0 Binary files /dev/null and b/mobile/openapi/test/server_info_api_test.dart differ diff --git a/mobile/openapi/test/server_info_response_dto_test.dart b/mobile/openapi/test/server_info_response_dto_test.dart new file mode 100644 index 0000000000..4deaa09a9e Binary files /dev/null and b/mobile/openapi/test/server_info_response_dto_test.dart differ diff --git a/mobile/openapi/test/server_ping_response_test.dart b/mobile/openapi/test/server_ping_response_test.dart new file mode 100644 index 0000000000..cb72680176 Binary files /dev/null and b/mobile/openapi/test/server_ping_response_test.dart differ diff --git a/mobile/openapi/test/server_version_reponse_dto_test.dart b/mobile/openapi/test/server_version_reponse_dto_test.dart new file mode 100644 index 0000000000..2c71afa994 Binary files /dev/null and b/mobile/openapi/test/server_version_reponse_dto_test.dart differ diff --git a/mobile/openapi/test/sign_up_dto_test.dart b/mobile/openapi/test/sign_up_dto_test.dart new file mode 100644 index 0000000000..4c4d376b82 Binary files /dev/null and b/mobile/openapi/test/sign_up_dto_test.dart differ diff --git a/mobile/openapi/test/smart_info_response_dto_test.dart b/mobile/openapi/test/smart_info_response_dto_test.dart new file mode 100644 index 0000000000..a5e901b7b3 Binary files /dev/null and b/mobile/openapi/test/smart_info_response_dto_test.dart differ diff --git a/mobile/openapi/test/update_album_dto_test.dart b/mobile/openapi/test/update_album_dto_test.dart new file mode 100644 index 0000000000..a03c126564 Binary files /dev/null and b/mobile/openapi/test/update_album_dto_test.dart differ diff --git a/mobile/openapi/test/update_device_info_dto_test.dart b/mobile/openapi/test/update_device_info_dto_test.dart new file mode 100644 index 0000000000..32bb953512 Binary files /dev/null and b/mobile/openapi/test/update_device_info_dto_test.dart differ diff --git a/mobile/openapi/test/update_user_dto_test.dart b/mobile/openapi/test/update_user_dto_test.dart new file mode 100644 index 0000000000..3010100d8e Binary files /dev/null and b/mobile/openapi/test/update_user_dto_test.dart differ diff --git a/mobile/openapi/test/user_api_test.dart b/mobile/openapi/test/user_api_test.dart new file mode 100644 index 0000000000..0d93508eeb Binary files /dev/null and b/mobile/openapi/test/user_api_test.dart differ diff --git a/mobile/openapi/test/user_count_response_dto_test.dart b/mobile/openapi/test/user_count_response_dto_test.dart new file mode 100644 index 0000000000..1f99d9511e Binary files /dev/null and b/mobile/openapi/test/user_count_response_dto_test.dart differ diff --git a/mobile/openapi/test/user_response_dto_test.dart b/mobile/openapi/test/user_response_dto_test.dart new file mode 100644 index 0000000000..861010fac6 Binary files /dev/null and b/mobile/openapi/test/user_response_dto_test.dart differ diff --git a/mobile/openapi/test/validate_access_token_response_dto_test.dart b/mobile/openapi/test/validate_access_token_response_dto_test.dart new file mode 100644 index 0000000000..fff824700c Binary files /dev/null and b/mobile/openapi/test/validate_access_token_response_dto_test.dart differ diff --git a/mobile/pubspec.lock b/mobile/pubspec.lock index 911cc10b19..ec0701fafe 100644 --- a/mobile/pubspec.lock +++ b/mobile/pubspec.lock @@ -637,6 +637,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "1.0.1" + openapi: + dependency: "direct main" + description: + path: openapi + relative: true + source: path + version: "1.0.0" package_config: dependency: transitive description: diff --git a/mobile/pubspec.yaml b/mobile/pubspec.yaml index 6633f1146e..5e284e3e62 100644 --- a/mobile/pubspec.yaml +++ b/mobile/pubspec.yaml @@ -48,6 +48,9 @@ dependencies: collection: ^1.16.0 http_parser: ^4.0.1 + openapi: + path: openapi + dev_dependencies: flutter_test: sdk: flutter @@ -76,3 +79,7 @@ flutter_icons: image_path_ios: "assets/immich-logo-no-outline.png" android: true # can specify file name here e.g. "ic_launcher" ios: true # can specify file name here e.g. "My-Launcher-Icon + +analyzer: + exclude: + - openapi/** diff --git a/server/apps/immich/src/api-v1/asset/asset.controller.ts b/server/apps/immich/src/api-v1/asset/asset.controller.ts index 45b5541496..2a7b30ab53 100644 --- a/server/apps/immich/src/api-v1/asset/asset.controller.ts +++ b/server/apps/immich/src/api-v1/asset/asset.controller.ts @@ -19,11 +19,10 @@ import { } from '@nestjs/common'; import { JwtAuthGuard } from '../../modules/immich-jwt/guards/jwt-auth.guard'; import { AssetService } from './asset.service'; -import { FileFieldsInterceptor, FileInterceptor } from '@nestjs/platform-express'; +import { FileInterceptor } from '@nestjs/platform-express'; import { assetUploadOption } from '../../config/asset-upload.config'; import { AuthUserDto, GetAuthUser } from '../../decorators/auth-user.decorator'; import { ServeFileDto } from './dto/serve-file.dto'; -import { AssetEntity } from '@app/database/entities/asset.entity'; import { Response as Res } from 'express'; import { BackgroundTaskService } from '../../modules/background-task/background-task.service'; import { DeleteAssetDto } from './dto/delete-asset.dto'; @@ -43,6 +42,7 @@ import { CheckDuplicateAssetResponseDto } from './response-dto/check-duplicate-a import { AssetFileUploadDto } from './dto/asset-file-upload.dto'; import { CreateAssetDto } from './dto/create-asset.dto'; import { AssetFileUploadResponseDto } from './response-dto/asset-file-upload-response.dto'; +import { DeleteAssetResponseDto, DeleteAssetStatusEnum } from './response-dto/delete-asset-response.dto'; @UseGuards(JwtAuthGuard) @ApiBearerAuth() @@ -124,7 +124,7 @@ export class AssetController { } @Get('/searchTerm') - async getAssetSearchTerms(@GetAuthUser() authUser: AuthUserDto): Promise { + async getAssetSearchTerms(@GetAuthUser() authUser: AuthUserDto): Promise { return this.assetService.getAssetSearchTerm(authUser); } @@ -164,7 +164,10 @@ export class AssetController { } @Delete('/') - async deleteAsset(@GetAuthUser() authUser: AuthUserDto, @Body(ValidationPipe) assetIds: DeleteAssetDto) { + async deleteAsset( + @GetAuthUser() authUser: AuthUserDto, + @Body(ValidationPipe) assetIds: DeleteAssetDto, + ): Promise { const deleteAssetList: AssetResponseDto[] = []; for (const id of assetIds.ids) { @@ -178,7 +181,7 @@ export class AssetController { const result = await this.assetService.deleteAssetById(authUser, assetIds); result.forEach((res) => { - deleteAssetList.filter((a) => a.id == res.id && res.status == 'success'); + deleteAssetList.filter((a) => a.id == res.id && res.status == DeleteAssetStatusEnum.SUCCESS); }); await this.backgroundTaskService.deleteFileOnDisk(deleteAssetList); diff --git a/server/apps/immich/src/api-v1/asset/asset.service.ts b/server/apps/immich/src/api-v1/asset/asset.service.ts index 647b45a315..e92e3c855b 100644 --- a/server/apps/immich/src/api-v1/asset/asset.service.ts +++ b/server/apps/immich/src/api-v1/asset/asset.service.ts @@ -22,6 +22,7 @@ import { CuratedObjectsResponseDto } from './response-dto/curated-objects-respon import { AssetResponseDto, mapAsset } from './response-dto/asset-response.dto'; import { AssetFileUploadDto } from './dto/asset-file-upload.dto'; import { CreateAssetDto } from './dto/create-asset.dto'; +import { DeleteAssetResponseDto, DeleteAssetStatusEnum } from './response-dto/delete-asset-response.dto'; const fileInfo = promisify(stat); @@ -280,7 +281,7 @@ export class AssetService { return new StreamableFile(fileReadStream); } catch (e) { - Logger.error(`Cannot create read stream for asset ${asset.id}`, 'serveFile[IMAGE]'); + Logger.error(`Cannot create read stream for asset ${asset.id} ${JSON.stringify(e)}`, 'serveFile[IMAGE]'); throw new InternalServerErrorException( e, `Cannot read thumbnail file for asset ${asset.id} - contact your administrator`, @@ -354,8 +355,8 @@ export class AssetService { } } - public async deleteAssetById(authUser: AuthUserDto, assetIds: DeleteAssetDto) { - const result = []; + public async deleteAssetById(authUser: AuthUserDto, assetIds: DeleteAssetDto): Promise { + const result: DeleteAssetResponseDto[] = []; const target = assetIds.ids; for (const assetId of target) { @@ -367,12 +368,12 @@ export class AssetService { if (res.affected) { result.push({ id: assetId, - status: 'success', + status: DeleteAssetStatusEnum.SUCCESS, }); } else { result.push({ id: assetId, - status: 'failed', + status: DeleteAssetStatusEnum.FAILED, }); } } diff --git a/server/apps/immich/src/api-v1/asset/dto/asset-file-upload.dto.ts b/server/apps/immich/src/api-v1/asset/dto/asset-file-upload.dto.ts index ba57ee4df4..25ca5a835f 100644 --- a/server/apps/immich/src/api-v1/asset/dto/asset-file-upload.dto.ts +++ b/server/apps/immich/src/api-v1/asset/dto/asset-file-upload.dto.ts @@ -1,7 +1,5 @@ -import { AssetType } from '@app/database/entities/asset.entity'; import { ApiProperty } from '@nestjs/swagger'; -import { IsEnum, IsNotEmpty, IsOptional } from 'class-validator'; -import { CreateAssetDto } from './create-asset.dto'; +import { IsNotEmpty } from 'class-validator'; export class AssetFileUploadDto { @IsNotEmpty() diff --git a/server/apps/immich/src/api-v1/asset/dto/create-asset.dto.ts b/server/apps/immich/src/api-v1/asset/dto/create-asset.dto.ts index f1194ec4de..7a5bcd3c09 100644 --- a/server/apps/immich/src/api-v1/asset/dto/create-asset.dto.ts +++ b/server/apps/immich/src/api-v1/asset/dto/create-asset.dto.ts @@ -1,5 +1,6 @@ import { IsNotEmpty, IsOptional } from 'class-validator'; import { AssetType } from '@app/database/entities/asset.entity'; +import { ApiProperty } from '@nestjs/swagger'; export class CreateAssetDto { @IsNotEmpty() @@ -9,6 +10,7 @@ export class CreateAssetDto { deviceId!: string; @IsNotEmpty() + @ApiProperty({ enumName: 'AssetTypeEnum', enum: AssetType }) assetType!: AssetType; @IsNotEmpty() diff --git a/server/apps/immich/src/api-v1/asset/dto/get-all-asset-query.dto.ts b/server/apps/immich/src/api-v1/asset/dto/get-all-asset-query.dto.ts deleted file mode 100644 index 67f5e319fa..0000000000 --- a/server/apps/immich/src/api-v1/asset/dto/get-all-asset-query.dto.ts +++ /dev/null @@ -1,6 +0,0 @@ -import { IsOptional } from 'class-validator'; - -export class GetAllAssetQueryDto { - @IsOptional() - nextPageKey?: string; -} diff --git a/server/apps/immich/src/api-v1/asset/dto/get-all-asset-response.dto.ts b/server/apps/immich/src/api-v1/asset/dto/get-all-asset-response.dto.ts deleted file mode 100644 index 5af3e205aa..0000000000 --- a/server/apps/immich/src/api-v1/asset/dto/get-all-asset-response.dto.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { AssetEntity } from '@app/database/entities/asset.entity'; - -// TODO: this doesn't seem to be used -export class GetAllAssetReponseDto { - data!: Array<{ date: string; assets: Array }>; - count!: number; - nextPageKey!: string; -} diff --git a/server/apps/immich/src/api-v1/asset/dto/get-new-asset-query.dto.ts b/server/apps/immich/src/api-v1/asset/dto/get-new-asset-query.dto.ts deleted file mode 100644 index 2d98f0fb59..0000000000 --- a/server/apps/immich/src/api-v1/asset/dto/get-new-asset-query.dto.ts +++ /dev/null @@ -1,6 +0,0 @@ -import { IsNotEmpty } from 'class-validator'; - -export class GetNewAssetQueryDto { - @IsNotEmpty() - latestDate!: string; -} diff --git a/server/apps/immich/src/api-v1/asset/dto/update-asset.dto.ts b/server/apps/immich/src/api-v1/asset/dto/update-asset.dto.ts deleted file mode 100644 index 891e14436b..0000000000 --- a/server/apps/immich/src/api-v1/asset/dto/update-asset.dto.ts +++ /dev/null @@ -1,4 +0,0 @@ -import { PartialType } from '@nestjs/mapped-types'; -import { CreateAssetDto } from './create-asset.dto'; - -export class UpdateAssetDto extends PartialType(CreateAssetDto) {} diff --git a/server/apps/immich/src/api-v1/asset/dto/update-exif.dto.ts b/server/apps/immich/src/api-v1/asset/dto/update-exif.dto.ts deleted file mode 100644 index 1bf5066649..0000000000 --- a/server/apps/immich/src/api-v1/asset/dto/update-exif.dto.ts +++ /dev/null @@ -1,4 +0,0 @@ -import { PartialType } from '@nestjs/mapped-types'; -import { CreateExifDto } from './create-exif.dto'; - -export class UpdateExifDto extends PartialType(CreateExifDto) {} diff --git a/server/apps/immich/src/api-v1/asset/response-dto/asset-response.dto.ts b/server/apps/immich/src/api-v1/asset/response-dto/asset-response.dto.ts index ef74145abf..5e4887fcd9 100644 --- a/server/apps/immich/src/api-v1/asset/response-dto/asset-response.dto.ts +++ b/server/apps/immich/src/api-v1/asset/response-dto/asset-response.dto.ts @@ -1,4 +1,5 @@ import { AssetEntity, AssetType } from '@app/database/entities/asset.entity'; +import { ApiProperty } from '@nestjs/swagger'; import { ExifResponseDto, mapExif } from './exif-response.dto'; import { SmartInfoResponseDto, mapSmartInfo } from './smart-info-response.dto'; @@ -7,6 +8,8 @@ export class AssetResponseDto { deviceAssetId!: string; ownerId!: string; deviceId!: string; + + @ApiProperty({ enumName: 'AssetTypeEnum', enum: AssetType }) type!: AssetType; originalPath!: string; resizePath!: string | null; diff --git a/server/apps/immich/src/api-v1/asset/response-dto/delete-asset-response.dto.ts b/server/apps/immich/src/api-v1/asset/response-dto/delete-asset-response.dto.ts new file mode 100644 index 0000000000..86f6afdf18 --- /dev/null +++ b/server/apps/immich/src/api-v1/asset/response-dto/delete-asset-response.dto.ts @@ -0,0 +1,13 @@ +import { ApiProperty } from '@nestjs/swagger'; + +export enum DeleteAssetStatusEnum { + SUCCESS = 'SUCCESS', + FAILED = 'FAILED', +} + +export class DeleteAssetResponseDto { + id!: string; + + @ApiProperty({ type: 'string', enum: DeleteAssetStatusEnum, enumName: 'DeleteAssetStatus' }) + status!: DeleteAssetStatusEnum; +} diff --git a/server/apps/immich/src/api-v1/asset/response-dto/exif-response.dto.ts b/server/apps/immich/src/api-v1/asset/response-dto/exif-response.dto.ts index 57b0c8e9c0..c43c55b4eb 100644 --- a/server/apps/immich/src/api-v1/asset/response-dto/exif-response.dto.ts +++ b/server/apps/immich/src/api-v1/asset/response-dto/exif-response.dto.ts @@ -1,26 +1,26 @@ import { ExifEntity } from '@app/database/entities/exif.entity'; export class ExifResponseDto { - id!: string; - make: string | null = null; - model: string | null = null; - imageName: string | null = null; - exifImageWidth: number | null = null; - exifImageHeight: number | null = null; - fileSizeInByte: number | null = null; - orientation: string | null = null; - dateTimeOriginal: Date | null = null; - modifyDate: Date | null = null; - lensModel: string | null = null; - fNumber: number | null = null; - focalLength: number | null = null; - iso: number | null = null; - exposureTime: number | null = null; - latitude: number | null = null; - longitude: number | null = null; - city: string | null = null; - state: string | null = null; - country: string | null = null; + id?: string | null = null; + make?: string | null = null; + model?: string | null = null; + imageName?: string | null = null; + exifImageWidth?: number | null = null; + exifImageHeight?: number | null = null; + fileSizeInByte?: number | null = null; + orientation?: string | null = null; + dateTimeOriginal?: Date | null = null; + modifyDate?: Date | null = null; + lensModel?: string | null = null; + fNumber?: number | null = null; + focalLength?: number | null = null; + iso?: number | null = null; + exposureTime?: number | null = null; + latitude?: number | null = null; + longitude?: number | null = null; + city?: string | null = null; + state?: string | null = null; + country?: string | null = null; } export function mapExif(entity: ExifEntity): ExifResponseDto { diff --git a/server/apps/immich/src/api-v1/auth/response-dto/validate-asset-token-response.dto,.ts b/server/apps/immich/src/api-v1/auth/response-dto/validate-asset-token-response.dto,.ts index 151d59e6f1..9d4d770de2 100644 --- a/server/apps/immich/src/api-v1/auth/response-dto/validate-asset-token-response.dto,.ts +++ b/server/apps/immich/src/api-v1/auth/response-dto/validate-asset-token-response.dto,.ts @@ -1,7 +1,10 @@ +import { ApiProperty } from '@nestjs/swagger'; + export class ValidateAccessTokenResponseDto { constructor(authStatus: boolean) { this.authStatus = authStatus; } - authStatus: boolean; + @ApiProperty({ type: 'boolean' }) + authStatus!: boolean; } diff --git a/server/apps/immich/src/api-v1/device-info/dto/create-device-info.dto.ts b/server/apps/immich/src/api-v1/device-info/dto/create-device-info.dto.ts index a9db6ea435..29dddea818 100644 --- a/server/apps/immich/src/api-v1/device-info/dto/create-device-info.dto.ts +++ b/server/apps/immich/src/api-v1/device-info/dto/create-device-info.dto.ts @@ -1,11 +1,13 @@ import { IsNotEmpty, IsOptional } from 'class-validator'; import { DeviceType } from '@app/database/entities/device-info.entity'; +import { ApiProperty } from '@nestjs/swagger'; export class CreateDeviceInfoDto { @IsNotEmpty() deviceId!: string; @IsNotEmpty() + @ApiProperty({ enumName: 'DeviceTypeEnum', enum: DeviceType }) deviceType!: DeviceType; @IsOptional() diff --git a/server/apps/immich/src/api-v1/device-info/dto/update-device-info.dto.ts b/server/apps/immich/src/api-v1/device-info/dto/update-device-info.dto.ts index cd2be701a7..a047e852b5 100644 --- a/server/apps/immich/src/api-v1/device-info/dto/update-device-info.dto.ts +++ b/server/apps/immich/src/api-v1/device-info/dto/update-device-info.dto.ts @@ -1,4 +1,17 @@ +import { DeviceType } from '@app/database/entities/device-info.entity'; import { PartialType } from '@nestjs/mapped-types'; +import { ApiProperty } from '@nestjs/swagger'; +import { IsNotEmpty, IsOptional } from 'class-validator'; import { CreateDeviceInfoDto } from './create-device-info.dto'; -export class UpdateDeviceInfoDto extends PartialType(CreateDeviceInfoDto) {} +export class UpdateDeviceInfoDto { + @IsNotEmpty() + deviceId!: string; + + @IsNotEmpty() + @ApiProperty({ enumName: 'DeviceTypeEnum', enum: DeviceType }) + deviceType!: DeviceType; + + @IsOptional() + isAutoBackup?: boolean; +} diff --git a/server/apps/immich/src/api-v1/device-info/response-dto/create-device-info-response.dto.ts b/server/apps/immich/src/api-v1/device-info/response-dto/create-device-info-response.dto.ts index e36389d332..5c1bcfadf9 100644 --- a/server/apps/immich/src/api-v1/device-info/response-dto/create-device-info-response.dto.ts +++ b/server/apps/immich/src/api-v1/device-info/response-dto/create-device-info-response.dto.ts @@ -1,11 +1,15 @@ import { DeviceInfoEntity, DeviceType } from '@app/database/entities/device-info.entity'; +import { ApiProperty } from '@nestjs/swagger'; export class DeviceInfoResponseDto { + @ApiProperty({ type: 'integer' }) id!: number; userId!: string; deviceId!: string; + + @ApiProperty({ enumName: 'DeviceTypeEnum', enum: DeviceType }) deviceType!: DeviceType; - notificationToken!: string | null; + createdAt!: string; isAutoBackup!: boolean; } @@ -16,7 +20,6 @@ export function mapDeviceInfoResponse(entity: DeviceInfoEntity): DeviceInfoRespo userId: entity.userId, deviceId: entity.deviceId, deviceType: entity.deviceType, - notificationToken: entity.notificationToken, createdAt: entity.createdAt, isAutoBackup: entity.isAutoBackup, }; diff --git a/server/apps/immich/src/api-v1/server-info/response-dto/server-info-response.dto.ts b/server/apps/immich/src/api-v1/server-info/response-dto/server-info-response.dto.ts index e0a5f5c2da..444292091b 100644 --- a/server/apps/immich/src/api-v1/server-info/response-dto/server-info-response.dto.ts +++ b/server/apps/immich/src/api-v1/server-info/response-dto/server-info-response.dto.ts @@ -1,9 +1,19 @@ +import { ApiProperty } from '@nestjs/swagger'; + export class ServerInfoResponseDto { diskSize!: string; diskUse!: string; diskAvailable!: string; + + @ApiProperty({ type: 'integer' }) diskSizeRaw!: number; + + @ApiProperty({ type: 'integer' }) diskUseRaw!: number; + + @ApiProperty({ type: 'integer' }) diskAvailableRaw!: number; + + @ApiProperty({ type: 'number', format: 'float' }) diskUsagePercentage!: number; } diff --git a/server/apps/immich/src/api-v1/server-info/response-dto/server-version-response.dto.ts b/server/apps/immich/src/api-v1/server-info/response-dto/server-version-response.dto.ts index d054d9b3ce..812f3b0714 100644 --- a/server/apps/immich/src/api-v1/server-info/response-dto/server-version-response.dto.ts +++ b/server/apps/immich/src/api-v1/server-info/response-dto/server-version-response.dto.ts @@ -1,8 +1,13 @@ +import { ApiProperty } from '@nestjs/swagger'; import { IServerVersion } from 'apps/immich/src/constants/server_version.constant'; export class ServerVersionReponseDto implements IServerVersion { + @ApiProperty({ type: 'integer' }) major!: number; + @ApiProperty({ type: 'integer' }) minor!: number; + @ApiProperty({ type: 'integer' }) patch!: number; + @ApiProperty({ type: 'integer' }) build!: number; } diff --git a/server/apps/immich/src/api-v1/server-info/server-info.service.ts b/server/apps/immich/src/api-v1/server-info/server-info.service.ts index 606161aa77..23de4123ea 100644 --- a/server/apps/immich/src/api-v1/server-info/server-info.service.ts +++ b/server/apps/immich/src/api-v1/server-info/server-info.service.ts @@ -5,7 +5,7 @@ import { APP_UPLOAD_LOCATION } from '../../constants/upload_location.constant'; @Injectable() export class ServerInfoService { - async getServerInfo() { + async getServerInfo(): Promise { const diskInfo = await diskusage.check(APP_UPLOAD_LOCATION); const usagePercentage = (((diskInfo.total - diskInfo.free) / diskInfo.total) * 100).toFixed(2); diff --git a/server/apps/immich/src/api-v1/user/dto/create-profile-image.dto.ts b/server/apps/immich/src/api-v1/user/dto/create-profile-image.dto.ts index a01f10f466..7b58ba5aa8 100644 --- a/server/apps/immich/src/api-v1/user/dto/create-profile-image.dto.ts +++ b/server/apps/immich/src/api-v1/user/dto/create-profile-image.dto.ts @@ -1,6 +1,7 @@ import { ApiProperty } from '@nestjs/swagger'; +import { Express } from 'express'; export class CreateProfileImageDto { @ApiProperty({ type: 'string', format: 'binary' }) - file: any; + file!: Express.Multer.File; } diff --git a/server/apps/immich/src/api-v1/user/response-dto/user-count-response.dto.ts b/server/apps/immich/src/api-v1/user/response-dto/user-count-response.dto.ts index 63df372350..bcdc3a5b78 100644 --- a/server/apps/immich/src/api-v1/user/response-dto/user-count-response.dto.ts +++ b/server/apps/immich/src/api-v1/user/response-dto/user-count-response.dto.ts @@ -1,5 +1,7 @@ +import { ApiProperty } from '@nestjs/swagger'; export class UserCountResponseDto { + @ApiProperty({ type: 'integer' }) userCount!: number; } @@ -7,4 +9,4 @@ export function mapUserCountResponse(count: number): UserCountResponseDto { return { userCount: count, }; -} \ No newline at end of file +} diff --git a/server/apps/immich/src/api-v1/user/user.controller.ts b/server/apps/immich/src/api-v1/user/user.controller.ts index 6ec543b1fa..3700050a42 100644 --- a/server/apps/immich/src/api-v1/user/user.controller.ts +++ b/server/apps/immich/src/api-v1/user/user.controller.ts @@ -11,6 +11,7 @@ import { UseInterceptors, UploadedFile, Response, + Request, StreamableFile, ParseBoolPipe, } from '@nestjs/common'; @@ -22,7 +23,7 @@ import { AdminRolesGuard } from '../../middlewares/admin-role-guard.middleware'; import { UpdateUserDto } from './dto/update-user.dto'; import { FileInterceptor } from '@nestjs/platform-express'; import { profileImageUploadOption } from '../../config/profile-image-upload.config'; -import { Response as Res } from 'express'; +import { Response as Res, Request as Req } from 'express'; import { ApiBearerAuth, ApiBody, ApiConsumes, ApiTags } from '@nestjs/swagger'; import { UserResponseDto } from './response-dto/user-response.dto'; import { UserCountResponseDto } from './response-dto/user-count-response.dto'; @@ -76,13 +77,16 @@ export class UserController { @ApiBearerAuth() @ApiConsumes('multipart/form-data') @ApiBody({ + description: 'A new avatar for the user', type: CreateProfileImageDto, }) @Post('/profile-image') async createProfileImage( @GetAuthUser() authUser: AuthUserDto, @UploadedFile() fileInfo: Express.Multer.File, + @Request() req: Req, ): Promise { + console.log(req.body, req.file); return await this.userService.createProfileImage(authUser, fileInfo); } diff --git a/server/immich-openapi-specs.json b/server/immich-openapi-specs.json index fadc2b713b..a70db8f2c1 100644 --- a/server/immich-openapi-specs.json +++ b/server/immich-openapi-specs.json @@ -1 +1 @@ -{"openapi":"3.0.0","paths":{"/user":{"get":{"operationId":"getAllUsers","parameters":[{"name":"isAll","required":true,"in":"query","schema":{"type":"boolean"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/UserResponseDto"}}}}}},"tags":["User"],"security":[{"bearer":[]}]},"post":{"operationId":"createUser","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateUserDto"}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserResponseDto"}}}}},"tags":["User"],"security":[{"bearer":[]}]},"put":{"operationId":"updateUser","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateUserDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserResponseDto"}}}}},"tags":["User"],"security":[{"bearer":[]}]}},"/user/me":{"get":{"operationId":"getMyUserInfo","parameters":[],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserResponseDto"}}}}},"tags":["User"],"security":[{"bearer":[]}]}},"/user/count":{"get":{"operationId":"getUserCount","parameters":[],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserCountResponseDto"}}}}},"tags":["User"]}},"/user/profile-image":{"post":{"operationId":"createProfileImage","parameters":[],"requestBody":{"required":true,"content":{"multipart/form-data":{"schema":{"$ref":"#/components/schemas/CreateProfileImageDto"}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateProfileImageResponseDto"}}}}},"tags":["User"],"security":[{"bearer":[]}]}},"/user/profile-image/{userId}":{"get":{"operationId":"getProfileImage","parameters":[{"name":"userId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object"}}}}},"tags":["User"]}},"/asset/upload":{"post":{"operationId":"uploadFile","parameters":[],"requestBody":{"required":true,"description":"Asset Upload Information","content":{"multipart/form-data":{"schema":{"$ref":"#/components/schemas/AssetFileUploadDto"}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AssetFileUploadResponseDto"}}}}},"tags":["Asset"],"security":[{"bearer":[]}]}},"/asset/download":{"get":{"operationId":"downloadFile","parameters":[{"name":"aid","required":true,"in":"query","schema":{"title":"Device Asset ID","type":"string"}},{"name":"did","required":true,"in":"query","schema":{"title":"Device ID","type":"string"}},{"name":"isThumb","required":false,"in":"query","schema":{"title":"Is serve thumbnail (resize) file","type":"boolean"}},{"name":"isWeb","required":false,"in":"query","schema":{"title":"Is request made from web","type":"boolean"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object"}}}}},"tags":["Asset"],"security":[{"bearer":[]}]}},"/asset/file":{"get":{"operationId":"serveFile","parameters":[{"name":"aid","required":true,"in":"query","schema":{"title":"Device Asset ID","type":"string"}},{"name":"did","required":true,"in":"query","schema":{"title":"Device ID","type":"string"}},{"name":"isThumb","required":false,"in":"query","schema":{"title":"Is serve thumbnail (resize) file","type":"boolean"}},{"name":"isWeb","required":false,"in":"query","schema":{"title":"Is request made from web","type":"boolean"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object"}}}}},"tags":["Asset"],"security":[{"bearer":[]}]}},"/asset/thumbnail/{assetId}":{"get":{"operationId":"getAssetThumbnail","parameters":[{"name":"assetId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object"}}}}},"tags":["Asset"],"security":[{"bearer":[]}]}},"/asset/allObjects":{"get":{"operationId":"getCuratedObjects","parameters":[],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/CuratedObjectsResponseDto"}}}}}},"tags":["Asset"],"security":[{"bearer":[]}]}},"/asset/allLocation":{"get":{"operationId":"getCuratedLocations","parameters":[],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/CuratedLocationsResponseDto"}}}}}},"tags":["Asset"],"security":[{"bearer":[]}]}},"/asset/searchTerm":{"get":{"operationId":"getAssetSearchTerms","parameters":[],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"array","items":{"type":"object"}}}}}},"tags":["Asset"],"security":[{"bearer":[]}]}},"/asset/search":{"post":{"operationId":"searchAsset","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SearchAssetDto"}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/AssetResponseDto"}}}}}},"tags":["Asset"],"security":[{"bearer":[]}]}},"/asset":{"get":{"operationId":"getAllAssets","summary":"","description":"Get all AssetEntity belong to the user","parameters":[],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/AssetResponseDto"}}}}}},"tags":["Asset"],"security":[{"bearer":[]}]},"delete":{"operationId":"deleteAsset","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DeleteAssetDto"}}}},"responses":{"200":{"description":""}},"tags":["Asset"],"security":[{"bearer":[]}]}},"/asset/{deviceId}":{"get":{"operationId":"getUserAssetsByDeviceId","summary":"","description":"Get all asset of a device that are in the database, ID only.","parameters":[{"name":"deviceId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"}}}}}},"tags":["Asset"],"security":[{"bearer":[]}]}},"/asset/assetById/{assetId}":{"get":{"operationId":"getAssetById","summary":"","description":"Get a single asset's information","parameters":[{"name":"assetId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AssetResponseDto"}}}}},"tags":["Asset"],"security":[{"bearer":[]}]}},"/asset/check":{"post":{"operationId":"checkDuplicateAsset","summary":"","description":"Check duplicated asset before uploading - for Web upload used","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CheckDuplicateAssetDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CheckDuplicateAssetResponseDto"}}}}},"tags":["Asset"],"security":[{"bearer":[]}]}},"/auth/login":{"post":{"operationId":"login","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/LoginCredentialDto"}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/LoginResponseDto"}}}}},"tags":["Authentication"]}},"/auth/admin-sign-up":{"post":{"operationId":"adminSignUp","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SignUpDto"}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AdminSignupResponseDto"}}}},"400":{"description":"The server already has an admin"}},"tags":["Authentication"]}},"/auth/validateToken":{"post":{"operationId":"validateAccessToken","parameters":[],"responses":{"201":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidateAccessTokenResponseDto"}}}}},"tags":["Authentication"],"security":[{"bearer":[]}]}},"/device-info":{"post":{"operationId":"createDeviceInfo","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateDeviceInfoDto"}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DeviceInfoResponseDto"}}}}},"tags":["Device Info"],"security":[{"bearer":[]}]},"patch":{"operationId":"updateDeviceInfo","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateDeviceInfoDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DeviceInfoResponseDto"}}}}},"tags":["Device Info"],"security":[{"bearer":[]}]}},"/server-info":{"get":{"operationId":"getServerInfo","parameters":[],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ServerInfoResponseDto"}}}}},"tags":["Server Info"]}},"/server-info/ping":{"get":{"operationId":"pingServer","parameters":[],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ServerPingResponse"}}}}},"tags":["Server Info"]}},"/server-info/version":{"get":{"operationId":"getServerVersion","parameters":[],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ServerVersionReponseDto"}}}}},"tags":["Server Info"]}},"/album":{"post":{"operationId":"createAlbum","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateAlbumDto"}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AlbumResponseDto"}}}}},"tags":["Album"],"security":[{"bearer":[]}]},"get":{"operationId":"getAllAlbums","parameters":[{"name":"shared","required":false,"in":"query","schema":{"type":"boolean"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/AlbumResponseDto"}}}}}},"tags":["Album"],"security":[{"bearer":[]}]}},"/album/{albumId}/users":{"put":{"operationId":"addUsersToAlbum","parameters":[{"name":"albumId","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AddUsersDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AlbumResponseDto"}}}}},"tags":["Album"],"security":[{"bearer":[]}]}},"/album/{albumId}/assets":{"put":{"operationId":"addAssetsToAlbum","parameters":[{"name":"albumId","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AddAssetsDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AlbumResponseDto"}}}}},"tags":["Album"],"security":[{"bearer":[]}]},"delete":{"operationId":"removeAssetFromAlbum","parameters":[{"name":"albumId","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RemoveAssetsDto"}}}},"responses":{"200":{"description":""}},"tags":["Album"],"security":[{"bearer":[]}]}},"/album/{albumId}":{"get":{"operationId":"getAlbumInfo","parameters":[{"name":"albumId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AlbumResponseDto"}}}}},"tags":["Album"],"security":[{"bearer":[]}]},"delete":{"operationId":"deleteAlbum","parameters":[{"name":"albumId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["Album"],"security":[{"bearer":[]}]},"patch":{"operationId":"updateAlbumInfo","parameters":[{"name":"albumId","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateAlbumDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AlbumResponseDto"}}}}},"tags":["Album"],"security":[{"bearer":[]}]}},"/album/{albumId}/user/{userId}":{"delete":{"operationId":"removeUserFromAlbum","parameters":[{"name":"albumId","required":true,"in":"path","schema":{"type":"string"}},{"name":"userId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["Album"],"security":[{"bearer":[]}]}}},"info":{"title":"Immich","description":"Immich API","version":"1.17.0","contact":{}},"tags":[],"servers":[{"url":"/api"}],"components":{"securitySchemes":{"bearer":{"scheme":"bearer","bearerFormat":"JWT","type":"http","name":"JWT","description":"Enter JWT token","in":"header"}},"schemas":{"UserResponseDto":{"type":"object","properties":{"id":{"type":"string"},"email":{"type":"string"},"firstName":{"type":"string"},"lastName":{"type":"string"},"createdAt":{"type":"string"},"profileImagePath":{"type":"string"},"shouldChangePassword":{"type":"boolean"},"isAdmin":{"type":"boolean"}},"required":["id","email","firstName","lastName","createdAt","profileImagePath","shouldChangePassword","isAdmin"]},"CreateUserDto":{"type":"object","properties":{"email":{"type":"string","example":"testuser@email.com"},"password":{"type":"string","example":"password"},"firstName":{"type":"string","example":"John"},"lastName":{"type":"string","example":"Doe"}},"required":["email","password","firstName","lastName"]},"UserCountResponseDto":{"type":"object","properties":{"userCount":{"type":"number"}},"required":["userCount"]},"UpdateUserDto":{"type":"object","properties":{"id":{"type":"string"},"password":{"type":"string"},"firstName":{"type":"string"},"lastName":{"type":"string"},"isAdmin":{"type":"boolean"},"shouldChangePassword":{"type":"boolean"},"profileImagePath":{"type":"string"}},"required":["id"]},"CreateProfileImageDto":{"type":"object","properties":{"file":{"type":"string","format":"binary"}},"required":["file"]},"CreateProfileImageResponseDto":{"type":"object","properties":{"userId":{"type":"string"},"profileImagePath":{"type":"string"}},"required":["userId","profileImagePath"]},"AssetFileUploadDto":{"type":"object","properties":{"assetData":{"type":"string","format":"binary"}},"required":["assetData"]},"AssetFileUploadResponseDto":{"type":"object","properties":{"id":{"type":"string"}},"required":["id"]},"CuratedObjectsResponseDto":{"type":"object","properties":{"id":{"type":"string"},"object":{"type":"string"},"resizePath":{"type":"string"},"deviceAssetId":{"type":"string"},"deviceId":{"type":"string"}},"required":["id","object","resizePath","deviceAssetId","deviceId"]},"CuratedLocationsResponseDto":{"type":"object","properties":{"id":{"type":"string"},"city":{"type":"string"},"resizePath":{"type":"string"},"deviceAssetId":{"type":"string"},"deviceId":{"type":"string"}},"required":["id","city","resizePath","deviceAssetId","deviceId"]},"SearchAssetDto":{"type":"object","properties":{"searchTerm":{"type":"string"}},"required":["searchTerm"]},"ExifResponseDto":{"type":"object","properties":{"id":{"type":"string"},"make":{"type":"string","nullable":true,"default":null},"model":{"type":"string","nullable":true,"default":null},"imageName":{"type":"string","nullable":true,"default":null},"exifImageWidth":{"type":"number","nullable":true,"default":null},"exifImageHeight":{"type":"number","nullable":true,"default":null},"fileSizeInByte":{"type":"number","nullable":true,"default":null},"orientation":{"type":"string","nullable":true,"default":null},"dateTimeOriginal":{"format":"date-time","type":"string","nullable":true,"default":null},"modifyDate":{"format":"date-time","type":"string","nullable":true,"default":null},"lensModel":{"type":"string","nullable":true,"default":null},"fNumber":{"type":"number","nullable":true,"default":null},"focalLength":{"type":"number","nullable":true,"default":null},"iso":{"type":"number","nullable":true,"default":null},"exposureTime":{"type":"number","nullable":true,"default":null},"latitude":{"type":"number","nullable":true,"default":null},"longitude":{"type":"number","nullable":true,"default":null},"city":{"type":"string","nullable":true,"default":null},"state":{"type":"string","nullable":true,"default":null},"country":{"type":"string","nullable":true,"default":null}},"required":["id","make","model","imageName","exifImageWidth","exifImageHeight","fileSizeInByte","orientation","dateTimeOriginal","modifyDate","lensModel","fNumber","focalLength","iso","exposureTime","latitude","longitude","city","state","country"]},"SmartInfoResponseDto":{"type":"object","properties":{"id":{"type":"string"},"tags":{"nullable":true,"type":"array","items":{"type":"string"}},"objects":{"nullable":true,"type":"array","items":{"type":"string"}}}},"AssetResponseDto":{"type":"object","properties":{"id":{"type":"string"},"deviceAssetId":{"type":"string"},"ownerId":{"type":"string"},"deviceId":{"type":"string"},"type":{"enum":["IMAGE","VIDEO","AUDIO","OTHER"],"type":"string"},"originalPath":{"type":"string"},"resizePath":{"type":"string","nullable":true},"createdAt":{"type":"string"},"modifiedAt":{"type":"string"},"isFavorite":{"type":"boolean"},"mimeType":{"type":"string","nullable":true},"duration":{"type":"string"},"webpPath":{"type":"string","nullable":true},"encodedVideoPath":{"type":"string","nullable":true},"exifInfo":{"$ref":"#/components/schemas/ExifResponseDto"},"smartInfo":{"$ref":"#/components/schemas/SmartInfoResponseDto"}},"required":["id","deviceAssetId","ownerId","deviceId","type","originalPath","resizePath","createdAt","modifiedAt","isFavorite","mimeType","duration","webpPath","encodedVideoPath"]},"DeleteAssetDto":{"type":"object","properties":{"ids":{"title":"Array of asset IDs to delete","example":["bf973405-3f2a-48d2-a687-2ed4167164be","dd41870b-5d00-46d2-924e-1d8489a0aa0f","fad77c3f-deef-4e7e-9608-14c1aa4e559a"],"type":"array","items":{"type":"string"}}},"required":["ids"]},"CheckDuplicateAssetDto":{"type":"object","properties":{"deviceAssetId":{"type":"string"},"deviceId":{"type":"string"}},"required":["deviceAssetId","deviceId"]},"CheckDuplicateAssetResponseDto":{"type":"object","properties":{"isExist":{"type":"boolean"}},"required":["isExist"]},"LoginCredentialDto":{"type":"object","properties":{"email":{"type":"string","example":"testuser@email.com"},"password":{"type":"string","example":"password"}},"required":["email","password"]},"LoginResponseDto":{"type":"object","properties":{"accessToken":{"type":"string","readOnly":true},"userId":{"type":"string","readOnly":true},"userEmail":{"type":"string","readOnly":true},"firstName":{"type":"string","readOnly":true},"lastName":{"type":"string","readOnly":true},"profileImagePath":{"type":"string","readOnly":true},"isAdmin":{"type":"boolean","readOnly":true},"shouldChangePassword":{"type":"boolean","readOnly":true}},"required":["accessToken","userId","userEmail","firstName","lastName","profileImagePath","isAdmin","shouldChangePassword"]},"SignUpDto":{"type":"object","properties":{"email":{"type":"string","example":"testuser@email.com"},"password":{"type":"string","example":"password"},"firstName":{"type":"string","example":"Admin"},"lastName":{"type":"string","example":"Doe"}},"required":["email","password","firstName","lastName"]},"AdminSignupResponseDto":{"type":"object","properties":{"id":{"type":"string"},"email":{"type":"string"},"firstName":{"type":"string"},"lastName":{"type":"string"},"createdAt":{"type":"string"}},"required":["id","email","firstName","lastName","createdAt"]},"ValidateAccessTokenResponseDto":{"type":"object","properties":{}},"CreateDeviceInfoDto":{"type":"object","properties":{"deviceId":{"type":"string"},"deviceType":{"type":"string","enum":["IOS","ANDROID","WEB"]},"isAutoBackup":{"type":"boolean"}},"required":["deviceId","deviceType"]},"DeviceInfoResponseDto":{"type":"object","properties":{"id":{"type":"number"},"userId":{"type":"string"},"deviceId":{"type":"string"},"deviceType":{"enum":["IOS","ANDROID","WEB"],"type":"string"},"notificationToken":{"type":"string","nullable":true},"createdAt":{"type":"string"},"isAutoBackup":{"type":"boolean"}},"required":["id","userId","deviceId","deviceType","notificationToken","createdAt","isAutoBackup"]},"UpdateDeviceInfoDto":{"type":"object","properties":{}},"ServerInfoResponseDto":{"type":"object","properties":{"diskSize":{"type":"string"},"diskUse":{"type":"string"},"diskAvailable":{"type":"string"},"diskSizeRaw":{"type":"number"},"diskUseRaw":{"type":"number"},"diskAvailableRaw":{"type":"number"},"diskUsagePercentage":{"type":"number"}},"required":["diskSize","diskUse","diskAvailable","diskSizeRaw","diskUseRaw","diskAvailableRaw","diskUsagePercentage"]},"ServerPingResponse":{"type":"object","properties":{"res":{"type":"string","readOnly":true,"example":"pong"}},"required":["res"]},"ServerVersionReponseDto":{"type":"object","properties":{"major":{"type":"number"},"minor":{"type":"number"},"patch":{"type":"number"},"build":{"type":"number"}},"required":["major","minor","patch","build"]},"CreateAlbumDto":{"type":"object","properties":{"albumName":{"type":"string"},"sharedWithUserIds":{"type":"array","items":{"type":"string"}},"assetIds":{"type":"array","items":{"type":"string"}}},"required":["albumName"]},"AlbumResponseDto":{"type":"object","properties":{"id":{"type":"string"},"ownerId":{"type":"string"},"albumName":{"type":"string"},"createdAt":{"type":"string"},"albumThumbnailAssetId":{"type":"string","nullable":true},"shared":{"type":"boolean"},"sharedUsers":{"type":"array","items":{"$ref":"#/components/schemas/UserResponseDto"}},"assets":{"type":"array","items":{"$ref":"#/components/schemas/AssetResponseDto"}}},"required":["id","ownerId","albumName","createdAt","albumThumbnailAssetId","shared","sharedUsers","assets"]},"AddUsersDto":{"type":"object","properties":{"sharedUserIds":{"type":"array","items":{"type":"string"}}},"required":["sharedUserIds"]},"AddAssetsDto":{"type":"object","properties":{"assetIds":{"type":"array","items":{"type":"string"}}},"required":["assetIds"]},"RemoveAssetsDto":{"type":"object","properties":{"assetIds":{"type":"array","items":{"type":"string"}}},"required":["assetIds"]},"UpdateAlbumDto":{"type":"object","properties":{"albumName":{"type":"string"},"ownerId":{"type":"string"}},"required":["albumName","ownerId"]}}}} \ No newline at end of file +{"openapi":"3.0.0","paths":{"/user":{"get":{"operationId":"getAllUsers","parameters":[{"name":"isAll","required":true,"in":"query","schema":{"type":"boolean"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/UserResponseDto"}}}}}},"tags":["User"],"security":[{"bearer":[]}]},"post":{"operationId":"createUser","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateUserDto"}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserResponseDto"}}}}},"tags":["User"],"security":[{"bearer":[]}]},"put":{"operationId":"updateUser","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateUserDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserResponseDto"}}}}},"tags":["User"],"security":[{"bearer":[]}]}},"/user/me":{"get":{"operationId":"getMyUserInfo","parameters":[],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserResponseDto"}}}}},"tags":["User"],"security":[{"bearer":[]}]}},"/user/count":{"get":{"operationId":"getUserCount","parameters":[],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserCountResponseDto"}}}}},"tags":["User"]}},"/user/profile-image":{"post":{"operationId":"createProfileImage","parameters":[],"requestBody":{"required":true,"description":"A new avatar for the user","content":{"multipart/form-data":{"schema":{"$ref":"#/components/schemas/CreateProfileImageDto"}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateProfileImageResponseDto"}}}}},"tags":["User"],"security":[{"bearer":[]}]}},"/user/profile-image/{userId}":{"get":{"operationId":"getProfileImage","parameters":[{"name":"userId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object"}}}}},"tags":["User"]}},"/asset/upload":{"post":{"operationId":"uploadFile","parameters":[],"requestBody":{"required":true,"description":"Asset Upload Information","content":{"multipart/form-data":{"schema":{"$ref":"#/components/schemas/AssetFileUploadDto"}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AssetFileUploadResponseDto"}}}}},"tags":["Asset"],"security":[{"bearer":[]}]}},"/asset/download":{"get":{"operationId":"downloadFile","parameters":[{"name":"aid","required":true,"in":"query","schema":{"title":"Device Asset ID","type":"string"}},{"name":"did","required":true,"in":"query","schema":{"title":"Device ID","type":"string"}},{"name":"isThumb","required":false,"in":"query","schema":{"title":"Is serve thumbnail (resize) file","type":"boolean"}},{"name":"isWeb","required":false,"in":"query","schema":{"title":"Is request made from web","type":"boolean"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object"}}}}},"tags":["Asset"],"security":[{"bearer":[]}]}},"/asset/file":{"get":{"operationId":"serveFile","parameters":[{"name":"aid","required":true,"in":"query","schema":{"title":"Device Asset ID","type":"string"}},{"name":"did","required":true,"in":"query","schema":{"title":"Device ID","type":"string"}},{"name":"isThumb","required":false,"in":"query","schema":{"title":"Is serve thumbnail (resize) file","type":"boolean"}},{"name":"isWeb","required":false,"in":"query","schema":{"title":"Is request made from web","type":"boolean"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object"}}}}},"tags":["Asset"],"security":[{"bearer":[]}]}},"/asset/thumbnail/{assetId}":{"get":{"operationId":"getAssetThumbnail","parameters":[{"name":"assetId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object"}}}}},"tags":["Asset"],"security":[{"bearer":[]}]}},"/asset/allObjects":{"get":{"operationId":"getCuratedObjects","parameters":[],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/CuratedObjectsResponseDto"}}}}}},"tags":["Asset"],"security":[{"bearer":[]}]}},"/asset/allLocation":{"get":{"operationId":"getCuratedLocations","parameters":[],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/CuratedLocationsResponseDto"}}}}}},"tags":["Asset"],"security":[{"bearer":[]}]}},"/asset/searchTerm":{"get":{"operationId":"getAssetSearchTerms","parameters":[],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"}}}}}},"tags":["Asset"],"security":[{"bearer":[]}]}},"/asset/search":{"post":{"operationId":"searchAsset","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SearchAssetDto"}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/AssetResponseDto"}}}}}},"tags":["Asset"],"security":[{"bearer":[]}]}},"/asset":{"get":{"operationId":"getAllAssets","summary":"","description":"Get all AssetEntity belong to the user","parameters":[],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/AssetResponseDto"}}}}}},"tags":["Asset"],"security":[{"bearer":[]}]},"delete":{"operationId":"deleteAsset","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DeleteAssetDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/DeleteAssetResponseDto"}}}}}},"tags":["Asset"],"security":[{"bearer":[]}]}},"/asset/{deviceId}":{"get":{"operationId":"getUserAssetsByDeviceId","summary":"","description":"Get all asset of a device that are in the database, ID only.","parameters":[{"name":"deviceId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"}}}}}},"tags":["Asset"],"security":[{"bearer":[]}]}},"/asset/assetById/{assetId}":{"get":{"operationId":"getAssetById","summary":"","description":"Get a single asset's information","parameters":[{"name":"assetId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AssetResponseDto"}}}}},"tags":["Asset"],"security":[{"bearer":[]}]}},"/asset/check":{"post":{"operationId":"checkDuplicateAsset","summary":"","description":"Check duplicated asset before uploading - for Web upload used","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CheckDuplicateAssetDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CheckDuplicateAssetResponseDto"}}}}},"tags":["Asset"],"security":[{"bearer":[]}]}},"/auth/login":{"post":{"operationId":"login","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/LoginCredentialDto"}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/LoginResponseDto"}}}}},"tags":["Authentication"]}},"/auth/admin-sign-up":{"post":{"operationId":"adminSignUp","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SignUpDto"}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AdminSignupResponseDto"}}}},"400":{"description":"The server already has an admin"}},"tags":["Authentication"]}},"/auth/validateToken":{"post":{"operationId":"validateAccessToken","parameters":[],"responses":{"201":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidateAccessTokenResponseDto"}}}}},"tags":["Authentication"],"security":[{"bearer":[]}]}},"/device-info":{"post":{"operationId":"createDeviceInfo","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateDeviceInfoDto"}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DeviceInfoResponseDto"}}}}},"tags":["Device Info"],"security":[{"bearer":[]}]},"patch":{"operationId":"updateDeviceInfo","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateDeviceInfoDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DeviceInfoResponseDto"}}}}},"tags":["Device Info"],"security":[{"bearer":[]}]}},"/server-info":{"get":{"operationId":"getServerInfo","parameters":[],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ServerInfoResponseDto"}}}}},"tags":["Server Info"]}},"/server-info/ping":{"get":{"operationId":"pingServer","parameters":[],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ServerPingResponse"}}}}},"tags":["Server Info"]}},"/server-info/version":{"get":{"operationId":"getServerVersion","parameters":[],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ServerVersionReponseDto"}}}}},"tags":["Server Info"]}},"/album":{"post":{"operationId":"createAlbum","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateAlbumDto"}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AlbumResponseDto"}}}}},"tags":["Album"],"security":[{"bearer":[]}]},"get":{"operationId":"getAllAlbums","parameters":[{"name":"shared","required":false,"in":"query","schema":{"type":"boolean"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/AlbumResponseDto"}}}}}},"tags":["Album"],"security":[{"bearer":[]}]}},"/album/{albumId}/users":{"put":{"operationId":"addUsersToAlbum","parameters":[{"name":"albumId","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AddUsersDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AlbumResponseDto"}}}}},"tags":["Album"],"security":[{"bearer":[]}]}},"/album/{albumId}/assets":{"put":{"operationId":"addAssetsToAlbum","parameters":[{"name":"albumId","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AddAssetsDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AlbumResponseDto"}}}}},"tags":["Album"],"security":[{"bearer":[]}]},"delete":{"operationId":"removeAssetFromAlbum","parameters":[{"name":"albumId","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RemoveAssetsDto"}}}},"responses":{"200":{"description":""}},"tags":["Album"],"security":[{"bearer":[]}]}},"/album/{albumId}":{"get":{"operationId":"getAlbumInfo","parameters":[{"name":"albumId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AlbumResponseDto"}}}}},"tags":["Album"],"security":[{"bearer":[]}]},"delete":{"operationId":"deleteAlbum","parameters":[{"name":"albumId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["Album"],"security":[{"bearer":[]}]},"patch":{"operationId":"updateAlbumInfo","parameters":[{"name":"albumId","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateAlbumDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AlbumResponseDto"}}}}},"tags":["Album"],"security":[{"bearer":[]}]}},"/album/{albumId}/user/{userId}":{"delete":{"operationId":"removeUserFromAlbum","parameters":[{"name":"albumId","required":true,"in":"path","schema":{"type":"string"}},{"name":"userId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["Album"],"security":[{"bearer":[]}]}}},"info":{"title":"Immich","description":"Immich API","version":"1.17.0","contact":{}},"tags":[],"servers":[{"url":"/api"}],"components":{"securitySchemes":{"bearer":{"scheme":"bearer","bearerFormat":"JWT","type":"http","name":"JWT","description":"Enter JWT token","in":"header"}},"schemas":{"UserResponseDto":{"type":"object","properties":{"id":{"type":"string"},"email":{"type":"string"},"firstName":{"type":"string"},"lastName":{"type":"string"},"createdAt":{"type":"string"},"profileImagePath":{"type":"string"},"shouldChangePassword":{"type":"boolean"},"isAdmin":{"type":"boolean"}},"required":["id","email","firstName","lastName","createdAt","profileImagePath","shouldChangePassword","isAdmin"]},"CreateUserDto":{"type":"object","properties":{"email":{"type":"string","example":"testuser@email.com"},"password":{"type":"string","example":"password"},"firstName":{"type":"string","example":"John"},"lastName":{"type":"string","example":"Doe"}},"required":["email","password","firstName","lastName"]},"UserCountResponseDto":{"type":"object","properties":{"userCount":{"type":"integer"}},"required":["userCount"]},"UpdateUserDto":{"type":"object","properties":{"id":{"type":"string"},"password":{"type":"string"},"firstName":{"type":"string"},"lastName":{"type":"string"},"isAdmin":{"type":"boolean"},"shouldChangePassword":{"type":"boolean"},"profileImagePath":{"type":"string"}},"required":["id"]},"CreateProfileImageDto":{"type":"object","properties":{"file":{"type":"string","format":"binary"}},"required":["file"]},"CreateProfileImageResponseDto":{"type":"object","properties":{"userId":{"type":"string"},"profileImagePath":{"type":"string"}},"required":["userId","profileImagePath"]},"AssetFileUploadDto":{"type":"object","properties":{"assetData":{"type":"string","format":"binary"}},"required":["assetData"]},"AssetFileUploadResponseDto":{"type":"object","properties":{"id":{"type":"string"}},"required":["id"]},"CuratedObjectsResponseDto":{"type":"object","properties":{"id":{"type":"string"},"object":{"type":"string"},"resizePath":{"type":"string"},"deviceAssetId":{"type":"string"},"deviceId":{"type":"string"}},"required":["id","object","resizePath","deviceAssetId","deviceId"]},"CuratedLocationsResponseDto":{"type":"object","properties":{"id":{"type":"string"},"city":{"type":"string"},"resizePath":{"type":"string"},"deviceAssetId":{"type":"string"},"deviceId":{"type":"string"}},"required":["id","city","resizePath","deviceAssetId","deviceId"]},"SearchAssetDto":{"type":"object","properties":{"searchTerm":{"type":"string"}},"required":["searchTerm"]},"AssetTypeEnum":{"type":"string","enum":["IMAGE","VIDEO","AUDIO","OTHER"]},"ExifResponseDto":{"type":"object","properties":{"id":{"type":"string","nullable":true,"default":null},"make":{"type":"string","nullable":true,"default":null},"model":{"type":"string","nullable":true,"default":null},"imageName":{"type":"string","nullable":true,"default":null},"exifImageWidth":{"type":"number","nullable":true,"default":null},"exifImageHeight":{"type":"number","nullable":true,"default":null},"fileSizeInByte":{"type":"number","nullable":true,"default":null},"orientation":{"type":"string","nullable":true,"default":null},"dateTimeOriginal":{"format":"date-time","type":"string","nullable":true,"default":null},"modifyDate":{"format":"date-time","type":"string","nullable":true,"default":null},"lensModel":{"type":"string","nullable":true,"default":null},"fNumber":{"type":"number","nullable":true,"default":null},"focalLength":{"type":"number","nullable":true,"default":null},"iso":{"type":"number","nullable":true,"default":null},"exposureTime":{"type":"number","nullable":true,"default":null},"latitude":{"type":"number","nullable":true,"default":null},"longitude":{"type":"number","nullable":true,"default":null},"city":{"type":"string","nullable":true,"default":null},"state":{"type":"string","nullable":true,"default":null},"country":{"type":"string","nullable":true,"default":null}}},"SmartInfoResponseDto":{"type":"object","properties":{"id":{"type":"string"},"tags":{"nullable":true,"type":"array","items":{"type":"string"}},"objects":{"nullable":true,"type":"array","items":{"type":"string"}}}},"AssetResponseDto":{"type":"object","properties":{"type":{"$ref":"#/components/schemas/AssetTypeEnum"},"id":{"type":"string"},"deviceAssetId":{"type":"string"},"ownerId":{"type":"string"},"deviceId":{"type":"string"},"originalPath":{"type":"string"},"resizePath":{"type":"string","nullable":true},"createdAt":{"type":"string"},"modifiedAt":{"type":"string"},"isFavorite":{"type":"boolean"},"mimeType":{"type":"string","nullable":true},"duration":{"type":"string"},"webpPath":{"type":"string","nullable":true},"encodedVideoPath":{"type":"string","nullable":true},"exifInfo":{"$ref":"#/components/schemas/ExifResponseDto"},"smartInfo":{"$ref":"#/components/schemas/SmartInfoResponseDto"}},"required":["type","id","deviceAssetId","ownerId","deviceId","originalPath","resizePath","createdAt","modifiedAt","isFavorite","mimeType","duration","webpPath","encodedVideoPath"]},"DeleteAssetDto":{"type":"object","properties":{"ids":{"title":"Array of asset IDs to delete","example":["bf973405-3f2a-48d2-a687-2ed4167164be","dd41870b-5d00-46d2-924e-1d8489a0aa0f","fad77c3f-deef-4e7e-9608-14c1aa4e559a"],"type":"array","items":{"type":"string"}}},"required":["ids"]},"DeleteAssetStatus":{"type":"string","enum":["SUCCESS","FAILED"]},"DeleteAssetResponseDto":{"type":"object","properties":{"status":{"$ref":"#/components/schemas/DeleteAssetStatus"},"id":{"type":"string"}},"required":["status","id"]},"CheckDuplicateAssetDto":{"type":"object","properties":{"deviceAssetId":{"type":"string"},"deviceId":{"type":"string"}},"required":["deviceAssetId","deviceId"]},"CheckDuplicateAssetResponseDto":{"type":"object","properties":{"isExist":{"type":"boolean"}},"required":["isExist"]},"LoginCredentialDto":{"type":"object","properties":{"email":{"type":"string","example":"testuser@email.com"},"password":{"type":"string","example":"password"}},"required":["email","password"]},"LoginResponseDto":{"type":"object","properties":{"accessToken":{"type":"string","readOnly":true},"userId":{"type":"string","readOnly":true},"userEmail":{"type":"string","readOnly":true},"firstName":{"type":"string","readOnly":true},"lastName":{"type":"string","readOnly":true},"profileImagePath":{"type":"string","readOnly":true},"isAdmin":{"type":"boolean","readOnly":true},"shouldChangePassword":{"type":"boolean","readOnly":true}},"required":["accessToken","userId","userEmail","firstName","lastName","profileImagePath","isAdmin","shouldChangePassword"]},"SignUpDto":{"type":"object","properties":{"email":{"type":"string","example":"testuser@email.com"},"password":{"type":"string","example":"password"},"firstName":{"type":"string","example":"Admin"},"lastName":{"type":"string","example":"Doe"}},"required":["email","password","firstName","lastName"]},"AdminSignupResponseDto":{"type":"object","properties":{"id":{"type":"string"},"email":{"type":"string"},"firstName":{"type":"string"},"lastName":{"type":"string"},"createdAt":{"type":"string"}},"required":["id","email","firstName","lastName","createdAt"]},"ValidateAccessTokenResponseDto":{"type":"object","properties":{"authStatus":{"type":"boolean"}},"required":["authStatus"]},"DeviceTypeEnum":{"type":"string","enum":["IOS","ANDROID","WEB"]},"CreateDeviceInfoDto":{"type":"object","properties":{"deviceType":{"$ref":"#/components/schemas/DeviceTypeEnum"},"deviceId":{"type":"string"},"isAutoBackup":{"type":"boolean"}},"required":["deviceType","deviceId"]},"DeviceInfoResponseDto":{"type":"object","properties":{"id":{"type":"integer"},"deviceType":{"$ref":"#/components/schemas/DeviceTypeEnum"},"userId":{"type":"string"},"deviceId":{"type":"string"},"createdAt":{"type":"string"},"isAutoBackup":{"type":"boolean"}},"required":["id","deviceType","userId","deviceId","createdAt","isAutoBackup"]},"UpdateDeviceInfoDto":{"type":"object","properties":{"deviceType":{"$ref":"#/components/schemas/DeviceTypeEnum"},"deviceId":{"type":"string"},"isAutoBackup":{"type":"boolean"}},"required":["deviceType","deviceId"]},"ServerInfoResponseDto":{"type":"object","properties":{"diskSizeRaw":{"type":"integer"},"diskUseRaw":{"type":"integer"},"diskAvailableRaw":{"type":"integer"},"diskUsagePercentage":{"type":"number","format":"float"},"diskSize":{"type":"string"},"diskUse":{"type":"string"},"diskAvailable":{"type":"string"}},"required":["diskSizeRaw","diskUseRaw","diskAvailableRaw","diskUsagePercentage","diskSize","diskUse","diskAvailable"]},"ServerPingResponse":{"type":"object","properties":{"res":{"type":"string","readOnly":true,"example":"pong"}},"required":["res"]},"ServerVersionReponseDto":{"type":"object","properties":{"major":{"type":"integer"},"minor":{"type":"integer"},"patch":{"type":"integer"},"build":{"type":"integer"}},"required":["major","minor","patch","build"]},"CreateAlbumDto":{"type":"object","properties":{"albumName":{"type":"string"},"sharedWithUserIds":{"type":"array","items":{"type":"string"}},"assetIds":{"type":"array","items":{"type":"string"}}},"required":["albumName"]},"AlbumResponseDto":{"type":"object","properties":{"id":{"type":"string"},"ownerId":{"type":"string"},"albumName":{"type":"string"},"createdAt":{"type":"string"},"albumThumbnailAssetId":{"type":"string","nullable":true},"shared":{"type":"boolean"},"sharedUsers":{"type":"array","items":{"$ref":"#/components/schemas/UserResponseDto"}},"assets":{"type":"array","items":{"$ref":"#/components/schemas/AssetResponseDto"}}},"required":["id","ownerId","albumName","createdAt","albumThumbnailAssetId","shared","sharedUsers","assets"]},"AddUsersDto":{"type":"object","properties":{"sharedUserIds":{"type":"array","items":{"type":"string"}}},"required":["sharedUserIds"]},"AddAssetsDto":{"type":"object","properties":{"assetIds":{"type":"array","items":{"type":"string"}}},"required":["assetIds"]},"RemoveAssetsDto":{"type":"object","properties":{"assetIds":{"type":"array","items":{"type":"string"}}},"required":["assetIds"]},"UpdateAlbumDto":{"type":"object","properties":{"albumName":{"type":"string"},"ownerId":{"type":"string"}},"required":["albumName","ownerId"]}}}} \ No newline at end of file diff --git a/server/package.json b/server/package.json index ab0910ac9c..7eccabdd05 100644 --- a/server/package.json +++ b/server/package.json @@ -23,7 +23,9 @@ "test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand", "test:e2e": "jest --config ./apps/immich/test/jest-e2e.json", "typeorm": "node --require ts-node/register ./node_modules/typeorm/cli.js", - "api:generate-typescript": "rm -rf ../web/src/api/open-api && npx openapi-generator-cli generate -g typescript-axios -i ./immich-openapi-specs.json -o ../web/src/api/open-api" + "api:typescript": "rm -rf ../web/src/api/open-api && npx openapi-generator-cli generate -g typescript-axios -i ./immich-openapi-specs.json -o ../web/src/api/open-api", + "api:dart": "npx openapi-generator-cli generate -g dart -i ./immich-openapi-specs.json -o ../mobile/openapi", + "api:generate": "npm run api:typescript && npm run api:dart" }, "dependencies": { "@mapbox/mapbox-sdk": "^0.13.3", diff --git a/web/src/api/open-api/api.ts b/web/src/api/open-api/api.ts index c94ef1437c..b61c24d1bc 100644 --- a/web/src/api/open-api/api.ts +++ b/web/src/api/open-api/api.ts @@ -158,6 +158,12 @@ export interface AssetFileUploadResponseDto { * @interface AssetResponseDto */ export interface AssetResponseDto { + /** + * + * @type {AssetTypeEnum} + * @memberof AssetResponseDto + */ + 'type': AssetTypeEnum; /** * * @type {string} @@ -182,12 +188,6 @@ export interface AssetResponseDto { * @memberof AssetResponseDto */ 'deviceId': string; - /** - * - * @type {string} - * @memberof AssetResponseDto - */ - 'type': AssetResponseDtoTypeEnum; /** * * @type {string} @@ -255,15 +255,21 @@ export interface AssetResponseDto { */ 'smartInfo'?: SmartInfoResponseDto; } +/** + * + * @export + * @enum {string} + */ -export const AssetResponseDtoTypeEnum = { +export const AssetTypeEnum = { Image: 'IMAGE', Video: 'VIDEO', Audio: 'AUDIO', Other: 'OTHER' } as const; -export type AssetResponseDtoTypeEnum = typeof AssetResponseDtoTypeEnum[keyof typeof AssetResponseDtoTypeEnum]; +export type AssetTypeEnum = typeof AssetTypeEnum[keyof typeof AssetTypeEnum]; + /** * @@ -330,16 +336,16 @@ export interface CreateAlbumDto { export interface CreateDeviceInfoDto { /** * - * @type {string} + * @type {DeviceTypeEnum} * @memberof CreateDeviceInfoDto */ - 'deviceId': string; + 'deviceType': DeviceTypeEnum; /** * * @type {string} * @memberof CreateDeviceInfoDto */ - 'deviceType': CreateDeviceInfoDtoDeviceTypeEnum; + 'deviceId': string; /** * * @type {boolean} @@ -347,15 +353,6 @@ export interface CreateDeviceInfoDto { */ 'isAutoBackup'?: boolean; } - -export const CreateDeviceInfoDtoDeviceTypeEnum = { - Ios: 'IOS', - Android: 'ANDROID', - Web: 'WEB' -} as const; - -export type CreateDeviceInfoDtoDeviceTypeEnum = typeof CreateDeviceInfoDtoDeviceTypeEnum[keyof typeof CreateDeviceInfoDtoDeviceTypeEnum]; - /** * * @export @@ -493,6 +490,39 @@ export interface DeleteAssetDto { */ 'ids': Array; } +/** + * + * @export + * @interface DeleteAssetResponseDto + */ +export interface DeleteAssetResponseDto { + /** + * + * @type {DeleteAssetStatus} + * @memberof DeleteAssetResponseDto + */ + 'status': DeleteAssetStatus; + /** + * + * @type {string} + * @memberof DeleteAssetResponseDto + */ + 'id': string; +} +/** + * + * @export + * @enum {string} + */ + +export const DeleteAssetStatus = { + Success: 'SUCCESS', + Failed: 'FAILED' +} as const; + +export type DeleteAssetStatus = typeof DeleteAssetStatus[keyof typeof DeleteAssetStatus]; + + /** * * @export @@ -505,6 +535,12 @@ export interface DeviceInfoResponseDto { * @memberof DeviceInfoResponseDto */ 'id': number; + /** + * + * @type {DeviceTypeEnum} + * @memberof DeviceInfoResponseDto + */ + 'deviceType': DeviceTypeEnum; /** * * @type {string} @@ -517,18 +553,6 @@ export interface DeviceInfoResponseDto { * @memberof DeviceInfoResponseDto */ 'deviceId': string; - /** - * - * @type {string} - * @memberof DeviceInfoResponseDto - */ - 'deviceType': DeviceInfoResponseDtoDeviceTypeEnum; - /** - * - * @type {string} - * @memberof DeviceInfoResponseDto - */ - 'notificationToken': string | null; /** * * @type {string} @@ -542,14 +566,20 @@ export interface DeviceInfoResponseDto { */ 'isAutoBackup': boolean; } +/** + * + * @export + * @enum {string} + */ -export const DeviceInfoResponseDtoDeviceTypeEnum = { +export const DeviceTypeEnum = { Ios: 'IOS', Android: 'ANDROID', Web: 'WEB' } as const; -export type DeviceInfoResponseDtoDeviceTypeEnum = typeof DeviceInfoResponseDtoDeviceTypeEnum[keyof typeof DeviceInfoResponseDtoDeviceTypeEnum]; +export type DeviceTypeEnum = typeof DeviceTypeEnum[keyof typeof DeviceTypeEnum]; + /** * @@ -562,121 +592,121 @@ export interface ExifResponseDto { * @type {string} * @memberof ExifResponseDto */ - 'id': string; + 'id'?: string | null; /** * * @type {string} * @memberof ExifResponseDto */ - 'make': string | null; + 'make'?: string | null; /** * * @type {string} * @memberof ExifResponseDto */ - 'model': string | null; + 'model'?: string | null; /** * * @type {string} * @memberof ExifResponseDto */ - 'imageName': string | null; + 'imageName'?: string | null; /** * * @type {number} * @memberof ExifResponseDto */ - 'exifImageWidth': number | null; + 'exifImageWidth'?: number | null; /** * * @type {number} * @memberof ExifResponseDto */ - 'exifImageHeight': number | null; + 'exifImageHeight'?: number | null; /** * * @type {number} * @memberof ExifResponseDto */ - 'fileSizeInByte': number | null; + 'fileSizeInByte'?: number | null; /** * * @type {string} * @memberof ExifResponseDto */ - 'orientation': string | null; + 'orientation'?: string | null; /** * * @type {string} * @memberof ExifResponseDto */ - 'dateTimeOriginal': string | null; + 'dateTimeOriginal'?: string | null; /** * * @type {string} * @memberof ExifResponseDto */ - 'modifyDate': string | null; + 'modifyDate'?: string | null; /** * * @type {string} * @memberof ExifResponseDto */ - 'lensModel': string | null; + 'lensModel'?: string | null; /** * * @type {number} * @memberof ExifResponseDto */ - 'fNumber': number | null; + 'fNumber'?: number | null; /** * * @type {number} * @memberof ExifResponseDto */ - 'focalLength': number | null; + 'focalLength'?: number | null; /** * * @type {number} * @memberof ExifResponseDto */ - 'iso': number | null; + 'iso'?: number | null; /** * * @type {number} * @memberof ExifResponseDto */ - 'exposureTime': number | null; + 'exposureTime'?: number | null; /** * * @type {number} * @memberof ExifResponseDto */ - 'latitude': number | null; + 'latitude'?: number | null; /** * * @type {number} * @memberof ExifResponseDto */ - 'longitude': number | null; + 'longitude'?: number | null; /** * * @type {string} * @memberof ExifResponseDto */ - 'city': string | null; + 'city'?: string | null; /** * * @type {string} * @memberof ExifResponseDto */ - 'state': string | null; + 'state'?: string | null; /** * * @type {string} * @memberof ExifResponseDto */ - 'country': string | null; + 'country'?: string | null; } /** * @@ -784,24 +814,6 @@ export interface SearchAssetDto { * @interface ServerInfoResponseDto */ export interface ServerInfoResponseDto { - /** - * - * @type {string} - * @memberof ServerInfoResponseDto - */ - 'diskSize': string; - /** - * - * @type {string} - * @memberof ServerInfoResponseDto - */ - 'diskUse': string; - /** - * - * @type {string} - * @memberof ServerInfoResponseDto - */ - 'diskAvailable': string; /** * * @type {number} @@ -826,6 +838,24 @@ export interface ServerInfoResponseDto { * @memberof ServerInfoResponseDto */ 'diskUsagePercentage': number; + /** + * + * @type {string} + * @memberof ServerInfoResponseDto + */ + 'diskSize': string; + /** + * + * @type {string} + * @memberof ServerInfoResponseDto + */ + 'diskUse': string; + /** + * + * @type {string} + * @memberof ServerInfoResponseDto + */ + 'diskAvailable': string; } /** * @@ -946,6 +976,31 @@ export interface UpdateAlbumDto { */ 'ownerId': string; } +/** + * + * @export + * @interface UpdateDeviceInfoDto + */ +export interface UpdateDeviceInfoDto { + /** + * + * @type {DeviceTypeEnum} + * @memberof UpdateDeviceInfoDto + */ + 'deviceType': DeviceTypeEnum; + /** + * + * @type {string} + * @memberof UpdateDeviceInfoDto + */ + 'deviceId': string; + /** + * + * @type {boolean} + * @memberof UpdateDeviceInfoDto + */ + 'isAutoBackup'?: boolean; +} /** * * @export @@ -1063,6 +1118,19 @@ export interface UserResponseDto { */ 'isAdmin': boolean; } +/** + * + * @export + * @interface ValidateAccessTokenResponseDto + */ +export interface ValidateAccessTokenResponseDto { + /** + * + * @type {boolean} + * @memberof ValidateAccessTokenResponseDto + */ + 'authStatus': boolean; +} /** * AlbumApi - axios parameter creator @@ -2306,7 +2374,7 @@ export const AssetApiFp = function(configuration?: Configuration) { * @param {*} [options] Override http request option. * @throws {RequiredError} */ - async deleteAsset(deleteAssetDto: DeleteAssetDto, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { + async deleteAsset(deleteAssetDto: DeleteAssetDto, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise>> { const localVarAxiosArgs = await localVarAxiosParamCreator.deleteAsset(deleteAssetDto, options); return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); }, @@ -2349,7 +2417,7 @@ export const AssetApiFp = function(configuration?: Configuration) { * @param {*} [options] Override http request option. * @throws {RequiredError} */ - async getAssetSearchTerms(options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise>> { + async getAssetSearchTerms(options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise>> { const localVarAxiosArgs = await localVarAxiosParamCreator.getAssetSearchTerms(options); return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); }, @@ -2451,7 +2519,7 @@ export const AssetApiFactory = function (configuration?: Configuration, basePath * @param {*} [options] Override http request option. * @throws {RequiredError} */ - deleteAsset(deleteAssetDto: DeleteAssetDto, options?: any): AxiosPromise { + deleteAsset(deleteAssetDto: DeleteAssetDto, options?: any): AxiosPromise> { return localVarFp.deleteAsset(deleteAssetDto, options).then((request) => request(axios, basePath)); }, /** @@ -2490,7 +2558,7 @@ export const AssetApiFactory = function (configuration?: Configuration, basePath * @param {*} [options] Override http request option. * @throws {RequiredError} */ - getAssetSearchTerms(options?: any): AxiosPromise> { + getAssetSearchTerms(options?: any): AxiosPromise> { return localVarFp.getAssetSearchTerms(options).then((request) => request(axios, basePath)); }, /** @@ -2863,7 +2931,7 @@ export const AuthenticationApiFp = function(configuration?: Configuration) { * @param {*} [options] Override http request option. * @throws {RequiredError} */ - async validateAccessToken(options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { + async validateAccessToken(options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { const localVarAxiosArgs = await localVarAxiosParamCreator.validateAccessToken(options); return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); }, @@ -2900,7 +2968,7 @@ export const AuthenticationApiFactory = function (configuration?: Configuration, * @param {*} [options] Override http request option. * @throws {RequiredError} */ - validateAccessToken(options?: any): AxiosPromise { + validateAccessToken(options?: any): AxiosPromise { return localVarFp.validateAccessToken(options).then((request) => request(axios, basePath)); }, }; @@ -2994,13 +3062,13 @@ export const DeviceInfoApiAxiosParamCreator = function (configuration?: Configur }, /** * - * @param {object} body + * @param {UpdateDeviceInfoDto} updateDeviceInfoDto * @param {*} [options] Override http request option. * @throws {RequiredError} */ - updateDeviceInfo: async (body: object, options: AxiosRequestConfig = {}): Promise => { - // verify required parameter 'body' is not null or undefined - assertParamExists('updateDeviceInfo', 'body', body) + updateDeviceInfo: async (updateDeviceInfoDto: UpdateDeviceInfoDto, options: AxiosRequestConfig = {}): Promise => { + // verify required parameter 'updateDeviceInfoDto' is not null or undefined + assertParamExists('updateDeviceInfo', 'updateDeviceInfoDto', updateDeviceInfoDto) const localVarPath = `/device-info`; // use dummy base URL string because the URL constructor only accepts absolute URLs. const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); @@ -3024,7 +3092,7 @@ export const DeviceInfoApiAxiosParamCreator = function (configuration?: Configur setSearchParams(localVarUrlObj, localVarQueryParameter); let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; - localVarRequestOptions.data = serializeDataIfNeeded(body, localVarRequestOptions, configuration) + localVarRequestOptions.data = serializeDataIfNeeded(updateDeviceInfoDto, localVarRequestOptions, configuration) return { url: toPathString(localVarUrlObj), @@ -3053,12 +3121,12 @@ export const DeviceInfoApiFp = function(configuration?: Configuration) { }, /** * - * @param {object} body + * @param {UpdateDeviceInfoDto} updateDeviceInfoDto * @param {*} [options] Override http request option. * @throws {RequiredError} */ - async updateDeviceInfo(body: object, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { - const localVarAxiosArgs = await localVarAxiosParamCreator.updateDeviceInfo(body, options); + async updateDeviceInfo(updateDeviceInfoDto: UpdateDeviceInfoDto, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { + const localVarAxiosArgs = await localVarAxiosParamCreator.updateDeviceInfo(updateDeviceInfoDto, options); return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); }, } @@ -3082,12 +3150,12 @@ export const DeviceInfoApiFactory = function (configuration?: Configuration, bas }, /** * - * @param {object} body + * @param {UpdateDeviceInfoDto} updateDeviceInfoDto * @param {*} [options] Override http request option. * @throws {RequiredError} */ - updateDeviceInfo(body: object, options?: any): AxiosPromise { - return localVarFp.updateDeviceInfo(body, options).then((request) => request(axios, basePath)); + updateDeviceInfo(updateDeviceInfoDto: UpdateDeviceInfoDto, options?: any): AxiosPromise { + return localVarFp.updateDeviceInfo(updateDeviceInfoDto, options).then((request) => request(axios, basePath)); }, }; }; @@ -3112,13 +3180,13 @@ export class DeviceInfoApi extends BaseAPI { /** * - * @param {object} body + * @param {UpdateDeviceInfoDto} updateDeviceInfoDto * @param {*} [options] Override http request option. * @throws {RequiredError} * @memberof DeviceInfoApi */ - public updateDeviceInfo(body: object, options?: AxiosRequestConfig) { - return DeviceInfoApiFp(this.configuration).updateDeviceInfo(body, options).then((request) => request(this.axios, this.basePath)); + public updateDeviceInfo(updateDeviceInfoDto: UpdateDeviceInfoDto, options?: AxiosRequestConfig) { + return DeviceInfoApiFp(this.configuration).updateDeviceInfo(updateDeviceInfoDto, options).then((request) => request(this.axios, this.basePath)); } } diff --git a/web/src/lib/components/forms/login-form.svelte b/web/src/lib/components/forms/login-form.svelte index 7b20c4402f..b0934fb5dc 100644 --- a/web/src/lib/components/forms/login-form.svelte +++ b/web/src/lib/components/forms/login-form.svelte @@ -45,10 +45,6 @@ {#if loginPageMessage} - -

{@html loginPageMessage}