mirror of
https://github.com/immich-app/immich.git
synced 2025-12-22 17:24:56 +03:00
feat: selection mode timeline (#19734)
* feat: new page * force multi-selection state * fix: provider scoping * Return selected assets * lint * lint * simplify provider scope and drop drilling * selection styling
This commit is contained in:
@@ -166,22 +166,22 @@ class _AssetTileWidget extends ConsumerWidget {
|
||||
BaseAsset asset,
|
||||
) {
|
||||
final multiSelectState = ref.read(multiSelectProvider);
|
||||
if (!multiSelectState.isEnabled) {
|
||||
|
||||
if (multiSelectState.forceEnable || multiSelectState.isEnabled) {
|
||||
ref.read(multiSelectProvider.notifier).toggleAssetSelection(asset);
|
||||
} else {
|
||||
ctx.pushRoute(
|
||||
AssetViewerRoute(
|
||||
initialIndex: assetIndex,
|
||||
timelineService: ref.read(timelineServiceProvider),
|
||||
),
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
ref.read(multiSelectProvider.notifier).toggleAssetSelection(asset);
|
||||
}
|
||||
|
||||
void _handleOnLongPress(WidgetRef ref, BaseAsset asset) {
|
||||
final multiSelectState = ref.read(multiSelectProvider);
|
||||
if (multiSelectState.isEnabled) {
|
||||
if (multiSelectState.isEnabled || multiSelectState.forceEnable) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -189,13 +189,35 @@ class _AssetTileWidget extends ConsumerWidget {
|
||||
ref.read(multiSelectProvider.notifier).toggleAssetSelection(asset);
|
||||
}
|
||||
|
||||
bool _getLockSelectionStatus(WidgetRef ref) {
|
||||
final lockSelectionAssets = ref.read(
|
||||
multiSelectProvider.select(
|
||||
(state) => state.lockedSelectionAssets,
|
||||
),
|
||||
);
|
||||
|
||||
if (lockSelectionAssets.isEmpty) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return lockSelectionAssets.contains(asset);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final lockSelection = _getLockSelectionStatus(ref);
|
||||
|
||||
return RepaintBoundary(
|
||||
child: GestureDetector(
|
||||
onTap: () => _handleOnTap(context, ref, assetIndex, asset),
|
||||
onLongPress: () => _handleOnLongPress(ref, asset),
|
||||
child: ThumbnailTile(asset),
|
||||
onTap: () => lockSelection
|
||||
? null
|
||||
: _handleOnTap(context, ref, assetIndex, asset),
|
||||
onLongPress: () =>
|
||||
lockSelection ? null : _handleOnLongPress(ref, asset),
|
||||
child: ThumbnailTile(
|
||||
asset,
|
||||
lockSelection: lockSelection,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -18,9 +18,14 @@ import 'package:immich_mobile/providers/infrastructure/setting.provider.dart';
|
||||
import 'package:immich_mobile/providers/infrastructure/timeline.provider.dart';
|
||||
import 'package:immich_mobile/providers/timeline/multiselect.provider.dart';
|
||||
import 'package:immich_mobile/widgets/common/immich_sliver_app_bar.dart';
|
||||
import 'package:immich_mobile/widgets/common/selection_sliver_app_bar.dart';
|
||||
|
||||
class Timeline extends StatelessWidget {
|
||||
const Timeline({super.key, this.topSliverWidget, this.topSliverWidgetHeight});
|
||||
const Timeline({
|
||||
super.key,
|
||||
this.topSliverWidget,
|
||||
this.topSliverWidgetHeight,
|
||||
});
|
||||
|
||||
final Widget? topSliverWidget;
|
||||
final double? topSliverWidgetHeight;
|
||||
@@ -52,7 +57,10 @@ class Timeline extends StatelessWidget {
|
||||
}
|
||||
|
||||
class _SliverTimeline extends ConsumerStatefulWidget {
|
||||
const _SliverTimeline({this.topSliverWidget, this.topSliverWidgetHeight});
|
||||
const _SliverTimeline({
|
||||
this.topSliverWidget,
|
||||
this.topSliverWidgetHeight,
|
||||
});
|
||||
|
||||
final Widget? topSliverWidget;
|
||||
final double? topSliverWidgetHeight;
|
||||
@@ -84,6 +92,10 @@ class _SliverTimelineState extends ConsumerState<_SliverTimeline> {
|
||||
final asyncSegments = ref.watch(timelineSegmentProvider);
|
||||
final maxHeight =
|
||||
ref.watch(timelineArgsProvider.select((args) => args.maxHeight));
|
||||
final isSelectionMode = ref.watch(
|
||||
multiSelectProvider.select((s) => s.forceEnable),
|
||||
);
|
||||
|
||||
return asyncSegments.widgetWhen(
|
||||
onData: (segments) {
|
||||
final childCount = (segments.lastOrNull?.lastIndex ?? -1) + 1;
|
||||
@@ -105,11 +117,14 @@ class _SliverTimelineState extends ConsumerState<_SliverTimeline> {
|
||||
primary: true,
|
||||
cacheExtent: maxHeight * 2,
|
||||
slivers: [
|
||||
const ImmichSliverAppBar(
|
||||
floating: true,
|
||||
pinned: false,
|
||||
snap: false,
|
||||
),
|
||||
if (isSelectionMode)
|
||||
const SelectionSliverAppBar()
|
||||
else
|
||||
const ImmichSliverAppBar(
|
||||
floating: true,
|
||||
pinned: false,
|
||||
snap: false,
|
||||
),
|
||||
if (widget.topSliverWidget != null) widget.topSliverWidget!,
|
||||
_SliverSegmentedList(
|
||||
segments: segments,
|
||||
@@ -134,40 +149,42 @@ class _SliverTimelineState extends ConsumerState<_SliverTimeline> {
|
||||
],
|
||||
),
|
||||
),
|
||||
Consumer(
|
||||
builder: (_, consumerRef, child) {
|
||||
final isMultiSelectEnabled = consumerRef.watch(
|
||||
multiSelectProvider.select(
|
||||
(s) => s.isEnabled,
|
||||
),
|
||||
);
|
||||
if (!isSelectionMode) ...[
|
||||
Consumer(
|
||||
builder: (_, consumerRef, child) {
|
||||
final isMultiSelectEnabled = consumerRef.watch(
|
||||
multiSelectProvider.select(
|
||||
(s) => s.isEnabled,
|
||||
),
|
||||
);
|
||||
|
||||
if (isMultiSelectEnabled) {
|
||||
return child!;
|
||||
}
|
||||
return const SizedBox.shrink();
|
||||
},
|
||||
child: const Positioned(
|
||||
top: 60,
|
||||
left: 25,
|
||||
child: _MultiSelectStatusButton(),
|
||||
if (isMultiSelectEnabled) {
|
||||
return child!;
|
||||
}
|
||||
return const SizedBox.shrink();
|
||||
},
|
||||
child: const Positioned(
|
||||
top: 60,
|
||||
left: 25,
|
||||
child: _MultiSelectStatusButton(),
|
||||
),
|
||||
),
|
||||
),
|
||||
Consumer(
|
||||
builder: (_, consumerRef, child) {
|
||||
final isMultiSelectEnabled = consumerRef.watch(
|
||||
multiSelectProvider.select(
|
||||
(s) => s.isEnabled,
|
||||
),
|
||||
);
|
||||
Consumer(
|
||||
builder: (_, consumerRef, child) {
|
||||
final isMultiSelectEnabled = consumerRef.watch(
|
||||
multiSelectProvider.select(
|
||||
(s) => s.isEnabled,
|
||||
),
|
||||
);
|
||||
|
||||
if (isMultiSelectEnabled) {
|
||||
return child!;
|
||||
}
|
||||
return const SizedBox.shrink();
|
||||
},
|
||||
child: const HomeBottomAppBar(),
|
||||
),
|
||||
if (isMultiSelectEnabled) {
|
||||
return child!;
|
||||
}
|
||||
return const SizedBox.shrink();
|
||||
},
|
||||
child: const HomeBottomAppBar(),
|
||||
),
|
||||
],
|
||||
],
|
||||
),
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user