mirror of
https://github.com/immich-app/immich.git
synced 2025-01-04 02:46:47 +01:00
5806a3ce25
* refactor(mobile): widgets * update
141 lines
4.8 KiB
Dart
141 lines
4.8 KiB
Dart
import 'dart:math';
|
|
|
|
import 'package:auto_route/auto_route.dart';
|
|
import 'package:flutter/gestures.dart';
|
|
import 'package:flutter/material.dart';
|
|
import 'package:flutter_hooks/flutter_hooks.dart';
|
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
|
import 'package:immich_mobile/extensions/asyncvalue_extensions.dart';
|
|
import 'package:immich_mobile/providers/asset_viewer/render_list.provider.dart';
|
|
import 'package:immich_mobile/widgets/asset_grid/asset_grid_data_structure.dart';
|
|
import 'package:immich_mobile/widgets/asset_grid/immich_asset_grid_view.dart';
|
|
import 'package:immich_mobile/providers/app_settings.provider.dart';
|
|
import 'package:immich_mobile/services/app_settings.service.dart';
|
|
import 'package:immich_mobile/entities/asset.entity.dart';
|
|
import 'package:scrollable_positioned_list/scrollable_positioned_list.dart';
|
|
|
|
class ImmichAssetGrid extends HookConsumerWidget {
|
|
final int? assetsPerRow;
|
|
final double margin;
|
|
final bool? showStorageIndicator;
|
|
final ImmichAssetGridSelectionListener? listener;
|
|
final bool selectionActive;
|
|
final List<Asset>? assets;
|
|
final RenderList? renderList;
|
|
final Future<void> Function()? onRefresh;
|
|
final Set<Asset>? preselectedAssets;
|
|
final bool canDeselect;
|
|
final bool? dynamicLayout;
|
|
final bool showMultiSelectIndicator;
|
|
final void Function(Iterable<ItemPosition> itemPositions)?
|
|
visibleItemsListener;
|
|
final Widget? topWidget;
|
|
final bool shrinkWrap;
|
|
final bool showDragScroll;
|
|
final bool showStack;
|
|
|
|
const ImmichAssetGrid({
|
|
super.key,
|
|
this.assets,
|
|
this.onRefresh,
|
|
this.renderList,
|
|
this.assetsPerRow,
|
|
this.showStorageIndicator,
|
|
this.listener,
|
|
this.margin = 2.0,
|
|
this.selectionActive = false,
|
|
this.preselectedAssets,
|
|
this.canDeselect = true,
|
|
this.dynamicLayout,
|
|
this.showMultiSelectIndicator = true,
|
|
this.visibleItemsListener,
|
|
this.topWidget,
|
|
this.shrinkWrap = false,
|
|
this.showDragScroll = true,
|
|
this.showStack = false,
|
|
});
|
|
|
|
@override
|
|
Widget build(BuildContext context, WidgetRef ref) {
|
|
var settings = ref.watch(appSettingsServiceProvider);
|
|
|
|
final perRow = useState(
|
|
assetsPerRow ?? settings.getSetting(AppSettingsEnum.tilesPerRow)!,
|
|
);
|
|
final scaleFactor = useState(7.0 - perRow.value);
|
|
final baseScaleFactor = useState(7.0 - perRow.value);
|
|
|
|
/// 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;
|
|
}
|
|
|
|
Widget buildAssetGridView(RenderList renderList) {
|
|
return RawGestureDetector(
|
|
gestures: {
|
|
CustomScaleGestureRecognizer: GestureRecognizerFactoryWithHandlers<
|
|
CustomScaleGestureRecognizer>(
|
|
() => CustomScaleGestureRecognizer(),
|
|
(CustomScaleGestureRecognizer scale) {
|
|
scale.onStart = (details) {
|
|
baseScaleFactor.value = scaleFactor.value;
|
|
};
|
|
|
|
scale.onUpdate = (details) {
|
|
scaleFactor.value = max(
|
|
min(5.0, baseScaleFactor.value * details.scale),
|
|
1.0,
|
|
);
|
|
if (7 - scaleFactor.value.toInt() != perRow.value) {
|
|
perRow.value = 7 - scaleFactor.value.toInt();
|
|
}
|
|
};
|
|
}),
|
|
},
|
|
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(),
|
|
shrinkWrap: shrinkWrap,
|
|
showDragScroll: showDragScroll,
|
|
showStack: showStack,
|
|
),
|
|
);
|
|
}
|
|
|
|
if (renderList != null) return buildAssetGridView(renderList!);
|
|
|
|
final renderListFuture = ref.watch(renderListProvider(assets!));
|
|
return renderListFuture.widgetWhen(
|
|
onData: (renderList) => buildAssetGridView(renderList),
|
|
);
|
|
}
|
|
}
|
|
|
|
/// accepts a gesture even though it should reject it (because child won)
|
|
class CustomScaleGestureRecognizer extends ScaleGestureRecognizer {
|
|
@override
|
|
void rejectGesture(int pointer) {
|
|
acceptGesture(pointer);
|
|
}
|
|
}
|