feat(mobile): drift search page (#19811)

* feat(mobile): drift search page

* migrate to drift page

---------

Co-authored-by: Alex <alex.tran1502@gmail.com>
This commit is contained in:
Daimolean 2025-07-18 04:25:25 +08:00 committed by GitHub
parent 2046dcc5b4
commit 8491fe459d
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
17 changed files with 1305 additions and 109 deletions

View file

@ -15,6 +15,7 @@ class TimelineArgs {
final double spacing;
final int columnCount;
final bool showStorageIndicator;
final GroupAssetsBy? groupBy;
const TimelineArgs({
required this.maxWidth,
@ -22,6 +23,7 @@ class TimelineArgs {
this.spacing = kTimelineSpacing,
this.columnCount = kTimelineColumnCount,
this.showStorageIndicator = false,
this.groupBy,
});
@override
@ -30,7 +32,8 @@ class TimelineArgs {
maxWidth == other.maxWidth &&
maxHeight == other.maxHeight &&
columnCount == other.columnCount &&
showStorageIndicator == other.showStorageIndicator;
showStorageIndicator == other.showStorageIndicator &&
groupBy == other.groupBy;
}
@override
@ -39,7 +42,8 @@ class TimelineArgs {
maxHeight.hashCode ^
spacing.hashCode ^
columnCount.hashCode ^
showStorageIndicator.hashCode;
showStorageIndicator.hashCode ^
groupBy.hashCode;
}
class TimelineState {
@ -97,8 +101,9 @@ final timelineSegmentProvider = StreamProvider.autoDispose<List<Segment>>(
final availableTileWidth = args.maxWidth - (spacing * (columnCount - 1));
final tileExtent = math.max(0, availableTileWidth) / columnCount;
final groupBy = GroupAssetsBy
.values[ref.watch(settingsProvider).get(Setting.groupAssetsBy)];
final groupBy = args.groupBy ??
GroupAssetsBy
.values[ref.watch(settingsProvider).get(Setting.groupAssetsBy)];
final timelineService = ref.watch(timelineServiceProvider);
yield* timelineService.watchBuckets().map((buckets) {

View file

@ -7,6 +7,7 @@ import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:immich_mobile/domain/models/setting.model.dart';
import 'package:immich_mobile/domain/models/timeline.model.dart';
import 'package:immich_mobile/domain/utils/event_stream.dart';
import 'package:immich_mobile/extensions/asyncvalue_extensions.dart';
import 'package:immich_mobile/extensions/build_context_extensions.dart';
@ -27,8 +28,13 @@ class Timeline extends StatelessWidget {
this.topSliverWidget,
this.topSliverWidgetHeight,
this.showStorageIndicator = false,
this.appBar,
this.appBar = const ImmichSliverAppBar(
floating: true,
pinned: false,
snap: false,
),
this.bottomSheet = const GeneralBottomSheet(),
this.groupBy,
});
final Widget? topSliverWidget;
@ -36,6 +42,8 @@ class Timeline extends StatelessWidget {
final bool showStorageIndicator;
final Widget? appBar;
final Widget? bottomSheet;
final GroupAssetsBy? groupBy;
@override
Widget build(BuildContext context) {
return Scaffold(
@ -50,6 +58,7 @@ class Timeline extends StatelessWidget {
settingsProvider.select((s) => s.get(Setting.tilesPerRow)),
),
showStorageIndicator: showStorageIndicator,
groupBy: groupBy,
),
),
],
@ -112,13 +121,17 @@ class _SliverTimelineState extends ConsumerState<_SliverTimeline> {
return asyncSegments.widgetWhen(
onData: (segments) {
final childCount = (segments.lastOrNull?.lastIndex ?? -1) + 1;
final statusBarHeight = context.padding.top;
final double appBarExpandedHeight =
widget.appBar != null && widget.appBar is MesmerizingSliverAppBar
? 200
: 0;
final totalAppBarHeight = statusBarHeight + kToolbarHeight;
final topPadding = context.padding.top +
(widget.appBar == null ? 0 : kToolbarHeight) +
10;
const scrubberBottomPadding = 100.0;
final bottomPadding = context.padding.bottom +
(widget.appBar == null ? 0 : scrubberBottomPadding);
return PrimaryScrollController(
controller: _scrollController,
@ -127,8 +140,8 @@ class _SliverTimelineState extends ConsumerState<_SliverTimeline> {
Scrubber(
layoutSegments: segments,
timelineHeight: maxHeight,
topPadding: totalAppBarHeight + 10,
bottomPadding: context.padding.bottom + scrubberBottomPadding,
topPadding: topPadding,
bottomPadding: bottomPadding,
monthSegmentSnappingOffset:
widget.topSliverWidgetHeight ?? 0 + appBarExpandedHeight,
child: CustomScrollView(
@ -137,13 +150,8 @@ class _SliverTimelineState extends ConsumerState<_SliverTimeline> {
slivers: [
if (isSelectionMode)
const SelectionSliverAppBar()
else
widget.appBar ??
const ImmichSliverAppBar(
floating: true,
pinned: false,
snap: false,
),
else if (widget.appBar != null)
widget.appBar!,
if (widget.topSliverWidget != null) widget.topSliverWidget!,
_SliverSegmentedList(
segments: segments,
@ -188,21 +196,22 @@ class _SliverTimelineState extends ConsumerState<_SliverTimeline> {
child: _MultiSelectStatusButton(),
),
),
Consumer(
builder: (_, consumerRef, child) {
final isMultiSelectEnabled = consumerRef.watch(
multiSelectProvider.select(
(s) => s.isEnabled,
),
);
if (widget.bottomSheet != null)
Consumer(
builder: (_, consumerRef, child) {
final isMultiSelectEnabled = consumerRef.watch(
multiSelectProvider.select(
(s) => s.isEnabled,
),
);
if (isMultiSelectEnabled) {
return child!;
}
return const SizedBox.shrink();
},
child: widget.bottomSheet,
),
if (isMultiSelectEnabled) {
return child!;
}
return const SizedBox.shrink();
},
child: widget.bottomSheet,
),
],
],
),