You've already forked immich
							
							
				mirror of
				https://github.com/immich-app/immich.git
				synced 2025-10-31 00:18:28 +02:00 
			
		
		
		
	Mobile performance improvements (#417)
* First performance tweaks (caching and rendering improvemetns) * Revert asset response caching * 3-step image loading in asset viewer * Prevent panning and zooming until full-scale version is loaded * Loading indicator * Adapt to gallery PR * Cleanup * Dart format * Fix exif sheet * Disable three stage loading until settings are available
This commit is contained in:
		| @@ -1,6 +1,8 @@ | |||||||
| import 'package:easy_localization/easy_localization.dart'; | import 'package:easy_localization/easy_localization.dart'; | ||||||
|  | import 'package:flutter/foundation.dart'; | ||||||
| import 'package:flutter/material.dart'; | import 'package:flutter/material.dart'; | ||||||
| import 'package:flutter/services.dart'; | import 'package:flutter/services.dart'; | ||||||
|  | import 'package:flutter_displaymode/flutter_displaymode.dart'; | ||||||
| import 'package:hive_flutter/hive_flutter.dart'; | import 'package:hive_flutter/hive_flutter.dart'; | ||||||
| import 'package:hooks_riverpod/hooks_riverpod.dart'; | import 'package:hooks_riverpod/hooks_riverpod.dart'; | ||||||
| import 'package:immich_mobile/constants/immich_colors.dart'; | import 'package:immich_mobile/constants/immich_colors.dart'; | ||||||
| @@ -49,6 +51,10 @@ void main() async { | |||||||
|     Locale('it', 'IT'), |     Locale('it', 'IT'), | ||||||
|   ]; |   ]; | ||||||
|  |  | ||||||
|  |   if (kReleaseMode) { | ||||||
|  |     await FlutterDisplayMode.setHighRefreshRate(); | ||||||
|  |   } | ||||||
|  |  | ||||||
|   runApp( |   runApp( | ||||||
|     EasyLocalization( |     EasyLocalization( | ||||||
|       supportedLocales: locales, |       supportedLocales: locales, | ||||||
|   | |||||||
| @@ -8,6 +8,7 @@ import 'package:immich_mobile/constants/hive_box.dart'; | |||||||
| import 'package:immich_mobile/modules/login/providers/authentication.provider.dart'; | import 'package:immich_mobile/modules/login/providers/authentication.provider.dart'; | ||||||
| import 'package:immich_mobile/modules/album/providers/asset_selection.provider.dart'; | import 'package:immich_mobile/modules/album/providers/asset_selection.provider.dart'; | ||||||
| import 'package:immich_mobile/routing/router.dart'; | import 'package:immich_mobile/routing/router.dart'; | ||||||
|  | import 'package:immich_mobile/utils/image_url_builder.dart'; | ||||||
| import 'package:openapi/api.dart'; | import 'package:openapi/api.dart'; | ||||||
|  |  | ||||||
| class AlbumViewerThumbnail extends HookConsumerWidget { | class AlbumViewerThumbnail extends HookConsumerWidget { | ||||||
| @@ -24,8 +25,7 @@ class AlbumViewerThumbnail extends HookConsumerWidget { | |||||||
|   Widget build(BuildContext context, WidgetRef ref) { |   Widget build(BuildContext context, WidgetRef ref) { | ||||||
|     final cacheKey = useState(1); |     final cacheKey = useState(1); | ||||||
|     var box = Hive.box(userInfoBox); |     var box = Hive.box(userInfoBox); | ||||||
|     var thumbnailRequestUrl = |     var thumbnailRequestUrl = getThumbnailUrl(asset); | ||||||
|         '${box.get(serverEndpointKey)}/asset/thumbnail/${asset.id}'; |  | ||||||
|     var deviceId = ref.watch(authenticationProvider).deviceId; |     var deviceId = ref.watch(authenticationProvider).deviceId; | ||||||
|     final selectedAssetsInAlbumViewer = |     final selectedAssetsInAlbumViewer = | ||||||
|         ref.watch(assetSelectionProvider).selectedAssetsInAlbumViewer; |         ref.watch(assetSelectionProvider).selectedAssetsInAlbumViewer; | ||||||
| @@ -37,7 +37,6 @@ class AlbumViewerThumbnail extends HookConsumerWidget { | |||||||
|         GalleryViewerRoute( |         GalleryViewerRoute( | ||||||
|           asset: asset, |           asset: asset, | ||||||
|           assetList: assetList, |           assetList: assetList, | ||||||
|           thumbnailRequestUrl: thumbnailRequestUrl, |  | ||||||
|         ), |         ), | ||||||
|       ); |       ); | ||||||
|     } |     } | ||||||
|   | |||||||
| @@ -4,6 +4,7 @@ import 'package:flutter_hooks/flutter_hooks.dart'; | |||||||
| import 'package:hive_flutter/hive_flutter.dart'; | import 'package:hive_flutter/hive_flutter.dart'; | ||||||
| import 'package:hooks_riverpod/hooks_riverpod.dart'; | import 'package:hooks_riverpod/hooks_riverpod.dart'; | ||||||
| import 'package:immich_mobile/constants/hive_box.dart'; | import 'package:immich_mobile/constants/hive_box.dart'; | ||||||
|  | import 'package:immich_mobile/utils/image_url_builder.dart'; | ||||||
| import 'package:openapi/api.dart'; | import 'package:openapi/api.dart'; | ||||||
|  |  | ||||||
| class SharedAlbumThumbnailImage extends HookConsumerWidget { | class SharedAlbumThumbnailImage extends HookConsumerWidget { | ||||||
| @@ -17,8 +18,6 @@ class SharedAlbumThumbnailImage extends HookConsumerWidget { | |||||||
|     final cacheKey = useState(1); |     final cacheKey = useState(1); | ||||||
|  |  | ||||||
|     var box = Hive.box(userInfoBox); |     var box = Hive.box(userInfoBox); | ||||||
|     var thumbnailRequestUrl = |  | ||||||
|         '${box.get(serverEndpointKey)}/asset/thumbnail/${asset.id}'; |  | ||||||
|  |  | ||||||
|     return GestureDetector( |     return GestureDetector( | ||||||
|       onTap: () { |       onTap: () { | ||||||
| @@ -32,7 +31,7 @@ class SharedAlbumThumbnailImage extends HookConsumerWidget { | |||||||
|             height: 500, |             height: 500, | ||||||
|             memCacheHeight: 500, |             memCacheHeight: 500, | ||||||
|             fit: BoxFit.cover, |             fit: BoxFit.cover, | ||||||
|             imageUrl: thumbnailRequestUrl, |             imageUrl: getThumbnailUrl(asset), | ||||||
|             httpHeaders: {"Authorization": "Bearer ${box.get(accessTokenKey)}"}, |             httpHeaders: {"Authorization": "Bearer ${box.get(accessTokenKey)}"}, | ||||||
|             fadeInDuration: const Duration(milliseconds: 250), |             fadeInDuration: const Duration(milliseconds: 250), | ||||||
|             progressIndicatorBuilder: (context, url, downloadProgress) => |             progressIndicatorBuilder: (context, url, downloadProgress) => | ||||||
|   | |||||||
| @@ -3,7 +3,7 @@ import 'package:flutter/cupertino.dart'; | |||||||
| import 'package:flutter/material.dart'; | import 'package:flutter/material.dart'; | ||||||
| import 'package:photo_view/photo_view.dart'; | import 'package:photo_view/photo_view.dart'; | ||||||
|  |  | ||||||
| enum _RemoteImageStatus { empty, thumbnail, full } | enum _RemoteImageStatus { empty, thumbnail, preview, full } | ||||||
|  |  | ||||||
| class _RemotePhotoViewState extends State<RemotePhotoView> { | class _RemotePhotoViewState extends State<RemotePhotoView> { | ||||||
|   late CachedNetworkImageProvider _imageProvider; |   late CachedNetworkImageProvider _imageProvider; | ||||||
| @@ -15,13 +15,16 @@ class _RemotePhotoViewState extends State<RemotePhotoView> { | |||||||
|   @override |   @override | ||||||
|   Widget build(BuildContext context) { |   Widget build(BuildContext context) { | ||||||
|     bool allowMoving = _status == _RemoteImageStatus.full; |     bool allowMoving = _status == _RemoteImageStatus.full; | ||||||
|     return PhotoView( |  | ||||||
|       imageProvider: _imageProvider, |     return IgnorePointer( | ||||||
|       minScale: PhotoViewComputedScale.contained, |       ignoring: !allowMoving, | ||||||
|       maxScale: allowMoving ? 1.0 : PhotoViewComputedScale.contained, |       child: PhotoView( | ||||||
|       enablePanAlways: true, |         imageProvider: _imageProvider, | ||||||
|       scaleStateChangedCallback: _scaleStateChanged, |         minScale: PhotoViewComputedScale.contained, | ||||||
|       onScaleEnd: _onScaleListener, |         enablePanAlways: true, | ||||||
|  |         scaleStateChangedCallback: _scaleStateChanged, | ||||||
|  |         onScaleEnd: _onScaleListener, | ||||||
|  |       ), | ||||||
|     ); |     ); | ||||||
|   } |   } | ||||||
|  |  | ||||||
| @@ -52,6 +55,14 @@ class _RemotePhotoViewState extends State<RemotePhotoView> { | |||||||
|     widget.isZoomedFunction(); |     widget.isZoomedFunction(); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|  |   void _fireStartLoadingEvent() { | ||||||
|  |     if (widget.onLoadingStart != null) widget.onLoadingStart!(); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   void _fireFinishedLoadingEvent() { | ||||||
|  |     if (widget.onLoadingCompleted != null) widget.onLoadingCompleted!(); | ||||||
|  |   } | ||||||
|  |  | ||||||
|   CachedNetworkImageProvider _authorizedImageProvider(String url) { |   CachedNetworkImageProvider _authorizedImageProvider(String url) { | ||||||
|     return CachedNetworkImageProvider( |     return CachedNetworkImageProvider( | ||||||
|       url, |       url, | ||||||
| @@ -64,14 +75,25 @@ class _RemotePhotoViewState extends State<RemotePhotoView> { | |||||||
|     _RemoteImageStatus newStatus, |     _RemoteImageStatus newStatus, | ||||||
|     CachedNetworkImageProvider provider, |     CachedNetworkImageProvider provider, | ||||||
|   ) { |   ) { | ||||||
|     // Transition to same status is forbidden |  | ||||||
|     if (_status == newStatus) return; |     if (_status == newStatus) return; | ||||||
|     // Transition full -> thumbnail is forbidden |  | ||||||
|     if (_status == _RemoteImageStatus.full && |     if (_status == _RemoteImageStatus.full && | ||||||
|         newStatus == _RemoteImageStatus.thumbnail) return; |         newStatus == _RemoteImageStatus.thumbnail) return; | ||||||
|  |  | ||||||
|  |     if (_status == _RemoteImageStatus.preview && | ||||||
|  |         newStatus == _RemoteImageStatus.thumbnail) return; | ||||||
|  |  | ||||||
|  |     if (_status == _RemoteImageStatus.full && | ||||||
|  |         newStatus == _RemoteImageStatus.preview) return; | ||||||
|  |  | ||||||
|     if (!mounted) return; |     if (!mounted) return; | ||||||
|  |  | ||||||
|  |     if (newStatus != _RemoteImageStatus.full) { | ||||||
|  |       _fireStartLoadingEvent(); | ||||||
|  |     } else { | ||||||
|  |       _fireFinishedLoadingEvent(); | ||||||
|  |     } | ||||||
|  |  | ||||||
|     setState(() { |     setState(() { | ||||||
|       _status = newStatus; |       _status = newStatus; | ||||||
|       _imageProvider = provider; |       _imageProvider = provider; | ||||||
| @@ -92,6 +114,16 @@ class _RemotePhotoViewState extends State<RemotePhotoView> { | |||||||
|       }), |       }), | ||||||
|     ); |     ); | ||||||
|  |  | ||||||
|  |     if (widget.previewUrl != null) { | ||||||
|  |       CachedNetworkImageProvider previewProvider = | ||||||
|  |           _authorizedImageProvider(widget.previewUrl!); | ||||||
|  |       previewProvider.resolve(const ImageConfiguration()).addListener( | ||||||
|  |         ImageStreamListener((ImageInfo imageInfo, _) { | ||||||
|  |           _performStateTransition(_RemoteImageStatus.preview, previewProvider); | ||||||
|  |         }), | ||||||
|  |       ); | ||||||
|  |     } | ||||||
|  |  | ||||||
|     CachedNetworkImageProvider fullProvider = |     CachedNetworkImageProvider fullProvider = | ||||||
|         _authorizedImageProvider(widget.imageUrl); |         _authorizedImageProvider(widget.imageUrl); | ||||||
|     fullProvider.resolve(const ImageConfiguration()).addListener( |     fullProvider.resolve(const ImageConfiguration()).addListener( | ||||||
| @@ -109,20 +141,26 @@ class _RemotePhotoViewState extends State<RemotePhotoView> { | |||||||
| } | } | ||||||
|  |  | ||||||
| class RemotePhotoView extends StatefulWidget { | class RemotePhotoView extends StatefulWidget { | ||||||
|   const RemotePhotoView({ |   const RemotePhotoView( | ||||||
|     Key? key, |       {Key? key, | ||||||
|     required this.thumbnailUrl, |       required this.thumbnailUrl, | ||||||
|     required this.imageUrl, |       required this.imageUrl, | ||||||
|     required this.authToken, |       required this.authToken, | ||||||
|     required this.isZoomedFunction, |       required this.isZoomedFunction, | ||||||
|     required this.isZoomedListener, |       required this.isZoomedListener, | ||||||
|     required this.onSwipeDown, |       required this.onSwipeDown, | ||||||
|     required this.onSwipeUp, |       required this.onSwipeUp, | ||||||
|   }) : super(key: key); |       this.previewUrl, | ||||||
|  |       this.onLoadingCompleted, | ||||||
|  |       this.onLoadingStart}) | ||||||
|  |       : super(key: key); | ||||||
|  |  | ||||||
|   final String thumbnailUrl; |   final String thumbnailUrl; | ||||||
|   final String imageUrl; |   final String imageUrl; | ||||||
|   final String authToken; |   final String authToken; | ||||||
|  |   final String? previewUrl; | ||||||
|  |   final Function? onLoadingCompleted; | ||||||
|  |   final Function? onLoadingStart; | ||||||
|  |  | ||||||
|   final void Function() onSwipeDown; |   final void Function() onSwipeDown; | ||||||
|   final void Function() onSwipeUp; |   final void Function() onSwipeUp; | ||||||
|   | |||||||
| @@ -11,11 +11,13 @@ class TopControlAppBar extends ConsumerWidget with PreferredSizeWidget { | |||||||
|     required this.asset, |     required this.asset, | ||||||
|     required this.onMoreInfoPressed, |     required this.onMoreInfoPressed, | ||||||
|     required this.onDownloadPressed, |     required this.onDownloadPressed, | ||||||
|  |     this.loading = false | ||||||
|   }) : super(key: key); |   }) : super(key: key); | ||||||
|  |  | ||||||
|   final AssetResponseDto asset; |   final AssetResponseDto asset; | ||||||
|   final Function onMoreInfoPressed; |   final Function onMoreInfoPressed; | ||||||
|   final Function onDownloadPressed; |   final Function onDownloadPressed; | ||||||
|  |   final bool loading; | ||||||
|  |  | ||||||
|   @override |   @override | ||||||
|   Widget build(BuildContext context, WidgetRef ref) { |   Widget build(BuildContext context, WidgetRef ref) { | ||||||
| @@ -35,6 +37,14 @@ class TopControlAppBar extends ConsumerWidget with PreferredSizeWidget { | |||||||
|         ), |         ), | ||||||
|       ), |       ), | ||||||
|       actions: [ |       actions: [ | ||||||
|  |         if (loading) Center( | ||||||
|  |           child: Container( | ||||||
|  |             margin: const EdgeInsets.symmetric(horizontal: 15.0), | ||||||
|  |             width: iconSize, | ||||||
|  |             height: iconSize, | ||||||
|  |             child: const CircularProgressIndicator(strokeWidth: 2.0), | ||||||
|  |           ), | ||||||
|  |         ) , | ||||||
|         IconButton( |         IconButton( | ||||||
|           iconSize: iconSize, |           iconSize: iconSize, | ||||||
|           splashRadius: iconSize, |           splashRadius: iconSize, | ||||||
|   | |||||||
| @@ -17,13 +17,13 @@ import 'package:openapi/api.dart'; | |||||||
| class GalleryViewerPage extends HookConsumerWidget { | class GalleryViewerPage extends HookConsumerWidget { | ||||||
|   late List<AssetResponseDto> assetList; |   late List<AssetResponseDto> assetList; | ||||||
|   final AssetResponseDto asset; |   final AssetResponseDto asset; | ||||||
|   final String thumbnailRequestUrl; |  | ||||||
|  |   static const _threeStageLoading = false; | ||||||
|  |  | ||||||
|   GalleryViewerPage({ |   GalleryViewerPage({ | ||||||
|     Key? key, |     Key? key, | ||||||
|     required this.assetList, |     required this.assetList, | ||||||
|     required this.asset, |     required this.asset, | ||||||
|     required this.thumbnailRequestUrl, |  | ||||||
|   }) : super(key: key); |   }) : super(key: key); | ||||||
|  |  | ||||||
|   AssetResponseDto? assetDetail; |   AssetResponseDto? assetDetail; | ||||||
| @@ -32,6 +32,7 @@ class GalleryViewerPage extends HookConsumerWidget { | |||||||
|     final Box<dynamic> box = Hive.box(userInfoBox); |     final Box<dynamic> box = Hive.box(userInfoBox); | ||||||
|  |  | ||||||
|     int indexOfAsset = assetList.indexOf(asset); |     int indexOfAsset = assetList.indexOf(asset); | ||||||
|  |     final loading = useState(false); | ||||||
|  |  | ||||||
|     @override |     @override | ||||||
|     void initState(int index) { |     void initState(int index) { | ||||||
| @@ -74,6 +75,7 @@ class GalleryViewerPage extends HookConsumerWidget { | |||||||
|     return Scaffold( |     return Scaffold( | ||||||
|       backgroundColor: Colors.black, |       backgroundColor: Colors.black, | ||||||
|       appBar: TopControlAppBar( |       appBar: TopControlAppBar( | ||||||
|  |         loading: loading.value, | ||||||
|         asset: assetList[indexOfAsset], |         asset: assetList[indexOfAsset], | ||||||
|         onMoreInfoPressed: () { |         onMoreInfoPressed: () { | ||||||
|           showInfo(); |           showInfo(); | ||||||
| @@ -98,15 +100,14 @@ class GalleryViewerPage extends HookConsumerWidget { | |||||||
|             getAssetExif(); |             getAssetExif(); | ||||||
|             if (assetList[index].type == AssetTypeEnum.IMAGE) { |             if (assetList[index].type == AssetTypeEnum.IMAGE) { | ||||||
|               return ImageViewerPage( |               return ImageViewerPage( | ||||||
|                 thumbnailUrl: |  | ||||||
|                     '${box.get(serverEndpointKey)}/asset/thumbnail/${assetList[index].id}', |  | ||||||
|                 imageUrl: |  | ||||||
|                     '${box.get(serverEndpointKey)}/asset/file?aid=${assetList[index].deviceAssetId}&did=${assetList[index].deviceId}&isThumb=false', |  | ||||||
|                 authToken: 'Bearer ${box.get(accessTokenKey)}', |                 authToken: 'Bearer ${box.get(accessTokenKey)}', | ||||||
|                 isZoomedFunction: isZoomedMethod, |                 isZoomedFunction: isZoomedMethod, | ||||||
|                 isZoomedListener: isZoomedListener, |                 isZoomedListener: isZoomedListener, | ||||||
|  |                 onLoadingCompleted: () => loading.value = false, | ||||||
|  |                 onLoadingStart: () => loading.value = _threeStageLoading, | ||||||
|                 asset: assetList[index], |                 asset: assetList[index], | ||||||
|                 heroTag: assetList[index].id, |                 heroTag: assetList[index].id, | ||||||
|  |                 threeStageLoading: _threeStageLoading | ||||||
|               ); |               ); | ||||||
|             } else { |             } else { | ||||||
|               return SwipeDetector( |               return SwipeDetector( | ||||||
|   | |||||||
| @@ -8,27 +8,30 @@ import 'package:immich_mobile/modules/asset_viewer/ui/download_loading_indicator | |||||||
| import 'package:immich_mobile/modules/asset_viewer/ui/exif_bottom_sheet.dart'; | import 'package:immich_mobile/modules/asset_viewer/ui/exif_bottom_sheet.dart'; | ||||||
| import 'package:immich_mobile/modules/asset_viewer/ui/remote_photo_view.dart'; | import 'package:immich_mobile/modules/asset_viewer/ui/remote_photo_view.dart'; | ||||||
| import 'package:immich_mobile/modules/home/services/asset.service.dart'; | import 'package:immich_mobile/modules/home/services/asset.service.dart'; | ||||||
|  | import 'package:immich_mobile/utils/image_url_builder.dart'; | ||||||
| import 'package:openapi/api.dart'; | import 'package:openapi/api.dart'; | ||||||
|  |  | ||||||
| // ignore: must_be_immutable | // ignore: must_be_immutable | ||||||
| class ImageViewerPage extends HookConsumerWidget { | class ImageViewerPage extends HookConsumerWidget { | ||||||
|   final String imageUrl; |  | ||||||
|   final String heroTag; |   final String heroTag; | ||||||
|   final String thumbnailUrl; |  | ||||||
|   final AssetResponseDto asset; |   final AssetResponseDto asset; | ||||||
|   final String authToken; |   final String authToken; | ||||||
|   final ValueNotifier<bool> isZoomedListener; |   final ValueNotifier<bool> isZoomedListener; | ||||||
|   final void Function() isZoomedFunction; |   final void Function() isZoomedFunction; | ||||||
|  |   final void Function() onLoadingCompleted; | ||||||
|  |   final void Function() onLoadingStart; | ||||||
|  |   final bool threeStageLoading; | ||||||
|  |  | ||||||
|   ImageViewerPage({ |   ImageViewerPage({ | ||||||
|     Key? key, |     Key? key, | ||||||
|     required this.imageUrl, |  | ||||||
|     required this.heroTag, |     required this.heroTag, | ||||||
|     required this.thumbnailUrl, |  | ||||||
|     required this.asset, |     required this.asset, | ||||||
|     required this.authToken, |     required this.authToken, | ||||||
|     required this.isZoomedFunction, |     required this.isZoomedFunction, | ||||||
|     required this.isZoomedListener, |     required this.isZoomedListener, | ||||||
|  |     required this.onLoadingCompleted, | ||||||
|  |     required this.onLoadingStart, | ||||||
|  |     required this.threeStageLoading, | ||||||
|   }) : super(key: key); |   }) : super(key: key); | ||||||
|  |  | ||||||
|   AssetResponseDto? assetDetail; |   AssetResponseDto? assetDetail; | ||||||
| @@ -68,14 +71,18 @@ class ImageViewerPage extends HookConsumerWidget { | |||||||
|           child: Hero( |           child: Hero( | ||||||
|             tag: heroTag, |             tag: heroTag, | ||||||
|             child: RemotePhotoView( |             child: RemotePhotoView( | ||||||
|               thumbnailUrl: thumbnailUrl, |                 thumbnailUrl: getThumbnailUrl(asset), | ||||||
|               imageUrl: imageUrl, |                 imageUrl: getImageUrl(asset), | ||||||
|               authToken: authToken, |                 previewUrl: threeStageLoading | ||||||
|               isZoomedFunction: isZoomedFunction, |                     ? getThumbnailUrl(asset, type: ThumbnailFormat.JPEG) | ||||||
|               isZoomedListener: isZoomedListener, |                     : null, | ||||||
|               onSwipeDown: () => AutoRouter.of(context).pop(), |                 authToken: authToken, | ||||||
|               onSwipeUp: () => showInfo(), |                 isZoomedFunction: isZoomedFunction, | ||||||
|             ), |                 isZoomedListener: isZoomedListener, | ||||||
|  |                 onSwipeDown: () => AutoRouter.of(context).pop(), | ||||||
|  |                 onSwipeUp: () => showInfo(), | ||||||
|  |                 onLoadingCompleted: onLoadingCompleted, | ||||||
|  |                 onLoadingStart: onLoadingStart), | ||||||
|           ), |           ), | ||||||
|         ), |         ), | ||||||
|         if (downloadAssetStatus == DownloadAssetStatus.loading) |         if (downloadAssetStatus == DownloadAssetStatus.loading) | ||||||
|   | |||||||
| @@ -9,6 +9,7 @@ import 'package:immich_mobile/constants/hive_box.dart'; | |||||||
| import 'package:immich_mobile/modules/home/providers/home_page_state.provider.dart'; | import 'package:immich_mobile/modules/home/providers/home_page_state.provider.dart'; | ||||||
| import 'package:immich_mobile/modules/login/providers/authentication.provider.dart'; | import 'package:immich_mobile/modules/login/providers/authentication.provider.dart'; | ||||||
| import 'package:immich_mobile/routing/router.dart'; | import 'package:immich_mobile/routing/router.dart'; | ||||||
|  | import 'package:immich_mobile/utils/image_url_builder.dart'; | ||||||
| import 'package:openapi/api.dart'; | import 'package:openapi/api.dart'; | ||||||
|  |  | ||||||
| class ThumbnailImage extends HookConsumerWidget { | class ThumbnailImage extends HookConsumerWidget { | ||||||
| @@ -23,8 +24,7 @@ class ThumbnailImage extends HookConsumerWidget { | |||||||
|     final cacheKey = useState(1); |     final cacheKey = useState(1); | ||||||
|  |  | ||||||
|     var box = Hive.box(userInfoBox); |     var box = Hive.box(userInfoBox); | ||||||
|     var thumbnailRequestUrl = |     var thumbnailRequestUrl = getThumbnailUrl(asset); | ||||||
|         '${box.get(serverEndpointKey)}/asset/thumbnail/${asset.id}'; |  | ||||||
|     var selectedAsset = ref.watch(homePageStateProvider).selectedItems; |     var selectedAsset = ref.watch(homePageStateProvider).selectedItems; | ||||||
|     var isMultiSelectEnable = |     var isMultiSelectEnable = | ||||||
|         ref.watch(homePageStateProvider).isMultiSelectEnable; |         ref.watch(homePageStateProvider).isMultiSelectEnable; | ||||||
| @@ -65,7 +65,6 @@ class ThumbnailImage extends HookConsumerWidget { | |||||||
|           AutoRouter.of(context).push( |           AutoRouter.of(context).push( | ||||||
|             GalleryViewerRoute( |             GalleryViewerRoute( | ||||||
|               assetList: assetList, |               assetList: assetList, | ||||||
|               thumbnailRequestUrl: thumbnailRequestUrl, |  | ||||||
|               asset: asset, |               asset: asset, | ||||||
|             ), |             ), | ||||||
|           ); |           ); | ||||||
|   | |||||||
| @@ -76,6 +76,7 @@ class HomePage extends HookConsumerWidget { | |||||||
|  |  | ||||||
|           imageGridGroup.add( |           imageGridGroup.add( | ||||||
|             DailyTitleText( |             DailyTitleText( | ||||||
|  |               key: Key('${dateGroup.toString()}title'), | ||||||
|               isoDate: dateGroup, |               isoDate: dateGroup, | ||||||
|               assetGroup: immichAssetList, |               assetGroup: immichAssetList, | ||||||
|             ), |             ), | ||||||
|   | |||||||
| @@ -46,10 +46,7 @@ class _$AppRouter extends RootStackRouter { | |||||||
|       return MaterialPageX<dynamic>( |       return MaterialPageX<dynamic>( | ||||||
|           routeData: routeData, |           routeData: routeData, | ||||||
|           child: GalleryViewerPage( |           child: GalleryViewerPage( | ||||||
|               key: args.key, |               key: args.key, assetList: args.assetList, asset: args.asset)); | ||||||
|               assetList: args.assetList, |  | ||||||
|               asset: args.asset, |  | ||||||
|               thumbnailRequestUrl: args.thumbnailRequestUrl)); |  | ||||||
|     }, |     }, | ||||||
|     ImageViewerRoute.name: (routeData) { |     ImageViewerRoute.name: (routeData) { | ||||||
|       final args = routeData.argsAs<ImageViewerRouteArgs>(); |       final args = routeData.argsAs<ImageViewerRouteArgs>(); | ||||||
| @@ -57,13 +54,14 @@ class _$AppRouter extends RootStackRouter { | |||||||
|           routeData: routeData, |           routeData: routeData, | ||||||
|           child: ImageViewerPage( |           child: ImageViewerPage( | ||||||
|               key: args.key, |               key: args.key, | ||||||
|               imageUrl: args.imageUrl, |  | ||||||
|               heroTag: args.heroTag, |               heroTag: args.heroTag, | ||||||
|               thumbnailUrl: args.thumbnailUrl, |  | ||||||
|               asset: args.asset, |               asset: args.asset, | ||||||
|               authToken: args.authToken, |               authToken: args.authToken, | ||||||
|               isZoomedFunction: args.isZoomedFunction, |               isZoomedFunction: args.isZoomedFunction, | ||||||
|               isZoomedListener: args.isZoomedListener)); |               isZoomedListener: args.isZoomedListener, | ||||||
|  |               onLoadingCompleted: args.onLoadingCompleted, | ||||||
|  |               onLoadingStart: args.onLoadingStart, | ||||||
|  |               threeStageLoading: args.threeStageLoading)); | ||||||
|     }, |     }, | ||||||
|     VideoViewerRoute.name: (routeData) { |     VideoViewerRoute.name: (routeData) { | ||||||
|       final args = routeData.argsAs<VideoViewerRouteArgs>(); |       final args = routeData.argsAs<VideoViewerRouteArgs>(); | ||||||
| @@ -258,25 +256,18 @@ class GalleryViewerRoute extends PageRouteInfo<GalleryViewerRouteArgs> { | |||||||
|   GalleryViewerRoute( |   GalleryViewerRoute( | ||||||
|       {Key? key, |       {Key? key, | ||||||
|       required List<AssetResponseDto> assetList, |       required List<AssetResponseDto> assetList, | ||||||
|       required AssetResponseDto asset, |       required AssetResponseDto asset}) | ||||||
|       required String thumbnailRequestUrl}) |  | ||||||
|       : super(GalleryViewerRoute.name, |       : super(GalleryViewerRoute.name, | ||||||
|             path: '/gallery-viewer-page', |             path: '/gallery-viewer-page', | ||||||
|             args: GalleryViewerRouteArgs( |             args: GalleryViewerRouteArgs( | ||||||
|                 key: key, |                 key: key, assetList: assetList, asset: asset)); | ||||||
|                 assetList: assetList, |  | ||||||
|                 asset: asset, |  | ||||||
|                 thumbnailRequestUrl: thumbnailRequestUrl)); |  | ||||||
|  |  | ||||||
|   static const String name = 'GalleryViewerRoute'; |   static const String name = 'GalleryViewerRoute'; | ||||||
| } | } | ||||||
|  |  | ||||||
| class GalleryViewerRouteArgs { | class GalleryViewerRouteArgs { | ||||||
|   const GalleryViewerRouteArgs( |   const GalleryViewerRouteArgs( | ||||||
|       {this.key, |       {this.key, required this.assetList, required this.asset}); | ||||||
|       required this.assetList, |  | ||||||
|       required this.asset, |  | ||||||
|       required this.thumbnailRequestUrl}); |  | ||||||
|  |  | ||||||
|   final Key? key; |   final Key? key; | ||||||
|  |  | ||||||
| @@ -284,11 +275,9 @@ class GalleryViewerRouteArgs { | |||||||
|  |  | ||||||
|   final AssetResponseDto asset; |   final AssetResponseDto asset; | ||||||
|  |  | ||||||
|   final String thumbnailRequestUrl; |  | ||||||
|  |  | ||||||
|   @override |   @override | ||||||
|   String toString() { |   String toString() { | ||||||
|     return 'GalleryViewerRouteArgs{key: $key, assetList: $assetList, asset: $asset, thumbnailRequestUrl: $thumbnailRequestUrl}'; |     return 'GalleryViewerRouteArgs{key: $key, assetList: $assetList, asset: $asset}'; | ||||||
|   } |   } | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -297,24 +286,26 @@ class GalleryViewerRouteArgs { | |||||||
| class ImageViewerRoute extends PageRouteInfo<ImageViewerRouteArgs> { | class ImageViewerRoute extends PageRouteInfo<ImageViewerRouteArgs> { | ||||||
|   ImageViewerRoute( |   ImageViewerRoute( | ||||||
|       {Key? key, |       {Key? key, | ||||||
|       required String imageUrl, |  | ||||||
|       required String heroTag, |       required String heroTag, | ||||||
|       required String thumbnailUrl, |  | ||||||
|       required AssetResponseDto asset, |       required AssetResponseDto asset, | ||||||
|       required String authToken, |       required String authToken, | ||||||
|       required void Function() isZoomedFunction, |       required void Function() isZoomedFunction, | ||||||
|       required ValueNotifier<bool> isZoomedListener}) |       required ValueNotifier<bool> isZoomedListener, | ||||||
|  |       required void Function() onLoadingCompleted, | ||||||
|  |       required void Function() onLoadingStart, | ||||||
|  |       required bool threeStageLoading}) | ||||||
|       : super(ImageViewerRoute.name, |       : super(ImageViewerRoute.name, | ||||||
|             path: '/image-viewer-page', |             path: '/image-viewer-page', | ||||||
|             args: ImageViewerRouteArgs( |             args: ImageViewerRouteArgs( | ||||||
|                 key: key, |                 key: key, | ||||||
|                 imageUrl: imageUrl, |  | ||||||
|                 heroTag: heroTag, |                 heroTag: heroTag, | ||||||
|                 thumbnailUrl: thumbnailUrl, |  | ||||||
|                 asset: asset, |                 asset: asset, | ||||||
|                 authToken: authToken, |                 authToken: authToken, | ||||||
|                 isZoomedFunction: isZoomedFunction, |                 isZoomedFunction: isZoomedFunction, | ||||||
|                 isZoomedListener: isZoomedListener)); |                 isZoomedListener: isZoomedListener, | ||||||
|  |                 onLoadingCompleted: onLoadingCompleted, | ||||||
|  |                 onLoadingStart: onLoadingStart, | ||||||
|  |                 threeStageLoading: threeStageLoading)); | ||||||
|  |  | ||||||
|   static const String name = 'ImageViewerRoute'; |   static const String name = 'ImageViewerRoute'; | ||||||
| } | } | ||||||
| @@ -322,22 +313,19 @@ class ImageViewerRoute extends PageRouteInfo<ImageViewerRouteArgs> { | |||||||
| class ImageViewerRouteArgs { | class ImageViewerRouteArgs { | ||||||
|   const ImageViewerRouteArgs( |   const ImageViewerRouteArgs( | ||||||
|       {this.key, |       {this.key, | ||||||
|       required this.imageUrl, |  | ||||||
|       required this.heroTag, |       required this.heroTag, | ||||||
|       required this.thumbnailUrl, |  | ||||||
|       required this.asset, |       required this.asset, | ||||||
|       required this.authToken, |       required this.authToken, | ||||||
|       required this.isZoomedFunction, |       required this.isZoomedFunction, | ||||||
|       required this.isZoomedListener}); |       required this.isZoomedListener, | ||||||
|  |       required this.onLoadingCompleted, | ||||||
|  |       required this.onLoadingStart, | ||||||
|  |       required this.threeStageLoading}); | ||||||
|  |  | ||||||
|   final Key? key; |   final Key? key; | ||||||
|  |  | ||||||
|   final String imageUrl; |  | ||||||
|  |  | ||||||
|   final String heroTag; |   final String heroTag; | ||||||
|  |  | ||||||
|   final String thumbnailUrl; |  | ||||||
|  |  | ||||||
|   final AssetResponseDto asset; |   final AssetResponseDto asset; | ||||||
|  |  | ||||||
|   final String authToken; |   final String authToken; | ||||||
| @@ -346,9 +334,15 @@ class ImageViewerRouteArgs { | |||||||
|  |  | ||||||
|   final ValueNotifier<bool> isZoomedListener; |   final ValueNotifier<bool> isZoomedListener; | ||||||
|  |  | ||||||
|  |   final void Function() onLoadingCompleted; | ||||||
|  |  | ||||||
|  |   final void Function() onLoadingStart; | ||||||
|  |  | ||||||
|  |   final bool threeStageLoading; | ||||||
|  |  | ||||||
|   @override |   @override | ||||||
|   String toString() { |   String toString() { | ||||||
|     return 'ImageViewerRouteArgs{key: $key, imageUrl: $imageUrl, heroTag: $heroTag, thumbnailUrl: $thumbnailUrl, asset: $asset, authToken: $authToken, isZoomedFunction: $isZoomedFunction, isZoomedListener: $isZoomedListener}'; |     return 'ImageViewerRouteArgs{key: $key, heroTag: $heroTag, asset: $asset, authToken: $authToken, isZoomedFunction: $isZoomedFunction, isZoomedListener: $isZoomedListener, onLoadingCompleted: $onLoadingCompleted, onLoadingStart: $onLoadingStart, threeStageLoading: $threeStageLoading}'; | ||||||
|   } |   } | ||||||
| } | } | ||||||
|  |  | ||||||
|   | |||||||
							
								
								
									
										16
									
								
								mobile/lib/utils/image_url_builder.dart
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								mobile/lib/utils/image_url_builder.dart
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,16 @@ | |||||||
|  | import 'package:hive/hive.dart'; | ||||||
|  | import 'package:openapi/api.dart'; | ||||||
|  |  | ||||||
|  | import '../constants/hive_box.dart'; | ||||||
|  |  | ||||||
|  | String getThumbnailUrl(final AssetResponseDto asset, | ||||||
|  |     {ThumbnailFormat type = ThumbnailFormat.WEBP}) { | ||||||
|  |   final box = Hive.box(userInfoBox); | ||||||
|  |  | ||||||
|  |   return '${box.get(serverEndpointKey)}/asset/thumbnail/${asset.id}?format=${type.value}'; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | String getImageUrl(final AssetResponseDto asset) { | ||||||
|  |   final box = Hive.box(userInfoBox); | ||||||
|  |   return '${box.get(serverEndpointKey)}/asset/file?aid=${asset.deviceAssetId}&did=${asset.deviceId}&isThumb=false'; | ||||||
|  | } | ||||||
| @@ -328,6 +328,13 @@ packages: | |||||||
|       url: "https://pub.dartlang.org" |       url: "https://pub.dartlang.org" | ||||||
|     source: hosted |     source: hosted | ||||||
|     version: "3.3.0" |     version: "3.3.0" | ||||||
|  |   flutter_displaymode: | ||||||
|  |     dependency: "direct main" | ||||||
|  |     description: | ||||||
|  |       name: flutter_displaymode | ||||||
|  |       url: "https://pub.dartlang.org" | ||||||
|  |     source: hosted | ||||||
|  |     version: "0.4.0" | ||||||
|   flutter_hooks: |   flutter_hooks: | ||||||
|     dependency: "direct main" |     dependency: "direct main" | ||||||
|     description: |     description: | ||||||
|   | |||||||
| @@ -41,6 +41,7 @@ dependencies: | |||||||
|   http: 0.13.4 |   http: 0.13.4 | ||||||
|   cancellation_token_http: ^1.1.0 |   cancellation_token_http: ^1.1.0 | ||||||
|   easy_localization: ^3.0.1 |   easy_localization: ^3.0.1 | ||||||
|  |   flutter_displaymode: ^0.4.0 | ||||||
|  |  | ||||||
|   path: ^1.8.1 |   path: ^1.8.1 | ||||||
|   path_provider: ^2.0.11 |   path_provider: ^2.0.11 | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user