You've already forked immich
mirror of
https://github.com/immich-app/immich.git
synced 2025-08-10 23:22:22 +02:00
refactor: reduce timeline rebuilds (#19704)
* reduce timeline rebuilds * feat: adds bottom sheet map and actions (#19692) * adds bottom sheet map and actions * PR feedbacks * only reload the asset viewer if asset is changed * styling tweak --------- Co-authored-by: shenlong-tanwen <139912620+shalong-tanwen@users.noreply.github.com> Co-authored-by: Alex <alex.tran1502@gmail.com> * rename singleton and remove event prefix --------- Co-authored-by: shenlong-tanwen <139912620+shalong-tanwen@users.noreply.github.com> Co-authored-by: Alex <alex.tran1502@gmail.com>
This commit is contained in:
@@ -4,9 +4,10 @@ import 'package:auto_route/auto_route.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:immich_mobile/domain/services/timeline.service.dart';
|
||||
import 'package:immich_mobile/domain/utils/event_stream.dart';
|
||||
import 'package:immich_mobile/extensions/build_context_extensions.dart';
|
||||
import 'package:immich_mobile/extensions/scroll_extensions.dart';
|
||||
import 'package:immich_mobile/presentation/widgets/asset_viewer/bottom_sheet.dart';
|
||||
import 'package:immich_mobile/presentation/widgets/asset_viewer/bottom_sheet.widget.dart';
|
||||
import 'package:immich_mobile/presentation/widgets/images/image_provider.dart';
|
||||
import 'package:immich_mobile/presentation/widgets/images/thumbnail.widget.dart';
|
||||
import 'package:immich_mobile/providers/infrastructure/asset_viewer/current_asset.provider.dart';
|
||||
@@ -60,6 +61,7 @@ class _AssetViewerState extends ConsumerState<AssetViewer> {
|
||||
PersistentBottomSheetController? sheetCloseNotifier;
|
||||
// PhotoViewGallery takes care of disposing it's controllers
|
||||
PhotoViewControllerBase? viewController;
|
||||
StreamSubscription? _reloadSubscription;
|
||||
|
||||
late Platform platform;
|
||||
late PhotoViewControllerValue initialPhotoViewState;
|
||||
@@ -88,6 +90,8 @@ class _AssetViewerState extends ConsumerState<AssetViewer> {
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
_onAssetChanged(widget.initialIndex);
|
||||
});
|
||||
_reloadSubscription =
|
||||
EventStream.shared.listen<TimelineReloadEvent>(_onTimelineReload);
|
||||
}
|
||||
|
||||
@override
|
||||
@@ -95,9 +99,31 @@ class _AssetViewerState extends ConsumerState<AssetViewer> {
|
||||
pageController.dispose();
|
||||
bottomSheetController.dispose();
|
||||
_cancelTimers();
|
||||
_reloadSubscription?.cancel();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
void _onTimelineReload(_) {
|
||||
setState(() {
|
||||
totalAssets = ref.read(timelineServiceProvider).totalAssets;
|
||||
if (totalAssets == 0) {
|
||||
context.maybePop();
|
||||
return;
|
||||
}
|
||||
|
||||
final index = pageController.page?.round() ?? 0;
|
||||
final newAsset = ref.read(timelineServiceProvider).getAsset(index);
|
||||
final currentAsset = ref.read(currentAssetNotifier);
|
||||
// Do not reload / close the bottom sheet if the asset has not changed
|
||||
if (newAsset.heroTag == currentAsset.heroTag) {
|
||||
return;
|
||||
}
|
||||
|
||||
_onAssetChanged(pageController.page!.round());
|
||||
sheetCloseNotifier?.close();
|
||||
});
|
||||
}
|
||||
|
||||
Color get backgroundColor {
|
||||
if (showingBottomSheet && !context.isDarkTheme) {
|
||||
return Colors.white;
|
||||
@@ -186,11 +212,12 @@ class _AssetViewerState extends ConsumerState<AssetViewer> {
|
||||
void _onDragStart(
|
||||
_,
|
||||
DragStartDetails details,
|
||||
PhotoViewControllerValue value,
|
||||
PhotoViewControllerBase controller,
|
||||
PhotoViewScaleStateController scaleStateController,
|
||||
) {
|
||||
viewController = controller;
|
||||
dragDownPosition = details.localPosition;
|
||||
initialPhotoViewState = value;
|
||||
initialPhotoViewState = controller.value;
|
||||
final isZoomed =
|
||||
scaleStateController.scaleState == PhotoViewScaleState.zoomedIn ||
|
||||
scaleStateController.scaleState == PhotoViewScaleState.covering;
|
||||
@@ -277,7 +304,7 @@ class _AssetViewerState extends ConsumerState<AssetViewer> {
|
||||
void _openBottomSheet(BuildContext ctx) {
|
||||
setState(() {
|
||||
initialScale = viewController?.scale;
|
||||
viewController?.animateMultiple(scale: _getScaleForBottomSheet);
|
||||
viewController?.updateMultiple(scale: _getScaleForBottomSheet);
|
||||
showingBottomSheet = true;
|
||||
previousExtent = _kBottomSheetMinimumExtent;
|
||||
sheetCloseNotifier = showBottomSheet(
|
||||
@@ -308,10 +335,8 @@ class _AssetViewerState extends ConsumerState<AssetViewer> {
|
||||
setState(() {
|
||||
showingBottomSheet = false;
|
||||
sheetCloseNotifier = null;
|
||||
viewController?.animateMultiple(
|
||||
position: Offset.zero,
|
||||
scale: initialScale,
|
||||
);
|
||||
viewController?.animateMultiple(position: Offset.zero);
|
||||
viewController?.updateMultiple(scale: initialScale);
|
||||
shouldPopOnDrag = false;
|
||||
hasDraggedDown = null;
|
||||
});
|
||||
@@ -420,10 +445,10 @@ class _AssetViewerState extends ConsumerState<AssetViewer> {
|
||||
PhotoViewGalleryPageOptions _assetBuilder(BuildContext ctx, int index) {
|
||||
final asset = ref.read(timelineServiceProvider).getAsset(index);
|
||||
final size = Size(ctx.width, ctx.height);
|
||||
final imageProvider = getFullImageProvider(asset, size: size);
|
||||
|
||||
return PhotoViewGalleryPageOptions(
|
||||
imageProvider: imageProvider,
|
||||
key: ValueKey(asset.heroTag),
|
||||
imageProvider: getFullImageProvider(asset, size: size),
|
||||
heroAttributes: PhotoViewHeroAttributes(tag: asset.heroTag),
|
||||
filterQuality: FilterQuality.high,
|
||||
tightMode: true,
|
||||
@@ -446,27 +471,34 @@ class _AssetViewerState extends ConsumerState<AssetViewer> {
|
||||
);
|
||||
}
|
||||
|
||||
void _onPop<T>(bool didPop, T? result) {
|
||||
ref.read(currentAssetNotifier.notifier).dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
// Currently it is not possible to scroll the asset when the bottom sheet is open all the way.
|
||||
// Issue: https://github.com/flutter/flutter/issues/109037
|
||||
// TODO: Add a custom scrum builder once the fix lands on stable
|
||||
return Scaffold(
|
||||
backgroundColor: Colors.black.withAlpha(backgroundOpacity),
|
||||
body: PhotoViewGallery.builder(
|
||||
gaplessPlayback: true,
|
||||
loadingBuilder: _placeholderBuilder,
|
||||
pageController: pageController,
|
||||
scrollPhysics: platform.isIOS
|
||||
? const FastScrollPhysics() // Use bouncing physics for iOS
|
||||
: const FastClampingScrollPhysics() // Use heavy physics for Android
|
||||
,
|
||||
itemCount: totalAssets,
|
||||
onPageChanged: _onPageChanged,
|
||||
onPageBuild: _onPageBuild,
|
||||
builder: _assetBuilder,
|
||||
backgroundDecoration: BoxDecoration(color: backgroundColor),
|
||||
enablePanAlways: true,
|
||||
return PopScope(
|
||||
onPopInvokedWithResult: _onPop,
|
||||
child: Scaffold(
|
||||
backgroundColor: Colors.black.withAlpha(backgroundOpacity),
|
||||
body: PhotoViewGallery.builder(
|
||||
gaplessPlayback: true,
|
||||
loadingBuilder: _placeholderBuilder,
|
||||
pageController: pageController,
|
||||
scrollPhysics: platform.isIOS
|
||||
? const FastScrollPhysics() // Use bouncing physics for iOS
|
||||
: const FastClampingScrollPhysics() // Use heavy physics for Android
|
||||
,
|
||||
itemCount: totalAssets,
|
||||
onPageChanged: _onPageChanged,
|
||||
onPageBuild: _onPageBuild,
|
||||
builder: _assetBuilder,
|
||||
backgroundDecoration: BoxDecoration(color: backgroundColor),
|
||||
enablePanAlways: true,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
Reference in New Issue
Block a user