mirror of
https://github.com/immich-app/immich.git
synced 2025-05-31 04:06:26 -04: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:
parent
46f4905259
commit
b46e834220
@ -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
|
||||||
|
Loading…
x
Reference in New Issue
Block a user