mirror of
https://github.com/immich-app/immich
synced 2025-10-17 18:19:27 +00:00
feat: new timeline multi-selection (#19443)
* feat: new timeline multiselection * select all from bucket * wip * group multi-select * group multi-select * pr feedback * pr feedback * lint
This commit is contained in:
parent
ebcf133bea
commit
9ca31abae9
5 changed files with 416 additions and 61 deletions
|
|
@ -9,7 +9,9 @@ import 'package:immich_mobile/presentation/widgets/timeline/header.widget.dart';
|
|||
import 'package:immich_mobile/presentation/widgets/timeline/segment.model.dart';
|
||||
import 'package:immich_mobile/presentation/widgets/timeline/segment_builder.dart';
|
||||
import 'package:immich_mobile/presentation/widgets/timeline/timeline.state.dart';
|
||||
import 'package:immich_mobile/providers/haptic_feedback.provider.dart';
|
||||
import 'package:immich_mobile/providers/infrastructure/timeline.provider.dart';
|
||||
import 'package:immich_mobile/providers/timeline/multiselect.provider.dart';
|
||||
|
||||
class FixedSegment extends Segment {
|
||||
final double tileHeight;
|
||||
|
|
@ -60,21 +62,39 @@ class FixedSegment extends Segment {
|
|||
return gridIndex + firstRowBelow - 1;
|
||||
}
|
||||
|
||||
void _handleOnTap(WidgetRef ref, BaseAsset asset) {
|
||||
if (!ref.read(multiSelectProvider.select((s) => s.isEnabled))) {
|
||||
return;
|
||||
}
|
||||
|
||||
ref.read(multiSelectProvider.notifier).toggleAssetSelection(asset);
|
||||
}
|
||||
|
||||
void _handleOnLongPress(WidgetRef ref, BaseAsset asset) {
|
||||
if (ref.read(multiSelectProvider.select((s) => s.isEnabled))) {
|
||||
return;
|
||||
}
|
||||
|
||||
ref.read(hapticFeedbackProvider.notifier).heavyImpact();
|
||||
ref.read(multiSelectProvider.notifier).toggleAssetSelection(asset);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget builder(BuildContext context, int index) {
|
||||
final rowIndexInSegment = index - (firstIndex + 1);
|
||||
final assetIndex = rowIndexInSegment * columnCount;
|
||||
final assetCount = bucket.assetCount;
|
||||
final numberOfAssets = math.min(columnCount, assetCount - assetIndex);
|
||||
|
||||
if (index == firstIndex) {
|
||||
return TimelineHeader(
|
||||
bucket: bucket,
|
||||
header: header,
|
||||
height: headerExtent,
|
||||
assetOffset: firstAssetIndex,
|
||||
);
|
||||
}
|
||||
|
||||
final rowIndexInSegment = index - (firstIndex + 1);
|
||||
final assetIndex = rowIndexInSegment * columnCount;
|
||||
final assetCount = bucket.assetCount;
|
||||
final numberOfAssets = math.min(columnCount, assetCount - assetIndex);
|
||||
|
||||
return _buildRow(firstAssetIndex + assetIndex, numberOfAssets);
|
||||
}
|
||||
|
||||
|
|
@ -97,7 +117,12 @@ class FixedSegment extends Segment {
|
|||
// Bucket is already loaded, show the assets
|
||||
if (timelineService.hasRange(assetIndex, count)) {
|
||||
final assets = timelineService.getAssets(assetIndex, count);
|
||||
return _buildAssetRow(ctx, assets);
|
||||
return _buildAssetRow(
|
||||
ctx,
|
||||
assets,
|
||||
onTap: (asset) => _handleOnTap(ref, asset),
|
||||
onLongPress: (asset) => _handleOnLongPress(ref, asset),
|
||||
);
|
||||
}
|
||||
|
||||
// Bucket is not loaded, show placeholders and load the bucket
|
||||
|
|
@ -113,20 +138,36 @@ class FixedSegment extends Segment {
|
|||
);
|
||||
}
|
||||
|
||||
return _buildAssetRow(ctxx, snap.requireData);
|
||||
return _buildAssetRow(
|
||||
ctxx,
|
||||
snap.requireData,
|
||||
onTap: (asset) => _handleOnTap(ref, asset),
|
||||
onLongPress: (asset) => _handleOnLongPress(ref, asset),
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
Widget _buildAssetRow(BuildContext context, List<BaseAsset> assets) =>
|
||||
Widget _buildAssetRow(
|
||||
BuildContext context,
|
||||
List<BaseAsset> assets, {
|
||||
required void Function(BaseAsset) onTap,
|
||||
required void Function(BaseAsset) onLongPress,
|
||||
}) =>
|
||||
FixedTimelineRow(
|
||||
dimension: tileHeight,
|
||||
spacing: spacing,
|
||||
textDirection: Directionality.of(context),
|
||||
children: List.generate(
|
||||
assets.length,
|
||||
(i) => RepaintBoundary(child: ThumbnailTile(assets[i])),
|
||||
(i) => RepaintBoundary(
|
||||
child: GestureDetector(
|
||||
onTap: () => onTap(assets[i]),
|
||||
onLongPress: () => onLongPress(assets[i]),
|
||||
child: ThumbnailTile(assets[i]),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue