2023-06-22 04:13:23 +02:00
|
|
|
import 'dart:math';
|
|
|
|
|
2023-07-13 17:42:06 +02:00
|
|
|
import 'package:auto_route/auto_route.dart';
|
2023-06-22 04:13:23 +02:00
|
|
|
import 'package:flutter/gestures.dart';
|
2022-09-28 18:30:38 +02:00
|
|
|
import 'package:flutter/material.dart';
|
2023-03-25 04:44:53 +01:00
|
|
|
import 'package:flutter_hooks/flutter_hooks.dart';
|
|
|
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
2023-11-29 05:17:29 +01:00
|
|
|
import 'package:immich_mobile/extensions/asyncvalue_extensions.dart';
|
2024-05-02 22:59:14 +02:00
|
|
|
import 'package:immich_mobile/providers/asset_viewer/render_list.provider.dart';
|
2024-05-07 06:04:21 +02:00
|
|
|
import 'package:immich_mobile/widgets/asset_grid/asset_grid_data_structure.dart';
|
|
|
|
import 'package:immich_mobile/widgets/asset_grid/immich_asset_grid_view.dart';
|
2024-05-02 22:59:14 +02:00
|
|
|
import 'package:immich_mobile/providers/app_settings.provider.dart';
|
|
|
|
import 'package:immich_mobile/services/app_settings.service.dart';
|
2024-05-01 04:36:40 +02:00
|
|
|
import 'package:immich_mobile/entities/asset.entity.dart';
|
2023-05-17 19:36:02 +02:00
|
|
|
import 'package:scrollable_positioned_list/scrollable_positioned_list.dart';
|
2022-09-28 18:30:38 +02:00
|
|
|
|
2023-03-25 04:44:53 +01:00
|
|
|
class ImmichAssetGrid extends HookConsumerWidget {
|
|
|
|
final int? assetsPerRow;
|
|
|
|
final double margin;
|
|
|
|
final bool? showStorageIndicator;
|
|
|
|
final ImmichAssetGridSelectionListener? listener;
|
|
|
|
final bool selectionActive;
|
2023-05-17 19:36:02 +02:00
|
|
|
final List<Asset>? assets;
|
2023-03-25 04:44:53 +01:00
|
|
|
final RenderList? renderList;
|
2023-03-27 04:35:52 +02:00
|
|
|
final Future<void> Function()? onRefresh;
|
2023-05-17 19:36:02 +02:00
|
|
|
final Set<Asset>? preselectedAssets;
|
|
|
|
final bool canDeselect;
|
|
|
|
final bool? dynamicLayout;
|
|
|
|
final bool showMultiSelectIndicator;
|
2024-01-15 16:26:13 +01:00
|
|
|
final void Function(Iterable<ItemPosition> itemPositions)?
|
2023-05-17 19:36:02 +02:00
|
|
|
visibleItemsListener;
|
2023-06-23 17:44:02 +02:00
|
|
|
final Widget? topWidget;
|
2023-08-27 07:07:35 +02:00
|
|
|
final bool shrinkWrap;
|
|
|
|
final bool showDragScroll;
|
2023-10-22 04:38:07 +02:00
|
|
|
final bool showStack;
|
2022-10-01 19:19:40 +02:00
|
|
|
|
2023-03-25 04:44:53 +01:00
|
|
|
const ImmichAssetGrid({
|
|
|
|
super.key,
|
2023-05-17 19:36:02 +02:00
|
|
|
this.assets,
|
2023-03-27 04:35:52 +02:00
|
|
|
this.onRefresh,
|
2023-03-25 04:44:53 +01:00
|
|
|
this.renderList,
|
|
|
|
this.assetsPerRow,
|
|
|
|
this.showStorageIndicator,
|
|
|
|
this.listener,
|
2024-04-01 16:45:11 +02:00
|
|
|
this.margin = 2.0,
|
2023-03-25 04:44:53 +01:00
|
|
|
this.selectionActive = false,
|
2023-05-17 19:36:02 +02:00
|
|
|
this.preselectedAssets,
|
|
|
|
this.canDeselect = true,
|
|
|
|
this.dynamicLayout,
|
|
|
|
this.showMultiSelectIndicator = true,
|
|
|
|
this.visibleItemsListener,
|
2023-06-23 17:44:02 +02:00
|
|
|
this.topWidget,
|
2023-08-27 07:07:35 +02:00
|
|
|
this.shrinkWrap = false,
|
|
|
|
this.showDragScroll = true,
|
2023-10-22 04:38:07 +02:00
|
|
|
this.showStack = false,
|
2023-03-25 04:44:53 +01:00
|
|
|
});
|
2022-10-06 22:41:56 +02:00
|
|
|
|
2023-03-25 04:44:53 +01:00
|
|
|
@override
|
|
|
|
Widget build(BuildContext context, WidgetRef ref) {
|
2023-07-08 22:03:54 +02:00
|
|
|
var settings = ref.watch(appSettingsServiceProvider);
|
|
|
|
|
2023-06-22 04:13:23 +02:00
|
|
|
final perRow = useState(
|
|
|
|
assetsPerRow ?? settings.getSetting(AppSettingsEnum.tilesPerRow)!,
|
|
|
|
);
|
|
|
|
final scaleFactor = useState(7.0 - perRow.value);
|
|
|
|
final baseScaleFactor = useState(7.0 - perRow.value);
|
|
|
|
|
2023-07-13 17:42:06 +02:00
|
|
|
/// assets need different hero tags across tabs / modals
|
|
|
|
/// otherwise, hero animations are performed across tabs (looks buggy!)
|
|
|
|
int heroOffset() {
|
|
|
|
const int range = 1152921504606846976; // 2^60
|
|
|
|
final tabScope = TabsRouterScope.of(context);
|
|
|
|
if (tabScope != null) {
|
|
|
|
final int tabIndex = tabScope.controller.activeIndex;
|
|
|
|
return tabIndex * range;
|
|
|
|
}
|
|
|
|
return range * 7;
|
2023-07-08 22:03:54 +02:00
|
|
|
}
|
|
|
|
|
2023-05-17 19:36:02 +02:00
|
|
|
Widget buildAssetGridView(RenderList renderList) {
|
2023-07-13 17:42:06 +02:00
|
|
|
return RawGestureDetector(
|
|
|
|
gestures: {
|
|
|
|
CustomScaleGestureRecognizer: GestureRecognizerFactoryWithHandlers<
|
|
|
|
CustomScaleGestureRecognizer>(
|
|
|
|
() => CustomScaleGestureRecognizer(),
|
|
|
|
(CustomScaleGestureRecognizer scale) {
|
|
|
|
scale.onStart = (details) {
|
|
|
|
baseScaleFactor.value = scaleFactor.value;
|
|
|
|
};
|
2023-06-22 04:13:23 +02:00
|
|
|
|
2023-07-13 17:42:06 +02:00
|
|
|
scale.onUpdate = (details) {
|
2024-01-15 16:26:13 +01:00
|
|
|
scaleFactor.value = max(
|
|
|
|
min(5.0, baseScaleFactor.value * details.scale),
|
|
|
|
1.0,
|
|
|
|
);
|
2023-07-13 17:42:06 +02:00
|
|
|
if (7 - scaleFactor.value.toInt() != perRow.value) {
|
|
|
|
perRow.value = 7 - scaleFactor.value.toInt();
|
|
|
|
}
|
|
|
|
};
|
2023-08-19 00:52:40 +02:00
|
|
|
}),
|
2023-07-13 17:42:06 +02:00
|
|
|
},
|
|
|
|
child: ImmichAssetGridView(
|
|
|
|
onRefresh: onRefresh,
|
|
|
|
assetsPerRow: perRow.value,
|
|
|
|
listener: listener,
|
|
|
|
showStorageIndicator: showStorageIndicator ??
|
|
|
|
settings.getSetting(AppSettingsEnum.storageIndicator),
|
|
|
|
renderList: renderList,
|
|
|
|
margin: margin,
|
|
|
|
selectionActive: selectionActive,
|
|
|
|
preselectedAssets: preselectedAssets,
|
|
|
|
canDeselect: canDeselect,
|
|
|
|
dynamicLayout: dynamicLayout ??
|
|
|
|
settings.getSetting(AppSettingsEnum.dynamicLayout),
|
|
|
|
showMultiSelectIndicator: showMultiSelectIndicator,
|
|
|
|
visibleItemsListener: visibleItemsListener,
|
|
|
|
topWidget: topWidget,
|
|
|
|
heroOffset: heroOffset(),
|
2023-08-27 07:07:35 +02:00
|
|
|
shrinkWrap: shrinkWrap,
|
|
|
|
showDragScroll: showDragScroll,
|
2023-10-22 04:38:07 +02:00
|
|
|
showStack: showStack,
|
2022-09-28 18:30:38 +02:00
|
|
|
),
|
2023-03-25 04:44:53 +01:00
|
|
|
);
|
2023-02-02 19:20:26 +01:00
|
|
|
}
|
|
|
|
|
2023-05-17 19:36:02 +02:00
|
|
|
if (renderList != null) return buildAssetGridView(renderList!);
|
|
|
|
|
|
|
|
final renderListFuture = ref.watch(renderListProvider(assets!));
|
2023-11-29 05:17:29 +01:00
|
|
|
return renderListFuture.widgetWhen(
|
|
|
|
onData: (renderList) => buildAssetGridView(renderList),
|
2022-09-30 10:47:31 +02:00
|
|
|
);
|
2022-09-28 18:30:38 +02:00
|
|
|
}
|
|
|
|
}
|
2023-06-22 04:13:23 +02:00
|
|
|
|
|
|
|
/// accepts a gesture even though it should reject it (because child won)
|
|
|
|
class CustomScaleGestureRecognizer extends ScaleGestureRecognizer {
|
|
|
|
@override
|
|
|
|
void rejectGesture(int pointer) {
|
|
|
|
acceptGesture(pointer);
|
|
|
|
}
|
|
|
|
}
|