fix: bring back timeline args auto-scoping (#26219)

Co-authored-by: shenlong-tanwen <139912620+shalong-tanwen@users.noreply.github.com>
This commit is contained in:
shenlong
2026-02-16 19:50:28 +05:30
committed by GitHub
parent 156e3479fa
commit 9211013996

View File

@@ -29,7 +29,38 @@ import 'package:immich_mobile/widgets/common/immich_sliver_app_bar.dart';
import 'package:immich_mobile/widgets/common/mesmerizing_sliver_app_bar.dart'; import 'package:immich_mobile/widgets/common/mesmerizing_sliver_app_bar.dart';
import 'package:immich_mobile/widgets/common/selection_sliver_app_bar.dart'; import 'package:immich_mobile/widgets/common/selection_sliver_app_bar.dart';
class Timeline extends ConsumerWidget { class _TimelineRestorationState extends ChangeNotifier {
int? _restoreAssetIndex;
bool _shouldRestoreAssetPosition = false;
int? get restoreAssetIndex => _restoreAssetIndex;
bool get shouldRestoreAssetPosition => _shouldRestoreAssetPosition;
void setRestoreAssetIndex(int? index) {
_restoreAssetIndex = index;
notifyListeners();
}
void setShouldRestoreAssetPosition(bool should) {
_shouldRestoreAssetPosition = should;
notifyListeners();
}
void clearRestoreAssetIndex() {
_restoreAssetIndex = null;
notifyListeners();
}
}
class _TimelineRestorationProvider extends InheritedNotifier<_TimelineRestorationState> {
const _TimelineRestorationProvider({required super.notifier, required super.child});
static _TimelineRestorationState of(BuildContext context) {
return context.dependOnInheritedWidgetOfExactType<_TimelineRestorationProvider>()!.notifier!;
}
}
class Timeline extends StatefulWidget {
const Timeline({ const Timeline({
super.key, super.key,
this.topSliverWidget, this.topSliverWidget,
@@ -58,36 +89,66 @@ class Timeline extends ConsumerWidget {
final bool readOnly; final bool readOnly;
@override @override
Widget build(BuildContext context, WidgetRef ref) { State<Timeline> createState() => _TimelineState();
}
class _TimelineState extends State<Timeline> {
double? _lastWidth;
late final _TimelineRestorationState _restorationState;
@override
void initState() {
super.initState();
_restorationState = _TimelineRestorationState();
}
@override
void dispose() {
_restorationState.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold( return Scaffold(
resizeToAvoidBottomInset: false, resizeToAvoidBottomInset: false,
floatingActionButton: const DownloadStatusFloatingButton(), floatingActionButton: const DownloadStatusFloatingButton(),
body: LayoutBuilder( body: LayoutBuilder(
builder: (_, constraints) => ProviderScope( builder: (_, constraints) {
if (_lastWidth != null && _lastWidth != constraints.maxWidth) {
_restorationState.setShouldRestoreAssetPosition(true);
}
_lastWidth = constraints.maxWidth;
return _TimelineRestorationProvider(
notifier: _restorationState,
child: ProviderScope(
key: ValueKey(_lastWidth),
overrides: [ overrides: [
timelineArgsProvider.overrideWithValue( timelineArgsProvider.overrideWith(
TimelineArgs( (ref) => TimelineArgs(
maxWidth: constraints.maxWidth, maxWidth: constraints.maxWidth,
maxHeight: constraints.maxHeight, maxHeight: constraints.maxHeight,
columnCount: ref.watch(settingsProvider.select((s) => s.get(Setting.tilesPerRow))), columnCount: ref.watch(settingsProvider.select((s) => s.get(Setting.tilesPerRow))),
showStorageIndicator: showStorageIndicator, showStorageIndicator: widget.showStorageIndicator,
withStack: withStack, withStack: widget.withStack,
groupBy: groupBy, groupBy: widget.groupBy,
), ),
), ),
if (readOnly) readonlyModeProvider.overrideWith(() => _AlwaysReadOnlyNotifier()), if (widget.readOnly) readonlyModeProvider.overrideWith(() => _AlwaysReadOnlyNotifier()),
], ],
child: _SliverTimeline( child: _SliverTimeline(
key: const ValueKey('_sliver_timeline'), key: const ValueKey('_sliver_timeline'),
topSliverWidget: topSliverWidget, topSliverWidget: widget.topSliverWidget,
topSliverWidgetHeight: topSliverWidgetHeight, topSliverWidgetHeight: widget.topSliverWidgetHeight,
appBar: appBar, appBar: widget.appBar,
bottomSheet: bottomSheet, bottomSheet: widget.bottomSheet,
withScrubber: withScrubber, withScrubber: widget.withScrubber,
snapToMonth: snapToMonth, snapToMonth: widget.snapToMonth,
initialScrollOffset: initialScrollOffset, initialScrollOffset: widget.initialScrollOffset,
), ),
), ),
);
},
), ),
); );
} }
@@ -141,7 +202,6 @@ class _SliverTimelineState extends ConsumerState<_SliverTimeline> {
int _perRow = 4; int _perRow = 4;
double _scaleFactor = 3.0; double _scaleFactor = 3.0;
double _baseScaleFactor = 3.0; double _baseScaleFactor = 3.0;
int? _restoreAssetIndex;
@override @override
void initState() { void initState() {
@@ -182,13 +242,16 @@ class _SliverTimelineState extends ConsumerState<_SliverTimeline> {
} }
void _restoreAssetPosition(_) { void _restoreAssetPosition(_) {
if (_restoreAssetIndex == null) return; final restorationState = _TimelineRestorationProvider.of(context);
if (!restorationState.shouldRestoreAssetPosition || restorationState.restoreAssetIndex == null) return;
final asyncSegments = ref.read(timelineSegmentProvider); final asyncSegments = ref.read(timelineSegmentProvider);
asyncSegments.whenData((segments) { asyncSegments.whenData((segments) {
final targetSegment = segments.lastWhereOrNull((segment) => segment.firstAssetIndex <= _restoreAssetIndex!); final targetSegment = segments.lastWhereOrNull(
(segment) => segment.firstAssetIndex <= restorationState.restoreAssetIndex!,
);
if (targetSegment != null) { if (targetSegment != null) {
final assetIndexInSegment = _restoreAssetIndex! - targetSegment.firstAssetIndex; final assetIndexInSegment = restorationState.restoreAssetIndex! - targetSegment.firstAssetIndex;
final newColumnCount = ref.read(timelineArgsProvider).columnCount; final newColumnCount = ref.read(timelineArgsProvider).columnCount;
final rowIndexInSegment = (assetIndexInSegment / newColumnCount).floor(); final rowIndexInSegment = (assetIndexInSegment / newColumnCount).floor();
final targetRowIndex = targetSegment.firstIndex + 1 + rowIndexInSegment; final targetRowIndex = targetSegment.firstIndex + 1 + rowIndexInSegment;
@@ -200,7 +263,7 @@ class _SliverTimelineState extends ConsumerState<_SliverTimeline> {
}); });
} }
}); });
_restoreAssetIndex = null; restorationState.clearRestoreAssetIndex();
} }
int? _getCurrentAssetIndex(List<Segment> segments) { int? _getCurrentAssetIndex(List<Segment> segments) {
@@ -411,7 +474,7 @@ class _SliverTimelineState extends ConsumerState<_SliverTimeline> {
onNotification: (notification) { onNotification: (notification) {
final currentIndex = _getCurrentAssetIndex(segments); final currentIndex = _getCurrentAssetIndex(segments);
if (currentIndex != null && mounted) { if (currentIndex != null && mounted) {
_restoreAssetIndex = currentIndex; _TimelineRestorationProvider.of(context).setRestoreAssetIndex(currentIndex);
} }
return false; return false;
}, },
@@ -430,12 +493,14 @@ class _SliverTimelineState extends ConsumerState<_SliverTimeline> {
final targetAssetIndex = _getCurrentAssetIndex(segments); final targetAssetIndex = _getCurrentAssetIndex(segments);
if (newPerRow != _perRow) { if (newPerRow != _perRow) {
final restorationState = _TimelineRestorationProvider.of(context);
setState(() { setState(() {
_scaleFactor = newScaleFactor; _scaleFactor = newScaleFactor;
_perRow = newPerRow; _perRow = newPerRow;
_restoreAssetIndex = targetAssetIndex;
}); });
restorationState.setRestoreAssetIndex(targetAssetIndex);
restorationState.setShouldRestoreAssetPosition(true);
ref.read(settingsProvider.notifier).set(Setting.tilesPerRow, _perRow); ref.read(settingsProvider.notifier).set(Setting.tilesPerRow, _perRow);
} }
}; };