1
0
mirror of https://github.com/immich-app/immich.git synced 2025-08-08 23:07:06 +02:00

feat: locked view mobile (#18316)

* feat: locked/private view

* feat: locked/private view

* feat: mobile lock/private view

* feat: mobile lock/private view

* merge main

* pr feedback

* pr feedback

* bottom sheet sizing

* always lock when navigating away
This commit is contained in:
Alex
2025-05-20 08:35:22 -05:00
committed by GitHub
parent 397808dd1a
commit fe71894308
57 changed files with 1893 additions and 289 deletions

View File

@ -6,6 +6,7 @@ import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:immich_mobile/extensions/build_context_extensions.dart';
import 'package:immich_mobile/providers/album/album.provider.dart';
import 'package:immich_mobile/providers/routes.provider.dart';
import 'package:immich_mobile/widgets/album/add_to_album_sliverlist.dart';
import 'package:immich_mobile/models/asset_selection_state.dart';
import 'package:immich_mobile/widgets/asset_grid/delete_dialog.dart';
@ -37,6 +38,7 @@ class ControlBottomAppBar extends HookConsumerWidget {
final void Function()? onEditTime;
final void Function()? onEditLocation;
final void Function()? onRemoveFromAlbum;
final void Function()? onToggleLocked;
final bool enabled;
final bool unfavorite;
@ -58,6 +60,7 @@ class ControlBottomAppBar extends HookConsumerWidget {
this.onEditTime,
this.onEditLocation,
this.onRemoveFromAlbum,
this.onToggleLocked,
this.selectionAssetState = const AssetSelectionState(),
this.enabled = true,
this.unarchive = false,
@ -77,6 +80,7 @@ class ControlBottomAppBar extends HookConsumerWidget {
ref.watch(albumProvider).where((a) => a.shared).toList();
const bottomPadding = 0.20;
final scrollController = useDraggableScrollController();
final isInLockedView = ref.watch(inLockedViewProvider);
void minimize() {
scrollController.animateTo(
@ -133,11 +137,12 @@ class ControlBottomAppBar extends HookConsumerWidget {
label: "share".tr(),
onPressed: enabled ? () => onShare(true) : null,
),
ControlBoxButton(
iconData: Icons.link_rounded,
label: "control_bottom_app_bar_share_link".tr(),
onPressed: enabled ? () => onShare(false) : null,
),
if (!isInLockedView)
ControlBoxButton(
iconData: Icons.link_rounded,
label: "share_link".tr(),
onPressed: enabled ? () => onShare(false) : null,
),
if (hasRemote && onArchive != null)
ControlBoxButton(
iconData:
@ -153,7 +158,7 @@ class ControlBottomAppBar extends HookConsumerWidget {
label: (unfavorite ? "unfavorite" : "favorite").tr(),
onPressed: enabled ? onFavorite : null,
),
if (hasLocal && hasRemote && onDelete != null)
if (hasLocal && hasRemote && onDelete != null && !isInLockedView)
ConstrainedBox(
constraints: const BoxConstraints(maxWidth: 90),
child: ControlBoxButton(
@ -166,7 +171,7 @@ class ControlBottomAppBar extends HookConsumerWidget {
enabled ? () => showForceDeleteDialog(onDelete!) : null,
),
),
if (hasRemote && onDeleteServer != null)
if (hasRemote && onDeleteServer != null && !isInLockedView)
ConstrainedBox(
constraints: const BoxConstraints(maxWidth: 85),
child: ControlBoxButton(
@ -189,9 +194,23 @@ class ControlBottomAppBar extends HookConsumerWidget {
: null,
),
),
if (hasLocal && onDeleteLocal != null)
if (isInLockedView)
ConstrainedBox(
constraints: const BoxConstraints(maxWidth: 85),
constraints: const BoxConstraints(maxWidth: 110),
child: ControlBoxButton(
iconData: Icons.delete_forever,
label: "delete_dialog_title".tr(),
onPressed: enabled
? () => showForceDeleteDialog(
onDeleteServer!,
alertMsg: "delete_dialog_alert_remote",
)
: null,
),
),
if (hasLocal && onDeleteLocal != null && !isInLockedView)
ConstrainedBox(
constraints: const BoxConstraints(maxWidth: 95),
child: ControlBoxButton(
iconData: Icons.no_cell_outlined,
label: "control_bottom_app_bar_delete_from_local".tr(),
@ -231,6 +250,19 @@ class ControlBottomAppBar extends HookConsumerWidget {
onPressed: enabled ? onEditLocation : null,
),
),
if (hasRemote)
ConstrainedBox(
constraints: const BoxConstraints(maxWidth: 100),
child: ControlBoxButton(
iconData: isInLockedView
? Icons.lock_open_rounded
: Icons.lock_outline_rounded,
label: isInLockedView
? "remove_from_locked_folder".tr()
: "move_to_locked_folder".tr(),
onPressed: enabled ? onToggleLocked : null,
),
),
if (!selectionAssetState.hasLocal &&
selectionAssetState.selectedCount > 1 &&
onStack != null)
@ -269,20 +301,40 @@ class ControlBottomAppBar extends HookConsumerWidget {
];
}
getInitialSize() {
if (isInLockedView) {
return 0.20;
}
if (hasRemote) {
return 0.35;
}
return bottomPadding;
}
getMaxChildSize() {
if (isInLockedView) {
return 0.20;
}
if (hasRemote) {
return 0.65;
}
return bottomPadding;
}
return DraggableScrollableSheet(
controller: scrollController,
initialChildSize: hasRemote ? 0.35 : bottomPadding,
initialChildSize: getInitialSize(),
minChildSize: bottomPadding,
maxChildSize: hasRemote ? 0.65 : bottomPadding,
maxChildSize: getMaxChildSize(),
snap: true,
builder: (
BuildContext context,
ScrollController scrollController,
) {
return Card(
color: context.colorScheme.surfaceContainerLow,
surfaceTintColor: Colors.transparent,
elevation: 18.0,
color: context.colorScheme.surfaceContainerHigh,
surfaceTintColor: context.colorScheme.surfaceContainerHigh,
elevation: 6.0,
shape: const RoundedRectangleBorder(
borderRadius: BorderRadius.only(
topLeft: Radius.circular(12),
@ -300,27 +352,27 @@ class ControlBottomAppBar extends HookConsumerWidget {
const CustomDraggingHandle(),
const SizedBox(height: 12),
SizedBox(
height: 100,
height: 120,
child: ListView(
shrinkWrap: true,
scrollDirection: Axis.horizontal,
children: renderActionButtons(),
),
),
if (hasRemote)
if (hasRemote && !isInLockedView) ...[
const Divider(
indent: 16,
endIndent: 16,
thickness: 1,
),
if (hasRemote)
_AddToAlbumTitleRow(
onCreateNewAlbum: enabled ? onCreateNewAlbum : null,
),
],
],
),
),
if (hasRemote)
if (hasRemote && !isInLockedView)
SliverPadding(
padding: const EdgeInsets.symmetric(horizontal: 16),
sliver: AddToAlbumSliverList(
@ -352,12 +404,9 @@ class _AddToAlbumTitleRow extends StatelessWidget {
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
const Text(
Text(
"add_to_album",
style: TextStyle(
fontSize: 14,
fontWeight: FontWeight.bold,
),
style: context.textTheme.titleSmall,
).tr(),
TextButton.icon(
onPressed: onCreateNewAlbum,