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:
Matthias Rupp 2022-08-08 02:43:09 +02:00 committed by GitHub
parent 46f4905259
commit b46e834220
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 159 additions and 81 deletions

View File

@ -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,

View File

@ -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,
), ),
); );
} }

View File

@ -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) =>

View File

@ -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;

View File

@ -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,

View File

@ -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(

View File

@ -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)

View File

@ -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,
), ),
); );

View File

@ -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,
), ),

View File

@ -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}';
} }
} }

View 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';
}

View File

@ -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:

View File

@ -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