feat(mobile): configure detail viewer asset loading (#1044)
This commit is contained in:
parent
da87b1256c
commit
424b11cf50
12 changed files with 138 additions and 131 deletions
|
@ -141,6 +141,11 @@
|
||||||
"setting_notifications_total_progress_subtitle": "Overall upload progress (done/total assets)",
|
"setting_notifications_total_progress_subtitle": "Overall upload progress (done/total assets)",
|
||||||
"setting_notifications_single_progress_title": "Show background backup detail progress",
|
"setting_notifications_single_progress_title": "Show background backup detail progress",
|
||||||
"setting_notifications_single_progress_subtitle": "Detailed upload progress information per asset",
|
"setting_notifications_single_progress_subtitle": "Detailed upload progress information per asset",
|
||||||
|
"setting_image_viewer_help": "The detail viewer loads the small thumbnail first, then loads the medium-size preview (if enabled), finally loads the original (if enabled).",
|
||||||
|
"setting_image_viewer_preview_title": "Load preview image",
|
||||||
|
"setting_image_viewer_preview_subtitle": "Enable to load a medium-resolution image. Disable to either directly load the original or only use the thumbnail.",
|
||||||
|
"setting_image_viewer_original_title": "Load original image",
|
||||||
|
"setting_image_viewer_original_subtitle": "Enable to load the original full-resolution image (large!). Disable to reduce data usage (both network and on device cache).",
|
||||||
"setting_pages_app_bar_settings": "Settings",
|
"setting_pages_app_bar_settings": "Settings",
|
||||||
"share_add": "Add",
|
"share_add": "Add",
|
||||||
"share_add_photos": "Add photos",
|
"share_add_photos": "Add photos",
|
||||||
|
@ -165,8 +170,6 @@
|
||||||
"theme_setting_system_theme_switch": "Automatic (Follow system setting)",
|
"theme_setting_system_theme_switch": "Automatic (Follow system setting)",
|
||||||
"theme_setting_theme_subtitle": "Choose the app's theme setting",
|
"theme_setting_theme_subtitle": "Choose the app's theme setting",
|
||||||
"theme_setting_theme_title": "Theme",
|
"theme_setting_theme_title": "Theme",
|
||||||
"theme_setting_three_stage_loading_subtitle": "Three-stage loading might increase the loading performance but causes significantly higher network load",
|
|
||||||
"theme_setting_three_stage_loading_title": "Enable three-stage loading",
|
|
||||||
"version_announcement_overlay_ack": "Acknowledge",
|
"version_announcement_overlay_ack": "Acknowledge",
|
||||||
"version_announcement_overlay_release_notes": "release notes",
|
"version_announcement_overlay_release_notes": "release notes",
|
||||||
"version_announcement_overlay_text_1": "Hi friend, there is a new release of",
|
"version_announcement_overlay_text_1": "Hi friend, there is a new release of",
|
||||||
|
|
|
@ -128,7 +128,7 @@ class _RemotePhotoViewState extends State<RemotePhotoView> {
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
|
||||||
if (widget.threeStageLoading) {
|
if (widget.loadPreview) {
|
||||||
_previewProvider = _authorizedImageProvider(
|
_previewProvider = _authorizedImageProvider(
|
||||||
getThumbnailUrl(widget.asset.remote!, type: ThumbnailFormat.JPEG),
|
getThumbnailUrl(widget.asset.remote!, type: ThumbnailFormat.JPEG),
|
||||||
"${widget.asset.id}_previewStage",
|
"${widget.asset.id}_previewStage",
|
||||||
|
@ -140,6 +140,7 @@ class _RemotePhotoViewState extends State<RemotePhotoView> {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (widget.loadOriginal) {
|
||||||
_fullProvider = _authorizedImageProvider(
|
_fullProvider = _authorizedImageProvider(
|
||||||
getImageUrl(widget.asset.remote!),
|
getImageUrl(widget.asset.remote!),
|
||||||
"${widget.asset.id}_fullStage",
|
"${widget.asset.id}_fullStage",
|
||||||
|
@ -150,6 +151,7 @@ class _RemotePhotoViewState extends State<RemotePhotoView> {
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
|
@ -178,7 +180,8 @@ class RemotePhotoView extends StatefulWidget {
|
||||||
Key? key,
|
Key? key,
|
||||||
required this.asset,
|
required this.asset,
|
||||||
required this.authToken,
|
required this.authToken,
|
||||||
required this.threeStageLoading,
|
required this.loadPreview,
|
||||||
|
required this.loadOriginal,
|
||||||
required this.isZoomedFunction,
|
required this.isZoomedFunction,
|
||||||
required this.isZoomedListener,
|
required this.isZoomedListener,
|
||||||
required this.onSwipeDown,
|
required this.onSwipeDown,
|
||||||
|
@ -187,7 +190,8 @@ class RemotePhotoView extends StatefulWidget {
|
||||||
|
|
||||||
final Asset asset;
|
final Asset asset;
|
||||||
final String authToken;
|
final String authToken;
|
||||||
final bool threeStageLoading;
|
final bool loadPreview;
|
||||||
|
final bool loadOriginal;
|
||||||
final void Function() onSwipeDown;
|
final void Function() onSwipeDown;
|
||||||
final void Function() onSwipeUp;
|
final void Function() onSwipeUp;
|
||||||
final void Function() isZoomedFunction;
|
final void Function() isZoomedFunction;
|
||||||
|
|
|
@ -31,8 +31,9 @@ class GalleryViewerPage extends HookConsumerWidget {
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context, WidgetRef ref) {
|
Widget build(BuildContext context, WidgetRef ref) {
|
||||||
final Box<dynamic> box = Hive.box(userInfoBox);
|
final Box<dynamic> box = Hive.box(userInfoBox);
|
||||||
final appSettingService = ref.watch(appSettingsServiceProvider);
|
final settings = ref.watch(appSettingsServiceProvider);
|
||||||
final threeStageLoading = useState(false);
|
final isLoadPreview = useState(AppSettingsEnum.loadPreview.defaultValue);
|
||||||
|
final isLoadOriginal = useState(AppSettingsEnum.loadOriginal.defaultValue);
|
||||||
final isZoomed = useState<bool>(false);
|
final isZoomed = useState<bool>(false);
|
||||||
final indexOfAsset = useState(assetList.indexOf(asset));
|
final indexOfAsset = useState(assetList.indexOf(asset));
|
||||||
final isPlayingMotionVideo = useState(false);
|
final isPlayingMotionVideo = useState(false);
|
||||||
|
@ -43,8 +44,10 @@ class GalleryViewerPage extends HookConsumerWidget {
|
||||||
|
|
||||||
useEffect(
|
useEffect(
|
||||||
() {
|
() {
|
||||||
threeStageLoading.value = appSettingService
|
isLoadPreview.value =
|
||||||
.getSetting<bool>(AppSettingsEnum.threeStageLoading);
|
settings.getSetting<bool>(AppSettingsEnum.loadPreview);
|
||||||
|
isLoadOriginal.value =
|
||||||
|
settings.getSetting<bool>(AppSettingsEnum.loadOriginal);
|
||||||
isPlayingMotionVideo.value = false;
|
isPlayingMotionVideo.value = false;
|
||||||
return null;
|
return null;
|
||||||
},
|
},
|
||||||
|
@ -140,7 +143,8 @@ class GalleryViewerPage extends HookConsumerWidget {
|
||||||
isZoomedListener: isZoomedListener,
|
isZoomedListener: isZoomedListener,
|
||||||
asset: assetList[index],
|
asset: assetList[index],
|
||||||
heroTag: assetList[index].id,
|
heroTag: assetList[index].id,
|
||||||
threeStageLoading: threeStageLoading.value,
|
loadPreview: isLoadPreview.value,
|
||||||
|
loadOriginal: isLoadOriginal.value,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -17,7 +17,8 @@ class ImageViewerPage extends HookConsumerWidget {
|
||||||
final String authToken;
|
final String authToken;
|
||||||
final ValueNotifier<bool> isZoomedListener;
|
final ValueNotifier<bool> isZoomedListener;
|
||||||
final void Function() isZoomedFunction;
|
final void Function() isZoomedFunction;
|
||||||
final bool threeStageLoading;
|
final bool loadPreview;
|
||||||
|
final bool loadOriginal;
|
||||||
|
|
||||||
ImageViewerPage({
|
ImageViewerPage({
|
||||||
Key? key,
|
Key? key,
|
||||||
|
@ -26,7 +27,8 @@ class ImageViewerPage extends HookConsumerWidget {
|
||||||
required this.authToken,
|
required this.authToken,
|
||||||
required this.isZoomedFunction,
|
required this.isZoomedFunction,
|
||||||
required this.isZoomedListener,
|
required this.isZoomedListener,
|
||||||
required this.threeStageLoading,
|
required this.loadPreview,
|
||||||
|
required this.loadOriginal,
|
||||||
}) : super(key: key);
|
}) : super(key: key);
|
||||||
|
|
||||||
Asset? assetDetail;
|
Asset? assetDetail;
|
||||||
|
@ -74,7 +76,8 @@ class ImageViewerPage extends HookConsumerWidget {
|
||||||
child: RemotePhotoView(
|
child: RemotePhotoView(
|
||||||
asset: asset,
|
asset: asset,
|
||||||
authToken: authToken,
|
authToken: authToken,
|
||||||
threeStageLoading: threeStageLoading,
|
loadPreview: loadPreview,
|
||||||
|
loadOriginal: loadOriginal,
|
||||||
isZoomedFunction: isZoomedFunction,
|
isZoomedFunction: isZoomedFunction,
|
||||||
isZoomedListener: isZoomedListener,
|
isZoomedListener: isZoomedListener,
|
||||||
onSwipeDown: () => AutoRouter.of(context).pop(),
|
onSwipeDown: () => AutoRouter.of(context).pop(),
|
||||||
|
|
|
@ -32,20 +32,20 @@ class AssetService {
|
||||||
AssetService(this._apiService, this._backupService, this._backgroundService);
|
AssetService(this._apiService, this._backupService, this._backgroundService);
|
||||||
|
|
||||||
/// Returns `null` if the server state did not change, else list of assets
|
/// Returns `null` if the server state did not change, else list of assets
|
||||||
Future<List<Asset>?> getRemoteAssets({required bool hasCache}) async {
|
Future<Pair<List<Asset>?, String?>> getRemoteAssets({String? etag}) async {
|
||||||
try {
|
try {
|
||||||
final Box box = Hive.box(userInfoBox);
|
final Pair<List<AssetResponseDto>, String?>? remote =
|
||||||
final Pair<List<AssetResponseDto>, String?>? remote = await _apiService
|
await _apiService.assetApi.getAllAssetsWithETag(eTag: etag);
|
||||||
.assetApi
|
|
||||||
.getAllAssetsWithETag(eTag: hasCache ? box.get(assetEtagKey) : null);
|
|
||||||
if (remote == null) {
|
if (remote == null) {
|
||||||
return null;
|
return const Pair(null, null);
|
||||||
}
|
}
|
||||||
box.put(assetEtagKey, remote.second);
|
return Pair(
|
||||||
return remote.first.map(Asset.remote).toList(growable: false);
|
remote.first.map(Asset.remote).toList(growable: false),
|
||||||
|
remote.second,
|
||||||
|
);
|
||||||
} catch (e, stack) {
|
} catch (e, stack) {
|
||||||
log.severe('Error while getting remote assets', e, stack);
|
log.severe('Error while getting remote assets', e, stack);
|
||||||
return null;
|
return const Pair(null, null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2,7 +2,8 @@ import 'package:hive_flutter/hive_flutter.dart';
|
||||||
import 'package:immich_mobile/constants/hive_box.dart';
|
import 'package:immich_mobile/constants/hive_box.dart';
|
||||||
|
|
||||||
enum AppSettingsEnum<T> {
|
enum AppSettingsEnum<T> {
|
||||||
threeStageLoading<bool>("threeStageLoading", false),
|
loadPreview<bool>("loadPreview", true),
|
||||||
|
loadOriginal<bool>("loadOriginal", false),
|
||||||
themeMode<String>("themeMode", "system"), // "light","dark","system"
|
themeMode<String>("themeMode", "system"), // "light","dark","system"
|
||||||
tilesPerRow<int>("tilesPerRow", 4),
|
tilesPerRow<int>("tilesPerRow", 4),
|
||||||
uploadErrorNotificationGracePeriod<int>(
|
uploadErrorNotificationGracePeriod<int>(
|
||||||
|
|
24
mobile/lib/modules/settings/ui/common.dart
Normal file
24
mobile/lib/modules/settings/ui/common.dart
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:immich_mobile/modules/settings/services/app_settings.service.dart';
|
||||||
|
|
||||||
|
SwitchListTile buildSwitchListTile(
|
||||||
|
BuildContext context,
|
||||||
|
AppSettingsService appSettingService,
|
||||||
|
ValueNotifier<bool> valueNotifier,
|
||||||
|
AppSettingsEnum settingsEnum, {
|
||||||
|
required String title,
|
||||||
|
String? subtitle,
|
||||||
|
}) {
|
||||||
|
return SwitchListTile.adaptive(
|
||||||
|
key: Key(settingsEnum.name),
|
||||||
|
value: valueNotifier.value,
|
||||||
|
onChanged: (value) {
|
||||||
|
valueNotifier.value = value;
|
||||||
|
appSettingService.setSetting(settingsEnum, value);
|
||||||
|
},
|
||||||
|
activeColor: Theme.of(context).primaryColor,
|
||||||
|
dense: true,
|
||||||
|
title: Text(title, style: const TextStyle(fontWeight: FontWeight.bold)),
|
||||||
|
subtitle: subtitle != null ? Text(subtitle) : null,
|
||||||
|
);
|
||||||
|
}
|
|
@ -1,14 +1,30 @@
|
||||||
import 'package:easy_localization/easy_localization.dart';
|
import 'package:easy_localization/easy_localization.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:immich_mobile/modules/settings/ui/image_viewer_quality_setting/three_stage_loading.dart';
|
import 'package:flutter_hooks/flutter_hooks.dart';
|
||||||
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
|
import 'package:immich_mobile/modules/settings/providers/app_settings.provider.dart';
|
||||||
|
import 'package:immich_mobile/modules/settings/services/app_settings.service.dart';
|
||||||
|
import 'package:immich_mobile/modules/settings/ui/common.dart';
|
||||||
|
|
||||||
class ImageViewerQualitySetting extends StatelessWidget {
|
class ImageViewerQualitySetting extends HookConsumerWidget {
|
||||||
const ImageViewerQualitySetting({
|
const ImageViewerQualitySetting({
|
||||||
Key? key,
|
Key? key,
|
||||||
}) : super(key: key);
|
}) : super(key: key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context, WidgetRef ref) {
|
||||||
|
final settings = ref.watch(appSettingsServiceProvider);
|
||||||
|
final isPreview = useState(AppSettingsEnum.loadPreview.defaultValue);
|
||||||
|
final isOriginal = useState(AppSettingsEnum.loadOriginal.defaultValue);
|
||||||
|
|
||||||
|
useEffect(
|
||||||
|
() {
|
||||||
|
isPreview.value = settings.getSetting(AppSettingsEnum.loadPreview);
|
||||||
|
isOriginal.value = settings.getSetting(AppSettingsEnum.loadOriginal);
|
||||||
|
return null;
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
return ExpansionTile(
|
return ExpansionTile(
|
||||||
textColor: Theme.of(context).primaryColor,
|
textColor: Theme.of(context).primaryColor,
|
||||||
title: const Text(
|
title: const Text(
|
||||||
|
@ -23,8 +39,27 @@ class ImageViewerQualitySetting extends StatelessWidget {
|
||||||
fontSize: 13,
|
fontSize: 13,
|
||||||
),
|
),
|
||||||
).tr(),
|
).tr(),
|
||||||
children: const [
|
children: [
|
||||||
ThreeStageLoading(),
|
ListTile(
|
||||||
|
title: const Text('setting_image_viewer_help').tr(),
|
||||||
|
dense: true,
|
||||||
|
),
|
||||||
|
buildSwitchListTile(
|
||||||
|
context,
|
||||||
|
settings,
|
||||||
|
isPreview,
|
||||||
|
AppSettingsEnum.loadPreview,
|
||||||
|
title: "setting_image_viewer_preview_title".tr(),
|
||||||
|
subtitle: "setting_image_viewer_preview_subtitle".tr(),
|
||||||
|
),
|
||||||
|
buildSwitchListTile(
|
||||||
|
context,
|
||||||
|
settings,
|
||||||
|
isOriginal,
|
||||||
|
AppSettingsEnum.loadOriginal,
|
||||||
|
title: "setting_image_viewer_original_title".tr(),
|
||||||
|
subtitle: "setting_image_viewer_original_subtitle".tr(),
|
||||||
|
),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,57 +0,0 @@
|
||||||
import 'package:easy_localization/easy_localization.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:flutter_hooks/flutter_hooks.dart';
|
|
||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
|
||||||
import 'package:immich_mobile/modules/settings/providers/app_settings.provider.dart';
|
|
||||||
import 'package:immich_mobile/modules/settings/services/app_settings.service.dart';
|
|
||||||
|
|
||||||
class ThreeStageLoading extends HookConsumerWidget {
|
|
||||||
const ThreeStageLoading({
|
|
||||||
Key? key,
|
|
||||||
}) : super(key: key);
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context, WidgetRef ref) {
|
|
||||||
final appSettingService = ref.watch(appSettingsServiceProvider);
|
|
||||||
|
|
||||||
final isEnable = useState(false);
|
|
||||||
|
|
||||||
useEffect(
|
|
||||||
() {
|
|
||||||
var isThreeStageLoadingEnable =
|
|
||||||
appSettingService.getSetting(AppSettingsEnum.threeStageLoading);
|
|
||||||
|
|
||||||
isEnable.value = isThreeStageLoadingEnable;
|
|
||||||
return null;
|
|
||||||
},
|
|
||||||
[],
|
|
||||||
);
|
|
||||||
|
|
||||||
void onSwitchChanged(bool switchValue) {
|
|
||||||
appSettingService.setSetting(
|
|
||||||
AppSettingsEnum.threeStageLoading,
|
|
||||||
switchValue,
|
|
||||||
);
|
|
||||||
isEnable.value = switchValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
return SwitchListTile.adaptive(
|
|
||||||
activeColor: Theme.of(context).primaryColor,
|
|
||||||
title: const Text(
|
|
||||||
"theme_setting_three_stage_loading_title",
|
|
||||||
style: TextStyle(
|
|
||||||
fontSize: 12,
|
|
||||||
fontWeight: FontWeight.bold,
|
|
||||||
),
|
|
||||||
).tr(),
|
|
||||||
subtitle: const Text(
|
|
||||||
"theme_setting_three_stage_loading_subtitle",
|
|
||||||
style: TextStyle(
|
|
||||||
fontSize: 12,
|
|
||||||
),
|
|
||||||
).tr(),
|
|
||||||
value: isEnable.value,
|
|
||||||
onChanged: onSwitchChanged,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -4,6 +4,7 @@ import 'package:flutter_hooks/flutter_hooks.dart';
|
||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
import 'package:immich_mobile/modules/settings/providers/app_settings.provider.dart';
|
import 'package:immich_mobile/modules/settings/providers/app_settings.provider.dart';
|
||||||
import 'package:immich_mobile/modules/settings/services/app_settings.service.dart';
|
import 'package:immich_mobile/modules/settings/services/app_settings.service.dart';
|
||||||
|
import 'package:immich_mobile/modules/settings/ui/common.dart';
|
||||||
|
|
||||||
class NotificationSetting extends HookConsumerWidget {
|
class NotificationSetting extends HookConsumerWidget {
|
||||||
const NotificationSetting({
|
const NotificationSetting({
|
||||||
|
@ -50,7 +51,7 @@ class NotificationSetting extends HookConsumerWidget {
|
||||||
),
|
),
|
||||||
).tr(),
|
).tr(),
|
||||||
children: [
|
children: [
|
||||||
_buildSwitchListTile(
|
buildSwitchListTile(
|
||||||
context,
|
context,
|
||||||
appSettingService,
|
appSettingService,
|
||||||
totalProgressValue,
|
totalProgressValue,
|
||||||
|
@ -58,7 +59,7 @@ class NotificationSetting extends HookConsumerWidget {
|
||||||
title: 'setting_notifications_total_progress_title'.tr(),
|
title: 'setting_notifications_total_progress_title'.tr(),
|
||||||
subtitle: 'setting_notifications_total_progress_subtitle'.tr(),
|
subtitle: 'setting_notifications_total_progress_subtitle'.tr(),
|
||||||
),
|
),
|
||||||
_buildSwitchListTile(
|
buildSwitchListTile(
|
||||||
context,
|
context,
|
||||||
appSettingService,
|
appSettingService,
|
||||||
singleProgressValue,
|
singleProgressValue,
|
||||||
|
@ -91,28 +92,6 @@ class NotificationSetting extends HookConsumerWidget {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
SwitchListTile _buildSwitchListTile(
|
|
||||||
BuildContext context,
|
|
||||||
AppSettingsService appSettingService,
|
|
||||||
ValueNotifier<bool> valueNotifier,
|
|
||||||
AppSettingsEnum settingsEnum, {
|
|
||||||
required String title,
|
|
||||||
String? subtitle,
|
|
||||||
}) {
|
|
||||||
return SwitchListTile(
|
|
||||||
key: Key(settingsEnum.name),
|
|
||||||
value: valueNotifier.value,
|
|
||||||
onChanged: (value) {
|
|
||||||
valueNotifier.value = value;
|
|
||||||
appSettingService.setSetting(settingsEnum, value);
|
|
||||||
},
|
|
||||||
activeColor: Theme.of(context).primaryColor,
|
|
||||||
dense: true,
|
|
||||||
title: Text(title, style: const TextStyle(fontWeight: FontWeight.bold)),
|
|
||||||
subtitle: subtitle != null ? Text(subtitle) : null,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
String _formatSliderValue(double v) {
|
String _formatSliderValue(double v) {
|
||||||
if (v == 0.0) {
|
if (v == 0.0) {
|
||||||
return 'setting_notifications_notify_immediately'.tr();
|
return 'setting_notifications_notify_immediately'.tr();
|
||||||
|
|
|
@ -59,7 +59,8 @@ class _$AppRouter extends RootStackRouter {
|
||||||
authToken: args.authToken,
|
authToken: args.authToken,
|
||||||
isZoomedFunction: args.isZoomedFunction,
|
isZoomedFunction: args.isZoomedFunction,
|
||||||
isZoomedListener: args.isZoomedListener,
|
isZoomedListener: args.isZoomedListener,
|
||||||
threeStageLoading: args.threeStageLoading));
|
loadPreview: args.loadPreview,
|
||||||
|
loadOriginal: args.loadOriginal));
|
||||||
},
|
},
|
||||||
VideoViewerRoute.name: (routeData) {
|
VideoViewerRoute.name: (routeData) {
|
||||||
final args = routeData.argsAs<VideoViewerRouteArgs>();
|
final args = routeData.argsAs<VideoViewerRouteArgs>();
|
||||||
|
@ -305,7 +306,8 @@ class ImageViewerRoute extends PageRouteInfo<ImageViewerRouteArgs> {
|
||||||
required String authToken,
|
required String authToken,
|
||||||
required void Function() isZoomedFunction,
|
required void Function() isZoomedFunction,
|
||||||
required ValueNotifier<bool> isZoomedListener,
|
required ValueNotifier<bool> isZoomedListener,
|
||||||
required bool threeStageLoading})
|
required bool loadPreview,
|
||||||
|
required bool loadOriginal})
|
||||||
: super(ImageViewerRoute.name,
|
: super(ImageViewerRoute.name,
|
||||||
path: '/image-viewer-page',
|
path: '/image-viewer-page',
|
||||||
args: ImageViewerRouteArgs(
|
args: ImageViewerRouteArgs(
|
||||||
|
@ -315,7 +317,8 @@ class ImageViewerRoute extends PageRouteInfo<ImageViewerRouteArgs> {
|
||||||
authToken: authToken,
|
authToken: authToken,
|
||||||
isZoomedFunction: isZoomedFunction,
|
isZoomedFunction: isZoomedFunction,
|
||||||
isZoomedListener: isZoomedListener,
|
isZoomedListener: isZoomedListener,
|
||||||
threeStageLoading: threeStageLoading));
|
loadPreview: loadPreview,
|
||||||
|
loadOriginal: loadOriginal));
|
||||||
|
|
||||||
static const String name = 'ImageViewerRoute';
|
static const String name = 'ImageViewerRoute';
|
||||||
}
|
}
|
||||||
|
@ -328,7 +331,8 @@ class ImageViewerRouteArgs {
|
||||||
required this.authToken,
|
required this.authToken,
|
||||||
required this.isZoomedFunction,
|
required this.isZoomedFunction,
|
||||||
required this.isZoomedListener,
|
required this.isZoomedListener,
|
||||||
required this.threeStageLoading});
|
required this.loadPreview,
|
||||||
|
required this.loadOriginal});
|
||||||
|
|
||||||
final Key? key;
|
final Key? key;
|
||||||
|
|
||||||
|
@ -342,11 +346,13 @@ class ImageViewerRouteArgs {
|
||||||
|
|
||||||
final ValueNotifier<bool> isZoomedListener;
|
final ValueNotifier<bool> isZoomedListener;
|
||||||
|
|
||||||
final bool threeStageLoading;
|
final bool loadPreview;
|
||||||
|
|
||||||
|
final bool loadOriginal;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String toString() {
|
String toString() {
|
||||||
return 'ImageViewerRouteArgs{key: $key, heroTag: $heroTag, asset: $asset, authToken: $authToken, isZoomedFunction: $isZoomedFunction, isZoomedListener: $isZoomedListener, threeStageLoading: $threeStageLoading}';
|
return 'ImageViewerRouteArgs{key: $key, heroTag: $heroTag, asset: $asset, authToken: $authToken, isZoomedFunction: $isZoomedFunction, isZoomedListener: $isZoomedListener, loadPreview: $loadPreview, loadOriginal: $loadOriginal}';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -8,6 +8,7 @@ import 'package:immich_mobile/modules/home/services/asset_cache.service.dart';
|
||||||
import 'package:immich_mobile/shared/models/asset.dart';
|
import 'package:immich_mobile/shared/models/asset.dart';
|
||||||
import 'package:immich_mobile/shared/services/device_info.service.dart';
|
import 'package:immich_mobile/shared/services/device_info.service.dart';
|
||||||
import 'package:collection/collection.dart';
|
import 'package:collection/collection.dart';
|
||||||
|
import 'package:immich_mobile/utils/tuple.dart';
|
||||||
import 'package:intl/intl.dart';
|
import 'package:intl/intl.dart';
|
||||||
import 'package:logging/logging.dart';
|
import 'package:logging/logging.dart';
|
||||||
import 'package:openapi/api.dart';
|
import 'package:openapi/api.dart';
|
||||||
|
@ -37,8 +38,11 @@ class AssetNotifier extends StateNotifier<List<Asset>> {
|
||||||
_getAllAssetInProgress = true;
|
_getAllAssetInProgress = true;
|
||||||
final bool isCacheValid = await _assetCacheService.isValid();
|
final bool isCacheValid = await _assetCacheService.isValid();
|
||||||
stopwatch.start();
|
stopwatch.start();
|
||||||
|
final Box box = Hive.box(userInfoBox);
|
||||||
final localTask = _assetService.getLocalAssets(urgent: !isCacheValid);
|
final localTask = _assetService.getLocalAssets(urgent: !isCacheValid);
|
||||||
final remoteTask = _assetService.getRemoteAssets(hasCache: isCacheValid);
|
final remoteTask = _assetService.getRemoteAssets(
|
||||||
|
etag: isCacheValid ? box.get(assetEtagKey) : null,
|
||||||
|
);
|
||||||
if (isCacheValid && state.isEmpty) {
|
if (isCacheValid && state.isEmpty) {
|
||||||
state = await _assetCacheService.get();
|
state = await _assetCacheService.get();
|
||||||
log.info(
|
log.info(
|
||||||
|
@ -50,7 +54,8 @@ class AssetNotifier extends StateNotifier<List<Asset>> {
|
||||||
int remoteBegin = state.indexWhere((a) => a.isRemote);
|
int remoteBegin = state.indexWhere((a) => a.isRemote);
|
||||||
remoteBegin = remoteBegin == -1 ? state.length : remoteBegin;
|
remoteBegin = remoteBegin == -1 ? state.length : remoteBegin;
|
||||||
final List<Asset> currentLocal = state.slice(0, remoteBegin);
|
final List<Asset> currentLocal = state.slice(0, remoteBegin);
|
||||||
List<Asset>? newRemote = await remoteTask;
|
final Pair<List<Asset>?, String?> remoteResult = await remoteTask;
|
||||||
|
List<Asset>? newRemote = remoteResult.first;
|
||||||
List<Asset>? newLocal = await localTask;
|
List<Asset>? newLocal = await localTask;
|
||||||
log.info("Load assets: ${stopwatch.elapsedMilliseconds}ms");
|
log.info("Load assets: ${stopwatch.elapsedMilliseconds}ms");
|
||||||
stopwatch.reset();
|
stopwatch.reset();
|
||||||
|
@ -63,14 +68,14 @@ class AssetNotifier extends StateNotifier<List<Asset>> {
|
||||||
newLocal ??= [];
|
newLocal ??= [];
|
||||||
state = _combineLocalAndRemoteAssets(local: newLocal, remote: newRemote);
|
state = _combineLocalAndRemoteAssets(local: newLocal, remote: newRemote);
|
||||||
log.info("Combining assets: ${stopwatch.elapsedMilliseconds}ms");
|
log.info("Combining assets: ${stopwatch.elapsedMilliseconds}ms");
|
||||||
} finally {
|
|
||||||
_getAllAssetInProgress = false;
|
|
||||||
}
|
|
||||||
log.info("setting new asset state");
|
|
||||||
|
|
||||||
stopwatch.reset();
|
stopwatch.reset();
|
||||||
_cacheState();
|
_cacheState();
|
||||||
|
box.put(assetEtagKey, remoteResult.second);
|
||||||
log.info("Store assets in cache: ${stopwatch.elapsedMilliseconds}ms");
|
log.info("Store assets in cache: ${stopwatch.elapsedMilliseconds}ms");
|
||||||
|
} finally {
|
||||||
|
_getAllAssetInProgress = false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
List<Asset> _combineLocalAndRemoteAssets({
|
List<Asset> _combineLocalAndRemoteAssets({
|
||||||
|
|
Loading…
Reference in a new issue