mirror of
https://github.com/immich-app/immich
synced 2025-11-14 17:36:12 +00:00
chore: bump line length to 120 (#20191)
This commit is contained in:
parent
977c9b96ba
commit
ad65e9011a
517 changed files with 4520 additions and 9514 deletions
|
|
@ -35,9 +35,7 @@ class ArchiveActionButton extends ConsumerWidget {
|
|||
if (context.mounted) {
|
||||
ImmichToast.show(
|
||||
context: context,
|
||||
msg: result.success
|
||||
? successMessage
|
||||
: 'scaffold_body_error_occurred'.t(context: context),
|
||||
msg: result.success ? successMessage : 'scaffold_body_error_occurred'.t(context: context),
|
||||
gravity: ToastGravity.BOTTOM,
|
||||
toastType: result.success ? ToastType.success : ToastType.error,
|
||||
);
|
||||
|
|
|
|||
|
|
@ -25,12 +25,10 @@ class BaseActionButton extends StatelessWidget {
|
|||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final miniWidth =
|
||||
minWidth ?? (context.isMobile ? context.width / 4.5 : 75.0);
|
||||
final miniWidth = minWidth ?? (context.isMobile ? context.width / 4.5 : 75.0);
|
||||
final iconTheme = IconTheme.of(context);
|
||||
final iconSize = iconTheme.size ?? 24.0;
|
||||
final iconColor =
|
||||
this.iconColor ?? iconTheme.color ?? context.themeData.iconTheme.color;
|
||||
final iconColor = this.iconColor ?? iconTheme.color ?? context.themeData.iconTheme.color;
|
||||
final textColor = context.themeData.textTheme.labelLarge?.color;
|
||||
|
||||
if (menuItem) {
|
||||
|
|
|
|||
|
|
@ -17,8 +17,7 @@ class CastActionButton extends ConsumerWidget {
|
|||
|
||||
return BaseActionButton(
|
||||
iconData: isCasting ? Icons.cast_connected_rounded : Icons.cast_rounded,
|
||||
iconColor:
|
||||
isCasting ? context.primaryColor : null, // null = default color
|
||||
iconColor: isCasting ? context.primaryColor : null, // null = default color
|
||||
label: "cast".t(context: context),
|
||||
onPressed: () {
|
||||
showDialog(
|
||||
|
|
|
|||
|
|
@ -35,9 +35,7 @@ class DeleteLocalActionButton extends ConsumerWidget {
|
|||
if (context.mounted) {
|
||||
ImmichToast.show(
|
||||
context: context,
|
||||
msg: result.success
|
||||
? successMessage
|
||||
: 'scaffold_body_error_occurred'.t(context: context),
|
||||
msg: result.success ? successMessage : 'scaffold_body_error_occurred'.t(context: context),
|
||||
gravity: ToastGravity.BOTTOM,
|
||||
toastType: result.success ? ToastType.success : ToastType.error,
|
||||
);
|
||||
|
|
|
|||
|
|
@ -20,8 +20,7 @@ class DeletePermanentActionButton extends ConsumerWidget {
|
|||
return;
|
||||
}
|
||||
|
||||
final result =
|
||||
await ref.read(actionProvider.notifier).deleteRemoteAndLocal(source);
|
||||
final result = await ref.read(actionProvider.notifier).deleteRemoteAndLocal(source);
|
||||
ref.read(multiSelectProvider.notifier).reset();
|
||||
|
||||
if (source == ActionSource.viewer) {
|
||||
|
|
@ -36,9 +35,7 @@ class DeletePermanentActionButton extends ConsumerWidget {
|
|||
if (context.mounted) {
|
||||
ImmichToast.show(
|
||||
context: context,
|
||||
msg: result.success
|
||||
? successMessage
|
||||
: 'scaffold_body_error_occurred'.t(context: context),
|
||||
msg: result.success ? successMessage : 'scaffold_body_error_occurred'.t(context: context),
|
||||
gravity: ToastGravity.BOTTOM,
|
||||
toastType: result.success ? ToastType.success : ToastType.error,
|
||||
);
|
||||
|
|
|
|||
|
|
@ -17,8 +17,7 @@ class DeleteTrashActionButton extends ConsumerWidget {
|
|||
return;
|
||||
}
|
||||
|
||||
final result =
|
||||
await ref.read(actionProvider.notifier).deleteRemoteAndLocal(source);
|
||||
final result = await ref.read(actionProvider.notifier).deleteRemoteAndLocal(source);
|
||||
ref.read(multiSelectProvider.notifier).reset();
|
||||
|
||||
final successMessage = 'assets_permanently_deleted_count'.t(
|
||||
|
|
@ -29,9 +28,7 @@ class DeleteTrashActionButton extends ConsumerWidget {
|
|||
if (context.mounted) {
|
||||
ImmichToast.show(
|
||||
context: context,
|
||||
msg: result.success
|
||||
? successMessage
|
||||
: 'scaffold_body_error_occurred'.t(context: context),
|
||||
msg: result.success ? successMessage : 'scaffold_body_error_occurred'.t(context: context),
|
||||
gravity: ToastGravity.BOTTOM,
|
||||
toastType: result.success ? ToastType.success : ToastType.error,
|
||||
);
|
||||
|
|
|
|||
|
|
@ -35,8 +35,7 @@ class DownloadActionButton extends ConsumerWidget {
|
|||
} else if (result.count > 0) {
|
||||
ImmichToast.show(
|
||||
context: context,
|
||||
msg: 'download_action_prompt'
|
||||
.t(context: context, args: {'count': result.count.toString()}),
|
||||
msg: 'download_action_prompt'.t(context: context, args: {'count': result.count.toString()}),
|
||||
gravity: ToastGravity.BOTTOM,
|
||||
toastType: ToastType.success,
|
||||
);
|
||||
|
|
|
|||
|
|
@ -18,8 +18,7 @@ class EditLocationActionButton extends ConsumerWidget {
|
|||
return;
|
||||
}
|
||||
|
||||
final result =
|
||||
await ref.read(actionProvider.notifier).editLocation(source, context);
|
||||
final result = await ref.read(actionProvider.notifier).editLocation(source, context);
|
||||
if (result == null) {
|
||||
return;
|
||||
}
|
||||
|
|
@ -34,9 +33,7 @@ class EditLocationActionButton extends ConsumerWidget {
|
|||
if (context.mounted) {
|
||||
ImmichToast.show(
|
||||
context: context,
|
||||
msg: result.success
|
||||
? successMessage
|
||||
: 'scaffold_body_error_occurred'.t(context: context),
|
||||
msg: result.success ? successMessage : 'scaffold_body_error_occurred'.t(context: context),
|
||||
gravity: ToastGravity.BOTTOM,
|
||||
toastType: result.success ? ToastType.success : ToastType.error,
|
||||
);
|
||||
|
|
|
|||
|
|
@ -39,9 +39,7 @@ class FavoriteActionButton extends ConsumerWidget {
|
|||
if (context.mounted) {
|
||||
ImmichToast.show(
|
||||
context: context,
|
||||
msg: result.success
|
||||
? successMessage
|
||||
: 'scaffold_body_error_occurred'.t(context: context),
|
||||
msg: result.success ? successMessage : 'scaffold_body_error_occurred'.t(context: context),
|
||||
gravity: ToastGravity.BOTTOM,
|
||||
toastType: result.success ? ToastType.success : ToastType.error,
|
||||
);
|
||||
|
|
|
|||
|
|
@ -14,9 +14,7 @@ class MotionPhotoActionButton extends ConsumerWidget {
|
|||
final isPlaying = ref.watch(isPlayingMotionVideoProvider);
|
||||
|
||||
return BaseActionButton(
|
||||
iconData: isPlaying
|
||||
? Icons.motion_photos_pause_outlined
|
||||
: Icons.play_circle_outline_rounded,
|
||||
iconData: isPlaying ? Icons.motion_photos_pause_outlined : Icons.play_circle_outline_rounded,
|
||||
label: "play_motion_photo".t(context: context),
|
||||
onPressed: ref.read(isPlayingMotionVideoProvider.notifier).toggle,
|
||||
menuItem: menuItem,
|
||||
|
|
|
|||
|
|
@ -20,8 +20,7 @@ class MoveToLockFolderActionButton extends ConsumerWidget {
|
|||
return;
|
||||
}
|
||||
|
||||
final result =
|
||||
await ref.read(actionProvider.notifier).moveToLockFolder(source);
|
||||
final result = await ref.read(actionProvider.notifier).moveToLockFolder(source);
|
||||
ref.read(multiSelectProvider.notifier).reset();
|
||||
|
||||
if (source == ActionSource.viewer) {
|
||||
|
|
@ -36,9 +35,7 @@ class MoveToLockFolderActionButton extends ConsumerWidget {
|
|||
if (context.mounted) {
|
||||
ImmichToast.show(
|
||||
context: context,
|
||||
msg: result.success
|
||||
? successMessage
|
||||
: 'scaffold_body_error_occurred'.t(context: context),
|
||||
msg: result.success ? successMessage : 'scaffold_body_error_occurred'.t(context: context),
|
||||
gravity: ToastGravity.BOTTOM,
|
||||
toastType: result.success ? ToastType.success : ToastType.error,
|
||||
);
|
||||
|
|
|
|||
|
|
@ -23,9 +23,7 @@ class RemoveFromAlbumActionButton extends ConsumerWidget {
|
|||
return;
|
||||
}
|
||||
|
||||
final result = await ref
|
||||
.read(actionProvider.notifier)
|
||||
.removeFromAlbum(source, albumId);
|
||||
final result = await ref.read(actionProvider.notifier).removeFromAlbum(source, albumId);
|
||||
ref.read(multiSelectProvider.notifier).reset();
|
||||
|
||||
final successMessage = 'remove_from_album_action_prompt'.t(
|
||||
|
|
@ -36,9 +34,7 @@ class RemoveFromAlbumActionButton extends ConsumerWidget {
|
|||
if (context.mounted) {
|
||||
ImmichToast.show(
|
||||
context: context,
|
||||
msg: result.success
|
||||
? successMessage
|
||||
: 'scaffold_body_error_occurred'.t(context: context),
|
||||
msg: result.success ? successMessage : 'scaffold_body_error_occurred'.t(context: context),
|
||||
gravity: ToastGravity.BOTTOM,
|
||||
toastType: result.success ? ToastType.success : ToastType.error,
|
||||
);
|
||||
|
|
|
|||
|
|
@ -18,8 +18,7 @@ class RemoveFromLockFolderActionButton extends ConsumerWidget {
|
|||
return;
|
||||
}
|
||||
|
||||
final result =
|
||||
await ref.read(actionProvider.notifier).removeFromLockFolder(source);
|
||||
final result = await ref.read(actionProvider.notifier).removeFromLockFolder(source);
|
||||
ref.read(multiSelectProvider.notifier).reset();
|
||||
|
||||
final successMessage = 'remove_from_lock_folder_action_prompt'.t(
|
||||
|
|
@ -30,9 +29,7 @@ class RemoveFromLockFolderActionButton extends ConsumerWidget {
|
|||
if (context.mounted) {
|
||||
ImmichToast.show(
|
||||
context: context,
|
||||
msg: result.success
|
||||
? successMessage
|
||||
: 'scaffold_body_error_occurred'.t(context: context),
|
||||
msg: result.success ? successMessage : 'scaffold_body_error_occurred'.t(context: context),
|
||||
gravity: ToastGravity.BOTTOM,
|
||||
toastType: result.success ? ToastType.success : ToastType.error,
|
||||
);
|
||||
|
|
|
|||
|
|
@ -28,9 +28,7 @@ class RestoreTrashActionButton extends ConsumerWidget {
|
|||
if (context.mounted) {
|
||||
ImmichToast.show(
|
||||
context: context,
|
||||
msg: result.success
|
||||
? successMessage
|
||||
: 'scaffold_body_error_occurred'.t(context: context),
|
||||
msg: result.success ? successMessage : 'scaffold_body_error_occurred'.t(context: context),
|
||||
gravity: ToastGravity.BOTTOM,
|
||||
toastType: result.success ? ToastType.success : ToastType.error,
|
||||
);
|
||||
|
|
|
|||
|
|
@ -37,8 +37,7 @@ class ShareActionButton extends ConsumerWidget {
|
|||
} else if (result.count > 0) {
|
||||
ImmichToast.show(
|
||||
context: context,
|
||||
msg: 'share_action_prompt'
|
||||
.t(context: context, args: {'count': result.count.toString()}),
|
||||
msg: 'share_action_prompt'.t(context: context, args: {'count': result.count.toString()}),
|
||||
gravity: ToastGravity.BOTTOM,
|
||||
toastType: ToastType.success,
|
||||
);
|
||||
|
|
@ -48,8 +47,7 @@ class ShareActionButton extends ConsumerWidget {
|
|||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
return BaseActionButton(
|
||||
iconData:
|
||||
Platform.isAndroid ? Icons.share_rounded : Icons.ios_share_rounded,
|
||||
iconData: Platform.isAndroid ? Icons.share_rounded : Icons.ios_share_rounded,
|
||||
label: 'share'.t(context: context),
|
||||
onPressed: () => _onTap(context, ref),
|
||||
);
|
||||
|
|
|
|||
|
|
@ -24,8 +24,7 @@ class StackActionButton extends ConsumerWidget {
|
|||
throw Exception('User must be logged in to access stack action');
|
||||
}
|
||||
|
||||
final result =
|
||||
await ref.read(actionProvider.notifier).stack(user.id, source);
|
||||
final result = await ref.read(actionProvider.notifier).stack(user.id, source);
|
||||
ref.read(multiSelectProvider.notifier).reset();
|
||||
|
||||
final successMessage = 'stack_action_prompt'.t(
|
||||
|
|
@ -36,9 +35,7 @@ class StackActionButton extends ConsumerWidget {
|
|||
if (context.mounted) {
|
||||
ImmichToast.show(
|
||||
context: context,
|
||||
msg: result.success
|
||||
? successMessage
|
||||
: 'scaffold_body_error_occurred'.t(context: context),
|
||||
msg: result.success ? successMessage : 'scaffold_body_error_occurred'.t(context: context),
|
||||
gravity: ToastGravity.BOTTOM,
|
||||
toastType: result.success ? ToastType.success : ToastType.error,
|
||||
);
|
||||
|
|
|
|||
|
|
@ -35,9 +35,7 @@ class TrashActionButton extends ConsumerWidget {
|
|||
if (context.mounted) {
|
||||
ImmichToast.show(
|
||||
context: context,
|
||||
msg: result.success
|
||||
? successMessage
|
||||
: 'scaffold_body_error_occurred'.t(context: context),
|
||||
msg: result.success ? successMessage : 'scaffold_body_error_occurred'.t(context: context),
|
||||
gravity: ToastGravity.BOTTOM,
|
||||
toastType: result.success ? ToastType.success : ToastType.error,
|
||||
);
|
||||
|
|
|
|||
|
|
@ -29,9 +29,7 @@ class UnArchiveActionButton extends ConsumerWidget {
|
|||
if (context.mounted) {
|
||||
ImmichToast.show(
|
||||
context: context,
|
||||
msg: result.success
|
||||
? successMessage
|
||||
: 'scaffold_body_error_occurred'.t(context: context),
|
||||
msg: result.success ? successMessage : 'scaffold_body_error_occurred'.t(context: context),
|
||||
gravity: ToastGravity.BOTTOM,
|
||||
toastType: result.success ? ToastType.success : ToastType.error,
|
||||
);
|
||||
|
|
|
|||
|
|
@ -39,9 +39,7 @@ class UnFavoriteActionButton extends ConsumerWidget {
|
|||
if (context.mounted) {
|
||||
ImmichToast.show(
|
||||
context: context,
|
||||
msg: result.success
|
||||
? successMessage
|
||||
: 'scaffold_body_error_occurred'.t(context: context),
|
||||
msg: result.success ? successMessage : 'scaffold_body_error_occurred'.t(context: context),
|
||||
gravity: ToastGravity.BOTTOM,
|
||||
toastType: result.success ? ToastType.success : ToastType.error,
|
||||
);
|
||||
|
|
|
|||
|
|
@ -29,9 +29,7 @@ class UnStackActionButton extends ConsumerWidget {
|
|||
if (context.mounted) {
|
||||
ImmichToast.show(
|
||||
context: context,
|
||||
msg: result.success
|
||||
? successMessage
|
||||
: 'scaffold_body_error_occurred'.t(context: context),
|
||||
msg: result.success ? successMessage : 'scaffold_body_error_occurred'.t(context: context),
|
||||
gravity: ToastGravity.BOTTOM,
|
||||
toastType: result.success ? ToastType.success : ToastType.error,
|
||||
);
|
||||
|
|
|
|||
|
|
@ -28,9 +28,7 @@ class UploadActionButton extends ConsumerWidget {
|
|||
if (context.mounted) {
|
||||
ImmichToast.show(
|
||||
context: context,
|
||||
msg: result.success
|
||||
? successMessage
|
||||
: 'scaffold_body_error_occurred'.t(context: context),
|
||||
msg: result.success ? successMessage : 'scaffold_body_error_occurred'.t(context: context),
|
||||
gravity: ToastGravity.BOTTOM,
|
||||
toastType: result.success ? ToastType.success : ToastType.error,
|
||||
);
|
||||
|
|
|
|||
|
|
@ -2,8 +2,7 @@ import 'package:hooks_riverpod/hooks_riverpod.dart';
|
|||
import 'package:immich_mobile/domain/models/asset/base_asset.model.dart';
|
||||
import 'package:immich_mobile/providers/infrastructure/asset.provider.dart';
|
||||
|
||||
class StackChildrenNotifier
|
||||
extends AutoDisposeFamilyAsyncNotifier<List<RemoteAsset>, BaseAsset?> {
|
||||
class StackChildrenNotifier extends AutoDisposeFamilyAsyncNotifier<List<RemoteAsset>, BaseAsset?> {
|
||||
@override
|
||||
Future<List<RemoteAsset>> build(BaseAsset? asset) async {
|
||||
if (asset == null || asset is! RemoteAsset || asset.stackId == null) {
|
||||
|
|
@ -14,7 +13,7 @@ class StackChildrenNotifier
|
|||
}
|
||||
}
|
||||
|
||||
final stackChildrenNotifier = AsyncNotifierProvider.autoDispose
|
||||
.family<StackChildrenNotifier, List<RemoteAsset>, BaseAsset?>(
|
||||
final stackChildrenNotifier =
|
||||
AsyncNotifierProvider.autoDispose.family<StackChildrenNotifier, List<RemoteAsset>, BaseAsset?>(
|
||||
StackChildrenNotifier.new,
|
||||
);
|
||||
|
|
|
|||
|
|
@ -14,8 +14,7 @@ class AssetStackRow extends ConsumerWidget {
|
|||
int opacity = ref.watch(
|
||||
assetViewerProvider.select((state) => state.backgroundOpacity),
|
||||
);
|
||||
final showControls =
|
||||
ref.watch(assetViewerProvider.select((s) => s.showingControls));
|
||||
final showControls = ref.watch(assetViewerProvider.select((s) => s.showingControls));
|
||||
|
||||
if (!showControls) {
|
||||
opacity = 0;
|
||||
|
|
@ -68,8 +67,7 @@ class _StackList extends ConsumerWidget {
|
|||
child: Container(
|
||||
height: 60,
|
||||
width: 60,
|
||||
decoration: index ==
|
||||
ref.watch(assetViewerProvider.select((s) => s.stackIndex))
|
||||
decoration: index == ref.watch(assetViewerProvider.select((s) => s.stackIndex))
|
||||
? const BoxDecoration(
|
||||
color: Colors.white,
|
||||
borderRadius: BorderRadius.all(Radius.circular(6)),
|
||||
|
|
|
|||
|
|
@ -120,12 +120,10 @@ class _AssetViewerState extends ConsumerState<AssetViewer> {
|
|||
super.dispose();
|
||||
}
|
||||
|
||||
bool get showingBottomSheet =>
|
||||
ref.read(assetViewerProvider.select((s) => s.showingBottomSheet));
|
||||
bool get showingBottomSheet => ref.read(assetViewerProvider.select((s) => s.showingBottomSheet));
|
||||
|
||||
Color get backgroundColor {
|
||||
final opacity =
|
||||
ref.read(assetViewerProvider.select((s) => s.backgroundOpacity));
|
||||
final opacity = ref.read(assetViewerProvider.select((s) => s.backgroundOpacity));
|
||||
return Colors.black.withAlpha(opacity);
|
||||
}
|
||||
|
||||
|
|
@ -139,9 +137,7 @@ class _AssetViewerState extends ConsumerState<AssetViewer> {
|
|||
// This is used to calculate the scale of the asset when the bottom sheet is showing.
|
||||
// It is a small increment to ensure that the asset is slightly zoomed in when the
|
||||
// bottom sheet is showing, which emulates the zoom effect.
|
||||
double get _getScaleForBottomSheet =>
|
||||
(viewController?.prevValue.scale ?? viewController?.value.scale ?? 1.0) +
|
||||
0.01;
|
||||
double get _getScaleForBottomSheet => (viewController?.prevValue.scale ?? viewController?.value.scale ?? 1.0) + 0.01;
|
||||
|
||||
double _getVerticalOffsetForBottomSheet(double extent) =>
|
||||
(context.height * extent) - (context.height * _kBottomSheetMinimumExtent);
|
||||
|
|
@ -235,8 +231,8 @@ class _AssetViewerState extends ConsumerState<AssetViewer> {
|
|||
void _onPageBuild(PhotoViewControllerBase controller) {
|
||||
viewController ??= controller;
|
||||
if (showingBottomSheet) {
|
||||
final verticalOffset = (context.height * bottomSheetController.size) -
|
||||
(context.height * _kBottomSheetMinimumExtent);
|
||||
final verticalOffset =
|
||||
(context.height * bottomSheetController.size) - (context.height * _kBottomSheetMinimumExtent);
|
||||
controller.position = Offset(0, -verticalOffset);
|
||||
}
|
||||
}
|
||||
|
|
@ -262,9 +258,8 @@ class _AssetViewerState extends ConsumerState<AssetViewer> {
|
|||
viewController = controller;
|
||||
dragDownPosition = details.localPosition;
|
||||
initialPhotoViewState = controller.value;
|
||||
final isZoomed =
|
||||
scaleStateController.scaleState == PhotoViewScaleState.zoomedIn ||
|
||||
scaleStateController.scaleState == PhotoViewScaleState.covering;
|
||||
final isZoomed = scaleStateController.scaleState == PhotoViewScaleState.zoomedIn ||
|
||||
scaleStateController.scaleState == PhotoViewScaleState.covering;
|
||||
if (!showingBottomSheet && isZoomed) {
|
||||
blockGestures = true;
|
||||
}
|
||||
|
|
@ -349,8 +344,7 @@ class _AssetViewerState extends ConsumerState<AssetViewer> {
|
|||
updatedScale = initialPhotoViewState.scale! * (1.0 - scaleReduction);
|
||||
}
|
||||
|
||||
final backgroundOpacity =
|
||||
(255 * (1.0 - (scaleReduction / dragRatio))).round();
|
||||
final backgroundOpacity = (255 * (1.0 - (scaleReduction / dragRatio))).round();
|
||||
|
||||
viewController?.updateMultiple(
|
||||
position: initialPhotoViewState.position + delta,
|
||||
|
|
@ -494,8 +488,7 @@ class _AssetViewerState extends ConsumerState<AssetViewer> {
|
|||
}
|
||||
|
||||
void _snapBottomSheet() {
|
||||
if (bottomSheetController.size > _kBottomSheetSnapExtent ||
|
||||
bottomSheetController.size < 0.4) {
|
||||
if (bottomSheetController.size > _kBottomSheetSnapExtent || bottomSheetController.size < 0.4) {
|
||||
return;
|
||||
}
|
||||
isSnapping = true;
|
||||
|
|
@ -514,8 +507,7 @@ class _AssetViewerState extends ConsumerState<AssetViewer> {
|
|||
BaseAsset asset = ref.read(timelineServiceProvider).getAsset(index);
|
||||
final stackChildren = ref.read(stackChildrenNotifier(asset)).valueOrNull;
|
||||
if (stackChildren != null && stackChildren.isNotEmpty) {
|
||||
asset = stackChildren
|
||||
.elementAt(ref.read(assetViewerProvider.select((s) => s.stackIndex)));
|
||||
asset = stackChildren.elementAt(ref.read(assetViewerProvider.select((s) => s.stackIndex)));
|
||||
}
|
||||
return Container(
|
||||
width: double.infinity,
|
||||
|
|
@ -547,8 +539,7 @@ class _AssetViewerState extends ConsumerState<AssetViewer> {
|
|||
BaseAsset asset = ref.read(timelineServiceProvider).getAsset(index);
|
||||
final stackChildren = ref.read(stackChildrenNotifier(asset)).valueOrNull;
|
||||
if (stackChildren != null && stackChildren.isNotEmpty) {
|
||||
asset = stackChildren
|
||||
.elementAt(ref.read(assetViewerProvider.select((s) => s.stackIndex)));
|
||||
asset = stackChildren.elementAt(ref.read(assetViewerProvider.select((s) => s.stackIndex)));
|
||||
}
|
||||
|
||||
final isPlayingMotionVideo = ref.read(isPlayingMotionVideoProvider);
|
||||
|
|
@ -564,8 +555,7 @@ class _AssetViewerState extends ConsumerState<AssetViewer> {
|
|||
return PhotoViewGalleryPageOptions(
|
||||
key: ValueKey(asset.heroTag),
|
||||
imageProvider: getFullImageProvider(asset, size: size),
|
||||
heroAttributes:
|
||||
PhotoViewHeroAttributes(tag: '${asset.heroTag}_$heroOffset'),
|
||||
heroAttributes: PhotoViewHeroAttributes(tag: '${asset.heroTag}_$heroOffset'),
|
||||
filterQuality: FilterQuality.high,
|
||||
tightMode: true,
|
||||
initialScale: PhotoViewComputedScale.contained * 0.999,
|
||||
|
|
@ -600,8 +590,7 @@ class _AssetViewerState extends ConsumerState<AssetViewer> {
|
|||
onDragUpdate: _onDragUpdate,
|
||||
onDragEnd: _onDragEnd,
|
||||
onTapDown: _onTapDown,
|
||||
heroAttributes:
|
||||
PhotoViewHeroAttributes(tag: '${asset.heroTag}_$heroOffset'),
|
||||
heroAttributes: PhotoViewHeroAttributes(tag: '${asset.heroTag}_$heroOffset'),
|
||||
filterQuality: FilterQuality.high,
|
||||
initialScale: PhotoViewComputedScale.contained * 0.99,
|
||||
maxScale: 1.0,
|
||||
|
|
@ -615,8 +604,7 @@ class _AssetViewerState extends ConsumerState<AssetViewer> {
|
|||
asset: asset,
|
||||
image: Image(
|
||||
key: ValueKey(asset),
|
||||
image:
|
||||
getFullImageProvider(asset, size: Size(ctx.width, ctx.height)),
|
||||
image: getFullImageProvider(asset, size: Size(ctx.width, ctx.height)),
|
||||
fit: BoxFit.contain,
|
||||
height: ctx.height,
|
||||
width: ctx.width,
|
||||
|
|
@ -641,8 +629,7 @@ class _AssetViewerState extends ConsumerState<AssetViewer> {
|
|||
ref.watch(isPlayingMotionVideoProvider);
|
||||
|
||||
// Listen for casting changes and send initial asset to the cast provider
|
||||
ref.listen(castProvider.select((value) => value.isCasting),
|
||||
(_, isCasting) async {
|
||||
ref.listen(castProvider.select((value) => value.isCasting), (_, isCasting) async {
|
||||
if (!isCasting) return;
|
||||
|
||||
final asset = ref.read(currentAssetNotifier);
|
||||
|
|
|
|||
|
|
@ -108,7 +108,6 @@ class AssetViewerStateNotifier extends AutoDisposeNotifier<AssetViewerState> {
|
|||
}
|
||||
}
|
||||
|
||||
final assetViewerProvider =
|
||||
AutoDisposeNotifierProvider<AssetViewerStateNotifier, AssetViewerState>(
|
||||
final assetViewerProvider = AutoDisposeNotifierProvider<AssetViewerStateNotifier, AssetViewerState>(
|
||||
AssetViewerStateNotifier.new,
|
||||
);
|
||||
|
|
|
|||
|
|
@ -29,8 +29,7 @@ class ViewerBottomBar extends ConsumerWidget {
|
|||
int opacity = ref.watch(
|
||||
assetViewerProvider.select((state) => state.backgroundOpacity),
|
||||
);
|
||||
final showControls =
|
||||
ref.watch(assetViewerProvider.select((s) => s.showingControls));
|
||||
final showControls = ref.watch(assetViewerProvider.select((s) => s.showingControls));
|
||||
|
||||
if (!showControls) {
|
||||
opacity = 0;
|
||||
|
|
@ -38,10 +37,8 @@ class ViewerBottomBar extends ConsumerWidget {
|
|||
|
||||
final actions = <Widget>[
|
||||
const ShareActionButton(source: ActionSource.viewer),
|
||||
if (asset.isLocalOnly)
|
||||
const UploadActionButton(source: ActionSource.viewer),
|
||||
if (asset.hasRemote && isOwner)
|
||||
const ArchiveActionButton(source: ActionSource.viewer),
|
||||
if (asset.isLocalOnly) const UploadActionButton(source: ActionSource.viewer),
|
||||
if (asset.hasRemote && isOwner) const ArchiveActionButton(source: ActionSource.viewer),
|
||||
];
|
||||
|
||||
return IgnorePointer(
|
||||
|
|
@ -55,11 +52,9 @@ class ViewerBottomBar extends ConsumerWidget {
|
|||
? const SizedBox.shrink()
|
||||
: Theme(
|
||||
data: context.themeData.copyWith(
|
||||
iconTheme:
|
||||
const IconThemeData(size: 22, color: Colors.white),
|
||||
iconTheme: const IconThemeData(size: 22, color: Colors.white),
|
||||
textTheme: context.themeData.textTheme.copyWith(
|
||||
labelLarge:
|
||||
context.themeData.textTheme.labelLarge?.copyWith(
|
||||
labelLarge: context.themeData.textTheme.labelLarge?.copyWith(
|
||||
color: Colors.white,
|
||||
),
|
||||
),
|
||||
|
|
|
|||
|
|
@ -52,8 +52,7 @@ class AssetDetailBottomSheet extends ConsumerWidget {
|
|||
if (asset.hasRemote) ...[
|
||||
const ShareLinkActionButton(source: ActionSource.viewer),
|
||||
const ArchiveActionButton(source: ActionSource.viewer),
|
||||
if (!asset.hasLocal)
|
||||
const DownloadActionButton(source: ActionSource.viewer),
|
||||
if (!asset.hasLocal) const DownloadActionButton(source: ActionSource.viewer),
|
||||
isTrashEnable
|
||||
? const TrashActionButton(source: ActionSource.viewer)
|
||||
: const DeletePermanentActionButton(source: ActionSource.viewer),
|
||||
|
|
@ -100,18 +99,14 @@ class _AssetDetailBottomSheet extends ConsumerWidget {
|
|||
String _getFileInfo(BaseAsset asset, ExifInfo? exifInfo) {
|
||||
final height = asset.height ?? exifInfo?.height;
|
||||
final width = asset.width ?? exifInfo?.width;
|
||||
final resolution = (width != null && height != null)
|
||||
? "${width.toInt()} x ${height.toInt()}"
|
||||
: null;
|
||||
final fileSize =
|
||||
exifInfo?.fileSize != null ? formatBytes(exifInfo!.fileSize!) : null;
|
||||
final resolution = (width != null && height != null) ? "${width.toInt()} x ${height.toInt()}" : null;
|
||||
final fileSize = exifInfo?.fileSize != null ? formatBytes(exifInfo!.fileSize!) : null;
|
||||
|
||||
return switch ((fileSize, resolution)) {
|
||||
(null, null) => '',
|
||||
(String fileSize, null) => fileSize,
|
||||
(null, String resolution) => resolution,
|
||||
(String fileSize, String resolution) =>
|
||||
'$fileSize$_kSeparator$resolution',
|
||||
(String fileSize, String resolution) => '$fileSize$_kSeparator$resolution',
|
||||
};
|
||||
}
|
||||
|
||||
|
|
@ -133,17 +128,12 @@ class _AssetDetailBottomSheet extends ConsumerWidget {
|
|||
return null;
|
||||
}
|
||||
|
||||
final fNumber =
|
||||
exifInfo.fNumber.isNotEmpty ? 'ƒ/${exifInfo.fNumber}' : null;
|
||||
final exposureTime =
|
||||
exifInfo.exposureTime.isNotEmpty ? exifInfo.exposureTime : null;
|
||||
final focalLength =
|
||||
exifInfo.focalLength.isNotEmpty ? '${exifInfo.focalLength} mm' : null;
|
||||
final fNumber = exifInfo.fNumber.isNotEmpty ? 'ƒ/${exifInfo.fNumber}' : null;
|
||||
final exposureTime = exifInfo.exposureTime.isNotEmpty ? exifInfo.exposureTime : null;
|
||||
final focalLength = exifInfo.focalLength.isNotEmpty ? '${exifInfo.focalLength} mm' : null;
|
||||
final iso = exifInfo.iso != null ? 'ISO ${exifInfo.iso}' : null;
|
||||
|
||||
return [fNumber, exposureTime, focalLength, iso]
|
||||
.where((spec) => spec != null && spec.isNotEmpty)
|
||||
.join(_kSeparator);
|
||||
return [fNumber, exposureTime, focalLength, iso].where((spec) => spec != null && spec.isNotEmpty).join(_kSeparator);
|
||||
}
|
||||
|
||||
@override
|
||||
|
|
|
|||
|
|
@ -71,17 +71,13 @@ class _SheetLocationDetailsState extends ConsumerState<SheetLocationDetails> {
|
|||
final hasCoordinates = exifInfo?.hasCoordinates ?? false;
|
||||
|
||||
// Guard no lat/lng
|
||||
if (!hasCoordinates ||
|
||||
(asset != null && asset is LocalAsset && asset!.hasRemote)) {
|
||||
if (!hasCoordinates || (asset != null && asset is LocalAsset && asset!.hasRemote)) {
|
||||
return const SizedBox.shrink();
|
||||
}
|
||||
|
||||
final remoteId = asset is LocalAsset
|
||||
? (asset as LocalAsset).remoteId
|
||||
: (asset as RemoteAsset).id;
|
||||
final remoteId = asset is LocalAsset ? (asset as LocalAsset).remoteId : (asset as RemoteAsset).id;
|
||||
final locationName = _getLocationName(exifInfo);
|
||||
final coordinates =
|
||||
"${exifInfo!.latitude!.toStringAsFixed(4)}, ${exifInfo!.longitude!.toStringAsFixed(4)}";
|
||||
final coordinates = "${exifInfo!.latitude!.toStringAsFixed(4)}, ${exifInfo!.longitude!.toStringAsFixed(4)}";
|
||||
|
||||
return Padding(
|
||||
padding: EdgeInsets.symmetric(
|
||||
|
|
|
|||
|
|
@ -30,13 +30,11 @@ class ViewerTopAppBar extends ConsumerWidget implements PreferredSizeWidget {
|
|||
final isOwner = asset is RemoteAsset && asset.ownerId == user?.id;
|
||||
final isInLockedView = ref.watch(inLockedViewProvider);
|
||||
|
||||
final isShowingSheet = ref
|
||||
.watch(assetViewerProvider.select((state) => state.showingBottomSheet));
|
||||
final isShowingSheet = ref.watch(assetViewerProvider.select((state) => state.showingBottomSheet));
|
||||
int opacity = ref.watch(
|
||||
assetViewerProvider.select((state) => state.backgroundOpacity),
|
||||
);
|
||||
final showControls =
|
||||
ref.watch(assetViewerProvider.select((s) => s.showingControls));
|
||||
final showControls = ref.watch(assetViewerProvider.select((s) => s.showingControls));
|
||||
|
||||
if (!showControls) {
|
||||
opacity = 0;
|
||||
|
|
@ -45,8 +43,7 @@ class ViewerTopAppBar extends ConsumerWidget implements PreferredSizeWidget {
|
|||
final isCasting = ref.watch(
|
||||
castProvider.select((c) => c.isCasting),
|
||||
);
|
||||
final websocketConnected =
|
||||
ref.watch(websocketProvider.select((c) => c.isConnected));
|
||||
final websocketConnected = ref.watch(websocketProvider.select((c) => c.isConnected));
|
||||
|
||||
final actions = <Widget>[
|
||||
if (isCasting || (asset.hasRemote && websocketConnected))
|
||||
|
|
@ -78,8 +75,7 @@ class ViewerTopAppBar extends ConsumerWidget implements PreferredSizeWidget {
|
|||
opacity: opacity / 255,
|
||||
duration: Durations.short2,
|
||||
child: AppBar(
|
||||
backgroundColor:
|
||||
isShowingSheet ? Colors.transparent : Colors.black.withAlpha(125),
|
||||
backgroundColor: isShowingSheet ? Colors.transparent : Colors.black.withAlpha(125),
|
||||
leading: const _AppBarBackButton(),
|
||||
iconTheme: const IconThemeData(size: 22, color: Colors.white),
|
||||
actionsIconTheme: const IconThemeData(size: 22, color: Colors.white),
|
||||
|
|
@ -117,12 +113,9 @@ class _AppBarBackButton extends ConsumerWidget {
|
|||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final isShowingSheet = ref
|
||||
.watch(assetViewerProvider.select((state) => state.showingBottomSheet));
|
||||
final backgroundColor =
|
||||
isShowingSheet && !context.isDarkTheme ? Colors.white : Colors.black;
|
||||
final foregroundColor =
|
||||
isShowingSheet && !context.isDarkTheme ? Colors.black : Colors.white;
|
||||
final isShowingSheet = ref.watch(assetViewerProvider.select((state) => state.showingBottomSheet));
|
||||
final backgroundColor = isShowingSheet && !context.isDarkTheme ? Colors.white : Colors.black;
|
||||
final foregroundColor = isShowingSheet && !context.isDarkTheme ? Colors.black : Colors.white;
|
||||
|
||||
return Padding(
|
||||
padding: const EdgeInsets.only(left: 12.0),
|
||||
|
|
|
|||
|
|
@ -92,9 +92,7 @@ class NativeVideoViewer extends HookConsumerWidget {
|
|||
|
||||
try {
|
||||
if (asset.hasLocal && asset.livePhotoVideoId == null) {
|
||||
final id = asset is LocalAsset
|
||||
? (asset as LocalAsset).id
|
||||
: (asset as RemoteAsset).localId!;
|
||||
final id = asset is LocalAsset ? (asset as LocalAsset).id : (asset as RemoteAsset).localId!;
|
||||
final file = await const StorageRepository().getFileForAsset(id);
|
||||
if (file == null) {
|
||||
throw Exception('No file found for the video');
|
||||
|
|
@ -111,10 +109,8 @@ class NativeVideoViewer extends HookConsumerWidget {
|
|||
|
||||
// Use a network URL for the video player controller
|
||||
final serverEndpoint = Store.get(StoreKey.serverEndpoint);
|
||||
final isOriginalVideo =
|
||||
ref.read(settingsProvider).get<bool>(Setting.loadOriginalVideo);
|
||||
final String postfixUrl =
|
||||
isOriginalVideo ? 'original' : 'video/playback';
|
||||
final isOriginalVideo = ref.read(settingsProvider).get<bool>(Setting.loadOriginalVideo);
|
||||
final String postfixUrl = isOriginalVideo ? 'original' : 'video/playback';
|
||||
final String videoUrl = asset.livePhotoVideoId != null
|
||||
? '$serverEndpoint/assets/${asset.livePhotoVideoId}/$postfixUrl'
|
||||
: '$serverEndpoint/assets/$remoteId/$postfixUrl';
|
||||
|
|
@ -142,8 +138,7 @@ class NativeVideoViewer extends HookConsumerWidget {
|
|||
}
|
||||
|
||||
try {
|
||||
aspectRatio.value =
|
||||
await ref.read(assetServiceProvider).getAspectRatio(asset);
|
||||
aspectRatio.value = await ref.read(assetServiceProvider).getAspectRatio(asset);
|
||||
} catch (error) {
|
||||
log.severe(
|
||||
'Error getting aspect ratio for asset ${asset.name}: $error',
|
||||
|
|
@ -159,8 +154,7 @@ class NativeVideoViewer extends HookConsumerWidget {
|
|||
}
|
||||
|
||||
final videoPlayback = ref.read(videoPlaybackValueProvider);
|
||||
if ((isBuffering.value ||
|
||||
videoPlayback.state == VideoPlaybackState.initializing) &&
|
||||
if ((isBuffering.value || videoPlayback.state == VideoPlaybackState.initializing) &&
|
||||
videoPlayback.state != VideoPlaybackState.buffering) {
|
||||
ref.read(videoPlaybackValueProvider.notifier).value =
|
||||
videoPlayback.copyWith(state: VideoPlaybackState.buffering);
|
||||
|
|
@ -219,8 +213,7 @@ class NativeVideoViewer extends HookConsumerWidget {
|
|||
return;
|
||||
}
|
||||
|
||||
final videoPlayback =
|
||||
VideoPlaybackValue.fromNativeController(videoController);
|
||||
final videoPlayback = VideoPlaybackValue.fromNativeController(videoController);
|
||||
ref.read(videoPlaybackValueProvider.notifier).value = videoPlayback;
|
||||
|
||||
if (ref.read(assetViewerProvider.select((s) => s.showingBottomSheet))) {
|
||||
|
|
@ -241,8 +234,7 @@ class NativeVideoViewer extends HookConsumerWidget {
|
|||
return;
|
||||
}
|
||||
|
||||
final videoPlayback =
|
||||
VideoPlaybackValue.fromNativeController(videoController);
|
||||
final videoPlayback = VideoPlaybackValue.fromNativeController(videoController);
|
||||
if (videoPlayback.state == VideoPlaybackState.playing) {
|
||||
// Sync with the controls playing
|
||||
WakelockPlus.enable();
|
||||
|
|
@ -251,8 +243,7 @@ class NativeVideoViewer extends HookConsumerWidget {
|
|||
WakelockPlus.disable();
|
||||
}
|
||||
|
||||
ref.read(videoPlaybackValueProvider.notifier).status =
|
||||
videoPlayback.state;
|
||||
ref.read(videoPlaybackValueProvider.notifier).status = videoPlayback.state;
|
||||
}
|
||||
|
||||
void onPlaybackPositionChanged() {
|
||||
|
|
@ -271,8 +262,7 @@ class NativeVideoViewer extends HookConsumerWidget {
|
|||
return;
|
||||
}
|
||||
|
||||
ref.read(videoPlaybackValueProvider.notifier).position =
|
||||
Duration(seconds: playbackInfo.position);
|
||||
ref.read(videoPlaybackValueProvider.notifier).position = Duration(seconds: playbackInfo.position);
|
||||
|
||||
// Check if the video is buffering
|
||||
if (playbackInfo.status == PlaybackStatus.playing) {
|
||||
|
|
@ -296,10 +286,8 @@ class NativeVideoViewer extends HookConsumerWidget {
|
|||
}
|
||||
|
||||
void removeListeners(NativeVideoPlayerController controller) {
|
||||
controller.onPlaybackPositionChanged
|
||||
.removeListener(onPlaybackPositionChanged);
|
||||
controller.onPlaybackStatusChanged
|
||||
.removeListener(onPlaybackStatusChanged);
|
||||
controller.onPlaybackPositionChanged.removeListener(onPlaybackPositionChanged);
|
||||
controller.onPlaybackStatusChanged.removeListener(onPlaybackStatusChanged);
|
||||
controller.onPlaybackReady.removeListener(onPlaybackReady);
|
||||
controller.onPlaybackEnded.removeListener(onPlaybackEnded);
|
||||
}
|
||||
|
|
@ -324,9 +312,7 @@ class NativeVideoViewer extends HookConsumerWidget {
|
|||
nc.loadVideoSource(source).catchError((error) {
|
||||
log.severe('Error loading video source: $error');
|
||||
});
|
||||
final loopVideo = ref
|
||||
.read(appSettingsServiceProvider)
|
||||
.getSetting<bool>(AppSettingsEnum.loopVideo);
|
||||
final loopVideo = ref.read(appSettingsServiceProvider).getSetting<bool>(AppSettingsEnum.loopVideo);
|
||||
nc.setLoop(!asset.isMotionPhoto && loopVideo);
|
||||
|
||||
controller.value = nc;
|
||||
|
|
|
|||
|
|
@ -23,15 +23,12 @@ class VideoViewerControls extends HookConsumerWidget {
|
|||
final assetIsVideo = ref.watch(
|
||||
currentAssetNotifier.select((asset) => asset != null && asset.isVideo),
|
||||
);
|
||||
bool showControls =
|
||||
ref.watch(assetViewerProvider.select((s) => s.showingControls));
|
||||
final showBottomSheet =
|
||||
ref.watch(assetViewerProvider.select((s) => s.showingBottomSheet));
|
||||
bool showControls = ref.watch(assetViewerProvider.select((s) => s.showingControls));
|
||||
final showBottomSheet = ref.watch(assetViewerProvider.select((s) => s.showingBottomSheet));
|
||||
if (showBottomSheet) {
|
||||
showControls = false;
|
||||
}
|
||||
final VideoPlaybackState state =
|
||||
ref.watch(videoPlaybackValueProvider.select((value) => value.state));
|
||||
final VideoPlaybackState state = ref.watch(videoPlaybackValueProvider.select((value) => value.state));
|
||||
|
||||
final cast = ref.watch(castProvider);
|
||||
|
||||
|
|
@ -45,15 +42,12 @@ class VideoViewerControls extends HookConsumerWidget {
|
|||
final state = ref.read(videoPlaybackValueProvider).state;
|
||||
|
||||
// Do not hide on paused
|
||||
if (state != VideoPlaybackState.paused &&
|
||||
state != VideoPlaybackState.completed &&
|
||||
assetIsVideo) {
|
||||
if (state != VideoPlaybackState.paused && state != VideoPlaybackState.completed && assetIsVideo) {
|
||||
ref.read(assetViewerProvider.notifier).setControls(false);
|
||||
}
|
||||
},
|
||||
);
|
||||
final showBuffering =
|
||||
state == VideoPlaybackState.buffering && !cast.isCasting;
|
||||
final showBuffering = state == VideoPlaybackState.buffering && !cast.isCasting;
|
||||
|
||||
/// Shows the controls and starts the timer to hide them
|
||||
void showControlsAndStartHideTimer() {
|
||||
|
|
@ -62,8 +56,7 @@ class VideoViewerControls extends HookConsumerWidget {
|
|||
}
|
||||
|
||||
// When we change position, show or hide timer
|
||||
ref.listen(videoPlayerControlsProvider.select((v) => v.position),
|
||||
(previous, next) {
|
||||
ref.listen(videoPlayerControlsProvider.select((v) => v.position), (previous, next) {
|
||||
showControlsAndStartHideTimer();
|
||||
});
|
||||
|
||||
|
|
@ -111,14 +104,13 @@ class VideoViewerControls extends HookConsumerWidget {
|
|||
)
|
||||
else
|
||||
GestureDetector(
|
||||
onTap: () =>
|
||||
ref.read(assetViewerProvider.notifier).setControls(false),
|
||||
onTap: () => ref.read(assetViewerProvider.notifier).setControls(false),
|
||||
child: CenterPlayButton(
|
||||
backgroundColor: Colors.black54,
|
||||
iconColor: Colors.white,
|
||||
isFinished: state == VideoPlaybackState.completed,
|
||||
isPlaying: state == VideoPlaybackState.playing ||
|
||||
(cast.isCasting && cast.castState == CastState.playing),
|
||||
isPlaying:
|
||||
state == VideoPlaybackState.playing || (cast.isCasting && cast.castState == CastState.playing),
|
||||
show: assetIsVideo && showControls,
|
||||
onPressed: togglePlay,
|
||||
),
|
||||
|
|
|
|||
|
|
@ -21,8 +21,7 @@ class BackupToggleButton extends ConsumerStatefulWidget {
|
|||
ConsumerState<BackupToggleButton> createState() => BackupToggleButtonState();
|
||||
}
|
||||
|
||||
class BackupToggleButtonState extends ConsumerState<BackupToggleButton>
|
||||
with SingleTickerProviderStateMixin {
|
||||
class BackupToggleButtonState extends ConsumerState<BackupToggleButton> with SingleTickerProviderStateMixin {
|
||||
late AnimationController _animationController;
|
||||
late Animation<double> _gradientAnimation;
|
||||
bool _isEnabled = false;
|
||||
|
|
@ -42,9 +41,7 @@ class BackupToggleButtonState extends ConsumerState<BackupToggleButton>
|
|||
),
|
||||
);
|
||||
|
||||
_isEnabled = ref
|
||||
.read(appSettingsServiceProvider)
|
||||
.getSetting(AppSettingsEnum.enableBackup);
|
||||
_isEnabled = ref.read(appSettingsServiceProvider).getSetting(AppSettingsEnum.enableBackup);
|
||||
}
|
||||
|
||||
@override
|
||||
|
|
@ -54,9 +51,7 @@ class BackupToggleButtonState extends ConsumerState<BackupToggleButton>
|
|||
}
|
||||
|
||||
Future<void> _onToggle(bool value) async {
|
||||
await ref
|
||||
.read(appSettingsServiceProvider)
|
||||
.setSetting(AppSettingsEnum.enableBackup, value);
|
||||
await ref.read(appSettingsServiceProvider).setSetting(AppSettingsEnum.enableBackup, value);
|
||||
|
||||
setState(() {
|
||||
_isEnabled = value;
|
||||
|
|
@ -141,8 +136,7 @@ class BackupToggleButtonState extends ConsumerState<BackupToggleButton>
|
|||
borderRadius: const BorderRadius.all(Radius.circular(20.5)),
|
||||
onTap: () => isCanceling ? null : _onToggle(!_isEnabled),
|
||||
child: Padding(
|
||||
padding:
|
||||
const EdgeInsets.symmetric(horizontal: 20, vertical: 16),
|
||||
padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 16),
|
||||
child: Row(
|
||||
children: [
|
||||
Container(
|
||||
|
|
@ -180,8 +174,7 @@ class BackupToggleButtonState extends ConsumerState<BackupToggleButton>
|
|||
children: [
|
||||
Text(
|
||||
"enable_backup".t(context: context),
|
||||
style:
|
||||
context.textTheme.titleMedium?.copyWith(
|
||||
style: context.textTheme.titleMedium?.copyWith(
|
||||
fontWeight: FontWeight.w600,
|
||||
color: context.primaryColor,
|
||||
),
|
||||
|
|
@ -214,9 +207,7 @@ class BackupToggleButtonState extends ConsumerState<BackupToggleButton>
|
|||
height: 18,
|
||||
child: CircularProgressIndicator(
|
||||
strokeWidth: 2,
|
||||
backgroundColor: context
|
||||
.colorScheme.onSurface
|
||||
.withValues(alpha: 0.2),
|
||||
backgroundColor: context.colorScheme.onSurface.withValues(alpha: 0.2),
|
||||
),
|
||||
),
|
||||
],
|
||||
|
|
@ -226,8 +217,7 @@ class BackupToggleButtonState extends ConsumerState<BackupToggleButton>
|
|||
),
|
||||
Switch.adaptive(
|
||||
value: _isEnabled,
|
||||
onChanged: (value) =>
|
||||
isCanceling ? null : _onToggle(value),
|
||||
onChanged: (value) => isCanceling ? null : _onToggle(value),
|
||||
),
|
||||
],
|
||||
),
|
||||
|
|
|
|||
|
|
@ -31,12 +31,10 @@ class BaseBottomSheet extends ConsumerStatefulWidget {
|
|||
});
|
||||
|
||||
@override
|
||||
ConsumerState<BaseBottomSheet> createState() =>
|
||||
_BaseDraggableScrollableSheetState();
|
||||
ConsumerState<BaseBottomSheet> createState() => _BaseDraggableScrollableSheetState();
|
||||
}
|
||||
|
||||
class _BaseDraggableScrollableSheetState
|
||||
extends ConsumerState<BaseBottomSheet> {
|
||||
class _BaseDraggableScrollableSheetState extends ConsumerState<BaseBottomSheet> {
|
||||
late DraggableScrollableController _controller;
|
||||
|
||||
@override
|
||||
|
|
@ -71,8 +69,7 @@ class _BaseDraggableScrollableSheetState
|
|||
shouldCloseOnMinExtent: widget.shouldCloseOnMinExtent,
|
||||
builder: (BuildContext context, ScrollController scrollController) {
|
||||
return Card(
|
||||
color: widget.backgroundColor ??
|
||||
context.colorScheme.surfaceContainerHigh,
|
||||
color: widget.backgroundColor ?? context.colorScheme.surfaceContainerHigh,
|
||||
borderOnForeground: false,
|
||||
clipBehavior: Clip.antiAlias,
|
||||
elevation: 6.0,
|
||||
|
|
|
|||
|
|
@ -71,5 +71,4 @@ ImageProvider getThumbnailImageProvider({
|
|||
}
|
||||
|
||||
bool _shouldUseLocalAsset(BaseAsset asset) =>
|
||||
asset.hasLocal &&
|
||||
(!asset.hasRemote || !AppSetting.get(Setting.preferRemoteImage));
|
||||
asset.hasLocal && (!asset.hasRemote || !AppSetting.get(Setting.preferRemoteImage));
|
||||
|
|
|
|||
|
|
@ -17,8 +17,7 @@ import 'package:immich_mobile/providers/image/exceptions/image_loading_exception
|
|||
import 'package:logging/logging.dart';
|
||||
|
||||
class LocalThumbProvider extends ImageProvider<LocalThumbProvider> {
|
||||
final AssetMediaRepository _assetMediaRepository =
|
||||
const AssetMediaRepository();
|
||||
final AssetMediaRepository _assetMediaRepository = const AssetMediaRepository();
|
||||
final CacheManager? cacheManager;
|
||||
|
||||
final String id;
|
||||
|
|
@ -63,20 +62,17 @@ class LocalThumbProvider extends ImageProvider<LocalThumbProvider> {
|
|||
CacheManager cache,
|
||||
ImageDecoderCallback decode,
|
||||
) async {
|
||||
final cacheKey =
|
||||
'${key.id}-${key.updatedAt}-${key.size.width}x${key.size.height}';
|
||||
final cacheKey = '${key.id}-${key.updatedAt}-${key.size.width}x${key.size.height}';
|
||||
|
||||
final fileFromCache = await cache.getFileFromCache(cacheKey);
|
||||
if (fileFromCache != null) {
|
||||
try {
|
||||
final buffer =
|
||||
await ImmutableBuffer.fromFilePath(fileFromCache.file.path);
|
||||
final buffer = await ImmutableBuffer.fromFilePath(fileFromCache.file.path);
|
||||
return decode(buffer);
|
||||
} catch (_) {}
|
||||
}
|
||||
|
||||
final thumbnailBytes =
|
||||
await _assetMediaRepository.getThumbnail(key.id, size: key.size);
|
||||
final thumbnailBytes = await _assetMediaRepository.getThumbnail(key.id, size: key.size);
|
||||
if (thumbnailBytes == null) {
|
||||
PaintingBinding.instance.imageCache.evict(key);
|
||||
throw StateError(
|
||||
|
|
@ -103,8 +99,7 @@ class LocalThumbProvider extends ImageProvider<LocalThumbProvider> {
|
|||
}
|
||||
|
||||
class LocalFullImageProvider extends ImageProvider<LocalFullImageProvider> {
|
||||
final AssetMediaRepository _assetMediaRepository =
|
||||
const AssetMediaRepository();
|
||||
final AssetMediaRepository _assetMediaRepository = const AssetMediaRepository();
|
||||
final StorageRepository _storageRepository = const StorageRepository();
|
||||
|
||||
final String id;
|
||||
|
|
@ -160,8 +155,7 @@ class LocalFullImageProvider extends ImageProvider<LocalFullImageProvider> {
|
|||
throw StateError('Unsupported asset type ${key.type}');
|
||||
}
|
||||
} catch (error, stack) {
|
||||
Logger('ImmichLocalImageProvider')
|
||||
.severe('Error loading local image ${key.name}', error, stack);
|
||||
Logger('ImmichLocalImageProvider').severe('Error loading local image ${key.name}', error, stack);
|
||||
throw const ImageLoadingException(
|
||||
'Could not load image from local storage',
|
||||
);
|
||||
|
|
@ -172,8 +166,7 @@ class LocalFullImageProvider extends ImageProvider<LocalFullImageProvider> {
|
|||
LocalFullImageProvider key,
|
||||
ImageDecoderCallback decode,
|
||||
) async {
|
||||
final thumbBytes =
|
||||
await _assetMediaRepository.getThumbnail(key.id, size: key.size);
|
||||
final thumbBytes = await _assetMediaRepository.getThumbnail(key.id, size: key.size);
|
||||
if (thumbBytes == null) {
|
||||
return null;
|
||||
}
|
||||
|
|
@ -191,8 +184,7 @@ class LocalFullImageProvider extends ImageProvider<LocalFullImageProvider> {
|
|||
}
|
||||
|
||||
final fileSize = await file.length();
|
||||
final devicePixelRatio =
|
||||
PlatformDispatcher.instance.views.first.devicePixelRatio;
|
||||
final devicePixelRatio = PlatformDispatcher.instance.views.first.devicePixelRatio;
|
||||
final isLargeFile = fileSize > 20 * 1024 * 1024; // 20MB
|
||||
final isHEIC = file.path.toLowerCase().contains(RegExp(r'\.(heic|heif)$'));
|
||||
final isProgressive = isLargeFile || (isHEIC && !Platform.isIOS);
|
||||
|
|
@ -204,8 +196,7 @@ class LocalFullImageProvider extends ImageProvider<LocalFullImageProvider> {
|
|||
(key.size.width * progressiveMultiplier).clamp(256, 1024),
|
||||
(key.size.height * progressiveMultiplier).clamp(256, 1024),
|
||||
);
|
||||
final mediumThumb =
|
||||
await _assetMediaRepository.getThumbnail(key.id, size: size);
|
||||
final mediumThumb = await _assetMediaRepository.getThumbnail(key.id, size: size);
|
||||
if (mediumThumb != null) {
|
||||
final mediumBuffer = await ImmutableBuffer.fromUint8List(mediumThumb);
|
||||
yield await decode(mediumBuffer);
|
||||
|
|
@ -221,8 +212,7 @@ class LocalFullImageProvider extends ImageProvider<LocalFullImageProvider> {
|
|||
(key.size.width * progressiveMultiplier).clamp(512, 2048),
|
||||
(key.size.height * progressiveMultiplier).clamp(512, 2048),
|
||||
);
|
||||
final highThumb =
|
||||
await _assetMediaRepository.getThumbnail(key.id, size: size);
|
||||
final highThumb = await _assetMediaRepository.getThumbnail(key.id, size: size);
|
||||
if (highThumb != null) {
|
||||
final highBuffer = await ImmutableBuffer.fromUint8List(highThumb);
|
||||
yield await decode(highBuffer);
|
||||
|
|
@ -238,15 +228,11 @@ class LocalFullImageProvider extends ImageProvider<LocalFullImageProvider> {
|
|||
bool operator ==(Object other) {
|
||||
if (identical(this, other)) return true;
|
||||
if (other is LocalFullImageProvider) {
|
||||
return id == other.id &&
|
||||
size == other.size &&
|
||||
type == other.type &&
|
||||
name == other.name;
|
||||
return id == other.id && size == other.size && type == other.type && name == other.name;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode =>
|
||||
id.hashCode ^ size.hashCode ^ type.hashCode ^ name.hashCode;
|
||||
int get hashCode => id.hashCode ^ size.hashCode ^ type.hashCode ^ name.hashCode;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -26,10 +26,8 @@ class Thumbnail extends StatelessWidget {
|
|||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final thumbHash =
|
||||
asset is RemoteAsset ? (asset as RemoteAsset).thumbHash : null;
|
||||
final provider =
|
||||
getThumbnailImageProvider(asset: asset, remoteId: remoteId, size: size);
|
||||
final thumbHash = asset is RemoteAsset ? (asset as RemoteAsset).thumbHash : null;
|
||||
final provider = getThumbnailImageProvider(asset: asset, remoteId: remoteId, size: size);
|
||||
|
||||
return OctoImage.fromSet(
|
||||
image: provider,
|
||||
|
|
@ -72,8 +70,7 @@ OctoErrorBuilder _blurHashErrorBuilder(
|
|||
BoxFit? fit,
|
||||
}) =>
|
||||
(context, e, s) {
|
||||
Logger("ImThumbnail")
|
||||
.warning("Error loading thumbnail for ${asset?.name}", e, s);
|
||||
Logger("ImThumbnail").warning("Error loading thumbnail for ${asset?.name}", e, s);
|
||||
provider?.evict();
|
||||
return Stack(
|
||||
alignment: Alignment.center,
|
||||
|
|
|
|||
|
|
@ -28,9 +28,8 @@ class ThumbnailTile extends ConsumerWidget {
|
|||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final heroOffset = TabsRouterScope.of(context)?.controller.activeIndex ?? 0;
|
||||
|
||||
final assetContainerColor = context.isDarkTheme
|
||||
? context.primaryColor.darken(amount: 0.4)
|
||||
: context.primaryColor.lighten(amount: 0.75);
|
||||
final assetContainerColor =
|
||||
context.isDarkTheme ? context.primaryColor.darken(amount: 0.4) : context.primaryColor.lighten(amount: 0.75);
|
||||
|
||||
final isSelected = ref.watch(
|
||||
multiSelectProvider.select(
|
||||
|
|
@ -53,8 +52,7 @@ class ThumbnailTile extends ConsumerWidget {
|
|||
)
|
||||
: const BoxDecoration();
|
||||
|
||||
final hasStack =
|
||||
asset is RemoteAsset && (asset as RemoteAsset).stackId != null;
|
||||
final hasStack = asset is RemoteAsset && (asset as RemoteAsset).stackId != null;
|
||||
|
||||
return Stack(
|
||||
children: [
|
||||
|
|
@ -63,9 +61,8 @@ class ThumbnailTile extends ConsumerWidget {
|
|||
curve: Curves.decelerate,
|
||||
decoration: borderStyle,
|
||||
child: ClipRRect(
|
||||
borderRadius: isSelected || lockSelection
|
||||
? const BorderRadius.all(Radius.circular(15.0))
|
||||
: BorderRadius.zero,
|
||||
borderRadius:
|
||||
isSelected || lockSelection ? const BorderRadius.all(Radius.circular(15.0)) : BorderRadius.zero,
|
||||
child: Stack(
|
||||
children: [
|
||||
Positioned.fill(
|
||||
|
|
@ -141,9 +138,7 @@ class ThumbnailTile extends ConsumerWidget {
|
|||
child: _SelectionIndicator(
|
||||
isSelected: isSelected,
|
||||
isLocked: lockSelection,
|
||||
color: lockSelection
|
||||
? context.colorScheme.surfaceContainerHighest
|
||||
: assetContainerColor,
|
||||
color: lockSelection ? context.colorScheme.surfaceContainerHighest : assetContainerColor,
|
||||
),
|
||||
),
|
||||
),
|
||||
|
|
|
|||
|
|
@ -46,11 +46,9 @@ class DriftMemoryCard extends StatelessWidget {
|
|||
BoxFit fit = BoxFit.contain;
|
||||
if (asset.width != null && asset.height != null) {
|
||||
final aspectRatio = asset.width! / asset.height!;
|
||||
final phoneAspectRatio =
|
||||
constraints.maxWidth / constraints.maxHeight;
|
||||
final phoneAspectRatio = constraints.maxWidth / constraints.maxHeight;
|
||||
// Look for a 25% difference in either direction
|
||||
if (phoneAspectRatio * .75 < aspectRatio &&
|
||||
phoneAspectRatio * 1.25 > aspectRatio) {
|
||||
if (phoneAspectRatio * .75 < aspectRatio && phoneAspectRatio * 1.25 > aspectRatio) {
|
||||
// Cover to look nice if we have nearly the same aspect ratio
|
||||
fit = BoxFit.cover;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -47,8 +47,7 @@ class DriftMemoryLane extends ConsumerWidget {
|
|||
),
|
||||
);
|
||||
},
|
||||
children:
|
||||
memories.map((memory) => DriftMemoryCard(memory: memory)).toList(),
|
||||
children: memories.map((memory) => DriftMemoryCard(memory: memory)).toList(),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,8 +13,7 @@ class PartnerUserAvatar extends StatelessWidget {
|
|||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final url =
|
||||
"${Store.get(StoreKey.serverEndpoint)}/users/${partner.id}/profile-image";
|
||||
final url = "${Store.get(StoreKey.serverEndpoint)}/users/${partner.id}/profile-image";
|
||||
final nameFirstLetter = partner.name.isNotEmpty ? partner.name[0] : "";
|
||||
return CircleAvatar(
|
||||
radius: 16,
|
||||
|
|
|
|||
|
|
@ -97,14 +97,12 @@ class DriftRemoteAlbumOption extends ConsumerWidget {
|
|||
ListTile(
|
||||
leading: Icon(
|
||||
Icons.delete,
|
||||
color:
|
||||
context.isDarkTheme ? Colors.red[400] : Colors.red[800],
|
||||
color: context.isDarkTheme ? Colors.red[400] : Colors.red[800],
|
||||
),
|
||||
title: Text(
|
||||
'delete_album'.t(context: context),
|
||||
style: textStyle.copyWith(
|
||||
color:
|
||||
context.isDarkTheme ? Colors.red[400] : Colors.red[800],
|
||||
color: context.isDarkTheme ? Colors.red[400] : Colors.red[800],
|
||||
),
|
||||
),
|
||||
onTap: onDeleteAlbum,
|
||||
|
|
|
|||
|
|
@ -90,8 +90,7 @@ class RenderFixedRow extends RenderBox
|
|||
}
|
||||
}
|
||||
|
||||
double get intrinsicWidth =>
|
||||
dimension * childCount + spacing * (childCount - 1);
|
||||
double get intrinsicWidth => dimension * childCount + spacing * (childCount - 1);
|
||||
|
||||
@override
|
||||
double computeMinIntrinsicWidth(double height) => intrinsicWidth;
|
||||
|
|
|
|||
|
|
@ -39,9 +39,7 @@ class FixedSegment extends Segment {
|
|||
@override
|
||||
double indexToLayoutOffset(int index) {
|
||||
final relativeIndex = index - gridIndex;
|
||||
return relativeIndex < 0
|
||||
? startOffset
|
||||
: gridOffset + (mainAxisExtend * relativeIndex);
|
||||
return relativeIndex < 0 ? startOffset : gridOffset + (mainAxisExtend * relativeIndex);
|
||||
}
|
||||
|
||||
@override
|
||||
|
|
@ -98,8 +96,7 @@ class _FixedSegmentRow extends ConsumerWidget {
|
|||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final isScrubbing =
|
||||
ref.watch(timelineStateProvider.select((s) => s.isScrubbing));
|
||||
final isScrubbing = ref.watch(timelineStateProvider.select((s) => s.isScrubbing));
|
||||
final timelineService = ref.read(timelineServiceProvider);
|
||||
|
||||
if (isScrubbing) {
|
||||
|
|
@ -215,11 +212,8 @@ class _AssetTileWidget extends ConsumerWidget {
|
|||
|
||||
return RepaintBoundary(
|
||||
child: GestureDetector(
|
||||
onTap: () => lockSelection
|
||||
? null
|
||||
: _handleOnTap(context, ref, assetIndex, asset),
|
||||
onLongPress: () =>
|
||||
lockSelection ? null : _handleOnLongPress(ref, asset),
|
||||
onTap: () => lockSelection ? null : _handleOnTap(context, ref, assetIndex, asset),
|
||||
onLongPress: () => lockSelection ? null : _handleOnLongPress(ref, asset),
|
||||
child: ThumbnailTile(
|
||||
asset,
|
||||
lockSelection: lockSelection,
|
||||
|
|
|
|||
|
|
@ -37,17 +37,13 @@ class FixedSegmentBuilder extends SegmentBuilder {
|
|||
GroupAssetsBy.month => HeaderType.month,
|
||||
GroupAssetsBy.day ||
|
||||
GroupAssetsBy.auto =>
|
||||
bucket is TimeBucket && bucket.date.month != previousDate?.month
|
||||
? HeaderType.monthAndDay
|
||||
: HeaderType.day,
|
||||
bucket is TimeBucket && bucket.date.month != previousDate?.month ? HeaderType.monthAndDay : HeaderType.day,
|
||||
GroupAssetsBy.none => HeaderType.none,
|
||||
};
|
||||
final headerExtent = SegmentBuilder.headerExtent(timelineHeader);
|
||||
|
||||
final segmentStartOffset = startOffset;
|
||||
startOffset += headerExtent +
|
||||
(tileHeight * numberOfRows) +
|
||||
spacing * (numberOfRows - 1);
|
||||
startOffset += headerExtent + (tileHeight * numberOfRows) + spacing * (numberOfRows - 1);
|
||||
final segmentEndOffset = startOffset;
|
||||
|
||||
segments.add(
|
||||
|
|
|
|||
|
|
@ -43,10 +43,8 @@ class TimelineHeader extends StatelessWidget {
|
|||
|
||||
final date = (bucket as TimeBucket).date;
|
||||
|
||||
final isMonthHeader =
|
||||
header == HeaderType.month || header == HeaderType.monthAndDay;
|
||||
final isDayHeader =
|
||||
header == HeaderType.day || header == HeaderType.monthAndDay;
|
||||
final isMonthHeader = header == HeaderType.month || header == HeaderType.monthAndDay;
|
||||
final isDayHeader = header == HeaderType.day || header == HeaderType.monthAndDay;
|
||||
|
||||
return Padding(
|
||||
padding: EdgeInsets.only(
|
||||
|
|
@ -111,9 +109,7 @@ class _BulkSelectIconButton extends ConsumerWidget {
|
|||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
List<BaseAsset> bucketAssets;
|
||||
try {
|
||||
bucketAssets = ref
|
||||
.watch(timelineServiceProvider)
|
||||
.getAssets(assetOffset, bucket.assetCount);
|
||||
bucketAssets = ref.watch(timelineServiceProvider).getAssets(assetOffset, bucket.assetCount);
|
||||
} catch (e) {
|
||||
bucketAssets = <BaseAsset>[];
|
||||
}
|
||||
|
|
|
|||
|
|
@ -58,15 +58,13 @@ List<_Segment> _buildSegments({
|
|||
DateTime? lastDate;
|
||||
double lastOffset = -offsetThreshold;
|
||||
for (final layoutSegment in layoutSegments) {
|
||||
final scrollPercentage =
|
||||
layoutSegment.startOffset / layoutSegments.last.endOffset;
|
||||
final scrollPercentage = layoutSegment.startOffset / layoutSegments.last.endOffset;
|
||||
final startOffset = scrollPercentage * timelineHeight;
|
||||
|
||||
final date = (layoutSegment.bucket as TimeBucket).date;
|
||||
final label = formatter.format(date);
|
||||
|
||||
final showSegment = lastOffset + offsetThreshold <= startOffset &&
|
||||
(lastDate == null || date.year != lastDate.year);
|
||||
final showSegment = lastOffset + offsetThreshold <= startOffset && (lastDate == null || date.year != lastDate.year);
|
||||
|
||||
segments.add(
|
||||
_Segment(
|
||||
|
|
@ -85,8 +83,7 @@ List<_Segment> _buildSegments({
|
|||
return segments;
|
||||
}
|
||||
|
||||
class ScrubberState extends ConsumerState<Scrubber>
|
||||
with TickerProviderStateMixin {
|
||||
class ScrubberState extends ConsumerState<Scrubber> with TickerProviderStateMixin {
|
||||
double _thumbTopOffset = 0.0;
|
||||
bool _isDragging = false;
|
||||
List<_Segment> _segments = [];
|
||||
|
|
@ -98,17 +95,14 @@ class ScrubberState extends ConsumerState<Scrubber>
|
|||
late AnimationController _labelAnimationController;
|
||||
late Animation<double> _labelAnimation;
|
||||
|
||||
double get _scrubberHeight =>
|
||||
widget.timelineHeight - widget.topPadding - widget.bottomPadding;
|
||||
double get _scrubberHeight => widget.timelineHeight - widget.topPadding - widget.bottomPadding;
|
||||
|
||||
late ScrollController _scrollController;
|
||||
|
||||
double get _currentOffset {
|
||||
if (_scrollController.hasClients != true) return 0.0;
|
||||
|
||||
return _scrollController.offset *
|
||||
_scrubberHeight /
|
||||
_scrollController.position.maxScrollExtent;
|
||||
return _scrollController.offset * _scrubberHeight / _scrollController.position.maxScrollExtent;
|
||||
}
|
||||
|
||||
@override
|
||||
|
|
@ -148,8 +142,7 @@ class ScrubberState extends ConsumerState<Scrubber>
|
|||
void didUpdateWidget(covariant Scrubber oldWidget) {
|
||||
super.didUpdateWidget(oldWidget);
|
||||
|
||||
if (oldWidget.layoutSegments.lastOrNull?.endOffset !=
|
||||
widget.layoutSegments.lastOrNull?.endOffset) {
|
||||
if (oldWidget.layoutSegments.lastOrNull?.endOffset != widget.layoutSegments.lastOrNull?.endOffset) {
|
||||
_segments = _buildSegments(
|
||||
layoutSegments: widget.layoutSegments,
|
||||
timelineHeight: _scrubberHeight,
|
||||
|
|
@ -179,8 +172,7 @@ class ScrubberState extends ConsumerState<Scrubber>
|
|||
return false;
|
||||
}
|
||||
|
||||
if (notification is ScrollStartNotification ||
|
||||
notification is ScrollUpdateNotification) {
|
||||
if (notification is ScrollStartNotification || notification is ScrollUpdateNotification) {
|
||||
ref.read(timelineStateProvider.notifier).setScrolling(true);
|
||||
} else if (notification is ScrollEndNotification) {
|
||||
ref.read(timelineStateProvider.notifier).setScrolling(false);
|
||||
|
|
@ -287,8 +279,7 @@ class ScrubberState extends ConsumerState<Scrubber>
|
|||
return widget.layoutSegments.indexWhere(
|
||||
(layoutSegment) {
|
||||
final bucket = layoutSegment.bucket as TimeBucket;
|
||||
return bucket.date.year == segment.date.year &&
|
||||
bucket.date.month == segment.date.month;
|
||||
return bucket.date.year == segment.date.year && bucket.date.month == segment.date.month;
|
||||
},
|
||||
);
|
||||
}
|
||||
|
|
@ -299,10 +290,7 @@ class ScrubberState extends ConsumerState<Scrubber>
|
|||
final viewportHeight = _scrollController.position.viewportDimension;
|
||||
|
||||
final targetScrollOffset = layoutSegment.startOffset;
|
||||
final centeredOffset = targetScrollOffset -
|
||||
(viewportHeight / 4) +
|
||||
100 +
|
||||
(widget.monthSegmentSnappingOffset ?? 0.0);
|
||||
final centeredOffset = targetScrollOffset - (viewportHeight / 4) + 100 + (widget.monthSegmentSnappingOffset ?? 0.0);
|
||||
|
||||
_scrollController.jumpTo(centeredOffset.clamp(0.0, maxScrollExtent));
|
||||
}
|
||||
|
|
@ -354,8 +342,7 @@ class ScrubberState extends ConsumerState<Scrubber>
|
|||
isDragging: _isDragging,
|
||||
),
|
||||
),
|
||||
if (_scrollController.hasClients &&
|
||||
_scrollController.position.maxScrollExtent > 0)
|
||||
if (_scrollController.hasClients && _scrollController.position.maxScrollExtent > 0)
|
||||
PositionedDirectional(
|
||||
top: _thumbTopOffset + widget.topPadding,
|
||||
end: 0,
|
||||
|
|
@ -492,9 +479,8 @@ class _Scrubber extends StatelessWidget {
|
|||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final backgroundColor = context.isDarkTheme
|
||||
? context.colorScheme.primary.darken(amount: .5)
|
||||
: context.colorScheme.primary;
|
||||
final backgroundColor =
|
||||
context.isDarkTheme ? context.colorScheme.primary.darken(amount: .5) : context.colorScheme.primary;
|
||||
|
||||
return _SlideFadeTransition(
|
||||
animation: thumbAnimation,
|
||||
|
|
@ -590,8 +576,7 @@ class _SlideFadeTransition extends StatelessWidget {
|
|||
Widget build(BuildContext context) {
|
||||
return AnimatedBuilder(
|
||||
animation: _animation,
|
||||
builder: (context, child) =>
|
||||
_animation.value == 0.0 ? const SizedBox() : child!,
|
||||
builder: (context, child) => _animation.value == 0.0 ? const SizedBox() : child!,
|
||||
child: SlideTransition(
|
||||
position: Tween(
|
||||
begin: const Offset(0.3, 0.0),
|
||||
|
|
|
|||
|
|
@ -42,8 +42,7 @@ abstract class Segment {
|
|||
|
||||
bool containsIndex(int index) => firstIndex <= index && index <= lastIndex;
|
||||
|
||||
bool isWithinOffset(double offset) =>
|
||||
startOffset <= offset && offset <= endOffset;
|
||||
bool isWithinOffset(double offset) => startOffset <= offset && offset <= endOffset;
|
||||
|
||||
int getMinChildIndexForScrollOffset(double scrollOffset);
|
||||
int getMaxChildIndexForScrollOffset(double scrollOffset);
|
||||
|
|
@ -88,13 +87,9 @@ abstract class Segment {
|
|||
}
|
||||
|
||||
extension SegmentListExtension on List<Segment> {
|
||||
bool equals(List<Segment> other) =>
|
||||
length == other.length &&
|
||||
lastOrNull?.endOffset == other.lastOrNull?.endOffset;
|
||||
bool equals(List<Segment> other) => length == other.length && lastOrNull?.endOffset == other.lastOrNull?.endOffset;
|
||||
|
||||
Segment? findByIndex(int index) =>
|
||||
firstWhereOrNull((s) => s.containsIndex(index));
|
||||
Segment? findByIndex(int index) => firstWhereOrNull((s) => s.containsIndex(index));
|
||||
|
||||
Segment? findByOffset(double offset) =>
|
||||
firstWhereOrNull((s) => s.isWithinOffset(offset)) ?? lastOrNull;
|
||||
Segment? findByOffset(double offset) => firstWhereOrNull((s) => s.isWithinOffset(offset)) ?? lastOrNull;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -105,9 +105,7 @@ final timelineSegmentProvider = StreamProvider.autoDispose<List<Segment>>(
|
|||
final availableTileWidth = args.maxWidth - (spacing * (columnCount - 1));
|
||||
final tileExtent = math.max(0, availableTileWidth) / columnCount;
|
||||
|
||||
final groupBy = args.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) {
|
||||
|
|
@ -123,7 +121,6 @@ final timelineSegmentProvider = StreamProvider.autoDispose<List<Segment>>(
|
|||
dependencies: [timelineServiceProvider, timelineArgsProvider],
|
||||
);
|
||||
|
||||
final timelineStateProvider =
|
||||
NotifierProvider<TimelineStateNotifier, TimelineState>(
|
||||
final timelineStateProvider = NotifierProvider<TimelineStateNotifier, TimelineState>(
|
||||
TimelineStateNotifier.new,
|
||||
);
|
||||
|
|
|
|||
|
|
@ -101,8 +101,7 @@ class _SliverTimelineState extends ConsumerState<_SliverTimeline> {
|
|||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_reloadSubscription =
|
||||
EventStream.shared.listen<TimelineReloadEvent>((_) => setState(() {}));
|
||||
_reloadSubscription = EventStream.shared.listen<TimelineReloadEvent>((_) => setState(() {}));
|
||||
}
|
||||
|
||||
@override
|
||||
|
|
@ -115,8 +114,7 @@ class _SliverTimelineState extends ConsumerState<_SliverTimeline> {
|
|||
@override
|
||||
Widget build(BuildContext _) {
|
||||
final asyncSegments = ref.watch(timelineSegmentProvider);
|
||||
final maxHeight =
|
||||
ref.watch(timelineArgsProvider.select((args) => args.maxHeight));
|
||||
final maxHeight = ref.watch(timelineArgsProvider.select((args) => args.maxHeight));
|
||||
final isSelectionMode = ref.watch(
|
||||
multiSelectProvider.select((s) => s.forceEnable),
|
||||
);
|
||||
|
|
@ -124,17 +122,11 @@ class _SliverTimelineState extends ConsumerState<_SliverTimeline> {
|
|||
return asyncSegments.widgetWhen(
|
||||
onData: (segments) {
|
||||
final childCount = (segments.lastOrNull?.lastIndex ?? -1) + 1;
|
||||
final double appBarExpandedHeight =
|
||||
widget.appBar != null && widget.appBar is MesmerizingSliverAppBar
|
||||
? 200
|
||||
: 0;
|
||||
final topPadding = context.padding.top +
|
||||
(widget.appBar == null ? 0 : kToolbarHeight) +
|
||||
10;
|
||||
final double appBarExpandedHeight = widget.appBar != null && widget.appBar is MesmerizingSliverAppBar ? 200 : 0;
|
||||
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);
|
||||
final bottomPadding = context.padding.bottom + (widget.appBar == null ? 0 : scrubberBottomPadding);
|
||||
|
||||
return PrimaryScrollController(
|
||||
controller: _scrollController,
|
||||
|
|
@ -145,16 +137,12 @@ class _SliverTimelineState extends ConsumerState<_SliverTimeline> {
|
|||
timelineHeight: maxHeight,
|
||||
topPadding: topPadding,
|
||||
bottomPadding: bottomPadding,
|
||||
monthSegmentSnappingOffset:
|
||||
widget.topSliverWidgetHeight ?? 0 + appBarExpandedHeight,
|
||||
monthSegmentSnappingOffset: widget.topSliverWidgetHeight ?? 0 + appBarExpandedHeight,
|
||||
child: CustomScrollView(
|
||||
primary: true,
|
||||
cacheExtent: maxHeight * 2,
|
||||
slivers: [
|
||||
if (isSelectionMode)
|
||||
const SelectionSliverAppBar()
|
||||
else if (widget.appBar != null)
|
||||
widget.appBar!,
|
||||
if (isSelectionMode) const SelectionSliverAppBar() else if (widget.appBar != null) widget.appBar!,
|
||||
if (widget.topSliverWidget != null) widget.topSliverWidget!,
|
||||
_SliverSegmentedList(
|
||||
segments: segments,
|
||||
|
|
@ -162,8 +150,7 @@ class _SliverTimelineState extends ConsumerState<_SliverTimeline> {
|
|||
(ctx, index) {
|
||||
if (index >= childCount) return null;
|
||||
final segment = segments.findByIndex(index);
|
||||
return segment?.builder(ctx, index) ??
|
||||
const SizedBox.shrink();
|
||||
return segment?.builder(ctx, index) ?? const SizedBox.shrink();
|
||||
},
|
||||
childCount: childCount,
|
||||
addAutomaticKeepAlives: false,
|
||||
|
|
@ -233,8 +220,7 @@ class _SliverSegmentedList extends SliverMultiBoxAdaptorWidget {
|
|||
}) : _segments = segments;
|
||||
|
||||
@override
|
||||
_RenderSliverTimelineBoxAdaptor createRenderObject(BuildContext context) =>
|
||||
_RenderSliverTimelineBoxAdaptor(
|
||||
_RenderSliverTimelineBoxAdaptor createRenderObject(BuildContext context) => _RenderSliverTimelineBoxAdaptor(
|
||||
childManager: context as SliverMultiBoxAdaptorElement,
|
||||
segments: _segments,
|
||||
);
|
||||
|
|
@ -266,17 +252,13 @@ class _RenderSliverTimelineBoxAdaptor extends RenderSliverMultiBoxAdaptor {
|
|||
}) : _segments = segments;
|
||||
|
||||
int getMinChildIndexForScrollOffset(double offset) =>
|
||||
_segments.findByOffset(offset)?.getMinChildIndexForScrollOffset(offset) ??
|
||||
0;
|
||||
_segments.findByOffset(offset)?.getMinChildIndexForScrollOffset(offset) ?? 0;
|
||||
|
||||
int getMaxChildIndexForScrollOffset(double offset) =>
|
||||
_segments.findByOffset(offset)?.getMaxChildIndexForScrollOffset(offset) ??
|
||||
0;
|
||||
_segments.findByOffset(offset)?.getMaxChildIndexForScrollOffset(offset) ?? 0;
|
||||
|
||||
double indexToLayoutOffset(int index) =>
|
||||
(_segments.findByIndex(index) ?? _segments.lastOrNull)
|
||||
?.indexToLayoutOffset(index) ??
|
||||
0;
|
||||
(_segments.findByIndex(index) ?? _segments.lastOrNull)?.indexToLayoutOffset(index) ?? 0;
|
||||
|
||||
double estimateMaxScrollOffset() => _segments.lastOrNull?.endOffset ?? 0;
|
||||
|
||||
|
|
@ -288,8 +270,7 @@ class _RenderSliverTimelineBoxAdaptor extends RenderSliverMultiBoxAdaptor {
|
|||
// Assume initially that we have enough children to fill the viewport/cache area.
|
||||
childManager.setDidUnderflow(false);
|
||||
|
||||
final double scrollOffset =
|
||||
constraints.scrollOffset + constraints.cacheOrigin;
|
||||
final double scrollOffset = constraints.scrollOffset + constraints.cacheOrigin;
|
||||
assert(scrollOffset >= 0.0);
|
||||
|
||||
final double remainingExtent = constraints.remainingCacheExtent;
|
||||
|
|
@ -298,31 +279,26 @@ class _RenderSliverTimelineBoxAdaptor extends RenderSliverMultiBoxAdaptor {
|
|||
final double targetScrollOffset = scrollOffset + remainingExtent;
|
||||
|
||||
// Find the index of the first child that should be visible or in the leading cache area.
|
||||
final int firstRequiredChildIndex =
|
||||
getMinChildIndexForScrollOffset(scrollOffset);
|
||||
final int firstRequiredChildIndex = getMinChildIndexForScrollOffset(scrollOffset);
|
||||
|
||||
// Find the index of the last child that should be visible or in the trailing cache area.
|
||||
final int? lastRequiredChildIndex = targetScrollOffset.isFinite
|
||||
? getMaxChildIndexForScrollOffset(targetScrollOffset)
|
||||
: null;
|
||||
final int? lastRequiredChildIndex =
|
||||
targetScrollOffset.isFinite ? getMaxChildIndexForScrollOffset(targetScrollOffset) : null;
|
||||
|
||||
// Remove children that are no longer visible or within the cache area.
|
||||
if (firstChild == null) {
|
||||
collectGarbage(0, 0);
|
||||
} else {
|
||||
final int leadingChildrenToRemove =
|
||||
calculateLeadingGarbage(firstIndex: firstRequiredChildIndex);
|
||||
final int trailingChildrenToRemove = lastRequiredChildIndex == null
|
||||
? 0
|
||||
: calculateTrailingGarbage(lastIndex: lastRequiredChildIndex);
|
||||
final int leadingChildrenToRemove = calculateLeadingGarbage(firstIndex: firstRequiredChildIndex);
|
||||
final int trailingChildrenToRemove =
|
||||
lastRequiredChildIndex == null ? 0 : calculateTrailingGarbage(lastIndex: lastRequiredChildIndex);
|
||||
collectGarbage(leadingChildrenToRemove, trailingChildrenToRemove);
|
||||
}
|
||||
|
||||
// If there are currently no children laid out (e.g., initial load),
|
||||
// try to add the first child needed for the current scroll offset.
|
||||
if (firstChild == null) {
|
||||
final double firstChildLayoutOffset =
|
||||
indexToLayoutOffset(firstRequiredChildIndex);
|
||||
final double firstChildLayoutOffset = indexToLayoutOffset(firstRequiredChildIndex);
|
||||
final bool childAdded = addInitialChild(
|
||||
index: firstRequiredChildIndex,
|
||||
layoutOffset: firstChildLayoutOffset,
|
||||
|
|
@ -330,8 +306,7 @@ class _RenderSliverTimelineBoxAdaptor extends RenderSliverMultiBoxAdaptor {
|
|||
|
||||
if (!childAdded) {
|
||||
// There are either no children, or we are past the end of all our children.
|
||||
final double max =
|
||||
firstRequiredChildIndex <= 0 ? 0.0 : computeMaxScrollOffset();
|
||||
final double max = firstRequiredChildIndex <= 0 ? 0.0 : computeMaxScrollOffset();
|
||||
geometry = SliverGeometry(scrollExtent: max, maxPaintExtent: max);
|
||||
childManager.didFinishLayout();
|
||||
return;
|
||||
|
|
@ -342,26 +317,20 @@ class _RenderSliverTimelineBoxAdaptor extends RenderSliverMultiBoxAdaptor {
|
|||
RenderBox? highestLaidOutChild;
|
||||
final childConstraints = constraints.asBoxConstraints();
|
||||
|
||||
for (int currentIndex = indexOf(firstChild!) - 1;
|
||||
currentIndex >= firstRequiredChildIndex;
|
||||
--currentIndex) {
|
||||
final RenderBox? newLeadingChild =
|
||||
insertAndLayoutLeadingChild(childConstraints);
|
||||
for (int currentIndex = indexOf(firstChild!) - 1; currentIndex >= firstRequiredChildIndex; --currentIndex) {
|
||||
final RenderBox? newLeadingChild = insertAndLayoutLeadingChild(childConstraints);
|
||||
if (newLeadingChild == null) {
|
||||
// If a child is missing where we expect one, it indicates
|
||||
// an inconsistency in offset that needs correction.
|
||||
final Segment? segment =
|
||||
_segments.findByIndex(currentIndex) ?? _segments.firstOrNull;
|
||||
final Segment? segment = _segments.findByIndex(currentIndex) ?? _segments.firstOrNull;
|
||||
geometry = SliverGeometry(
|
||||
// Request a scroll correction based on where the missing child should have been.
|
||||
scrollOffsetCorrection:
|
||||
segment?.indexToLayoutOffset(currentIndex) ?? 0.0,
|
||||
scrollOffsetCorrection: segment?.indexToLayoutOffset(currentIndex) ?? 0.0,
|
||||
);
|
||||
// Parent will re-layout everything.
|
||||
return;
|
||||
}
|
||||
final childParentData =
|
||||
newLeadingChild.parentData! as SliverMultiBoxAdaptorParentData;
|
||||
final childParentData = newLeadingChild.parentData! as SliverMultiBoxAdaptorParentData;
|
||||
childParentData.layoutOffset = indexToLayoutOffset(currentIndex);
|
||||
assert(childParentData.index == currentIndex);
|
||||
highestLaidOutChild ??= newLeadingChild;
|
||||
|
|
@ -375,10 +344,8 @@ class _RenderSliverTimelineBoxAdaptor extends RenderSliverMultiBoxAdaptor {
|
|||
// The [firstChild] that existed at the start of performLayout is still the first one we need.
|
||||
if (highestLaidOutChild == null) {
|
||||
firstChild!.layout(childConstraints);
|
||||
final childParentData =
|
||||
firstChild!.parentData! as SliverMultiBoxAdaptorParentData;
|
||||
childParentData.layoutOffset =
|
||||
indexToLayoutOffset(firstRequiredChildIndex);
|
||||
final childParentData = firstChild!.parentData! as SliverMultiBoxAdaptorParentData;
|
||||
childParentData.layoutOffset = indexToLayoutOffset(firstRequiredChildIndex);
|
||||
highestLaidOutChild = firstChild;
|
||||
}
|
||||
|
||||
|
|
@ -389,8 +356,7 @@ class _RenderSliverTimelineBoxAdaptor extends RenderSliverMultiBoxAdaptor {
|
|||
double calculatedMaxScrollOffset = double.infinity;
|
||||
|
||||
for (int currentIndex = indexOf(mostRecentlyLaidOutChild!) + 1;
|
||||
lastRequiredChildIndex == null ||
|
||||
currentIndex <= lastRequiredChildIndex;
|
||||
lastRequiredChildIndex == null || currentIndex <= lastRequiredChildIndex;
|
||||
++currentIndex) {
|
||||
RenderBox? child = childAfter(mostRecentlyLaidOutChild!);
|
||||
|
||||
|
|
@ -400,11 +366,8 @@ class _RenderSliverTimelineBoxAdaptor extends RenderSliverMultiBoxAdaptor {
|
|||
after: mostRecentlyLaidOutChild,
|
||||
);
|
||||
if (child == null) {
|
||||
final Segment? segment =
|
||||
_segments.findByIndex(currentIndex) ?? _segments.lastOrNull;
|
||||
calculatedMaxScrollOffset =
|
||||
segment?.indexToLayoutOffset(currentIndex) ??
|
||||
computeMaxScrollOffset();
|
||||
final Segment? segment = _segments.findByIndex(currentIndex) ?? _segments.lastOrNull;
|
||||
calculatedMaxScrollOffset = segment?.indexToLayoutOffset(currentIndex) ?? computeMaxScrollOffset();
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
|
|
@ -412,28 +375,23 @@ class _RenderSliverTimelineBoxAdaptor extends RenderSliverMultiBoxAdaptor {
|
|||
}
|
||||
|
||||
mostRecentlyLaidOutChild = child;
|
||||
final childParentData = mostRecentlyLaidOutChild.parentData!
|
||||
as SliverMultiBoxAdaptorParentData;
|
||||
final childParentData = mostRecentlyLaidOutChild.parentData! as SliverMultiBoxAdaptorParentData;
|
||||
assert(childParentData.index == currentIndex);
|
||||
childParentData.layoutOffset = indexToLayoutOffset(currentIndex);
|
||||
}
|
||||
|
||||
final int lastLaidOutChildIndex = indexOf(lastChild!);
|
||||
final double leadingScrollOffset =
|
||||
indexToLayoutOffset(firstRequiredChildIndex);
|
||||
final double trailingScrollOffset =
|
||||
indexToLayoutOffset(lastLaidOutChildIndex + 1);
|
||||
final double leadingScrollOffset = indexToLayoutOffset(firstRequiredChildIndex);
|
||||
final double trailingScrollOffset = indexToLayoutOffset(lastLaidOutChildIndex + 1);
|
||||
|
||||
assert(
|
||||
firstRequiredChildIndex == 0 ||
|
||||
(childScrollOffset(firstChild!) ?? -1.0) - scrollOffset <=
|
||||
precisionErrorTolerance,
|
||||
(childScrollOffset(firstChild!) ?? -1.0) - scrollOffset <= precisionErrorTolerance,
|
||||
);
|
||||
assert(debugAssertChildListIsNonEmptyAndContiguous());
|
||||
assert(indexOf(firstChild!) == firstRequiredChildIndex);
|
||||
assert(
|
||||
lastRequiredChildIndex == null ||
|
||||
lastLaidOutChildIndex <= lastRequiredChildIndex,
|
||||
lastRequiredChildIndex == null || lastLaidOutChildIndex <= lastRequiredChildIndex,
|
||||
);
|
||||
|
||||
calculatedMaxScrollOffset = math.min(
|
||||
|
|
@ -453,11 +411,9 @@ class _RenderSliverTimelineBoxAdaptor extends RenderSliverMultiBoxAdaptor {
|
|||
to: trailingScrollOffset,
|
||||
);
|
||||
|
||||
final double targetEndScrollOffsetForPaint =
|
||||
constraints.scrollOffset + constraints.remainingPaintExtent;
|
||||
final int? targetLastIndexForPaint = targetEndScrollOffsetForPaint.isFinite
|
||||
? getMaxChildIndexForScrollOffset(targetEndScrollOffsetForPaint)
|
||||
: null;
|
||||
final double targetEndScrollOffsetForPaint = constraints.scrollOffset + constraints.remainingPaintExtent;
|
||||
final int? targetLastIndexForPaint =
|
||||
targetEndScrollOffsetForPaint.isFinite ? getMaxChildIndexForScrollOffset(targetEndScrollOffsetForPaint) : null;
|
||||
|
||||
final maxPaintExtent = math.max(paintExtent, calculatedMaxScrollOffset);
|
||||
|
||||
|
|
@ -468,8 +424,7 @@ class _RenderSliverTimelineBoxAdaptor extends RenderSliverMultiBoxAdaptor {
|
|||
// Indicates if there's content scrolled off-screen.
|
||||
// This is true if the last child needed for painting is actually laid out,
|
||||
// or if the first child is partially visible.
|
||||
hasVisualOverflow: (targetLastIndexForPaint != null &&
|
||||
lastLaidOutChildIndex >= targetLastIndexForPaint) ||
|
||||
hasVisualOverflow: (targetLastIndexForPaint != null && lastLaidOutChildIndex >= targetLastIndexForPaint) ||
|
||||
constraints.scrollOffset > 0.0,
|
||||
cacheExtent: cacheExtent,
|
||||
);
|
||||
|
|
@ -489,8 +444,7 @@ class _MultiSelectStatusButton extends ConsumerWidget {
|
|||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final selectCount =
|
||||
ref.watch(multiSelectProvider.select((s) => s.selectedAssets.length));
|
||||
final selectCount = ref.watch(multiSelectProvider.select((s) => s.selectedAssets.length));
|
||||
return ElevatedButton.icon(
|
||||
onPressed: () => ref.read(multiSelectProvider.notifier).reset(),
|
||||
icon: Icon(
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue