1
0
mirror of https://github.com/immich-app/immich.git synced 2024-12-30 11:28:22 +02:00
immich/mobile/lib/modules/home/ui/control_bottom_app_bar.dart
shenlong 4c2befc68c
feat(mobile): separate delete buttons (#4505)
* feat(mobile): delete assets from device only

* mobile: add backed up only toggle for delete device only

* remove toggle inside alert and show different content

* mobile: change content color for local only

* mobile: delete local only button to dialog

* style: display bottom action in two lines

* feat: separate delete buttons

* fix: incorrect error message for ownedRemoteSelection

* fix: handle remoteOnly from delete everywhere

* request confirmation for local only only when non-backed assets are in selection

---------

Co-authored-by: shalong-tanwen <139912620+shalong-tanwen@users.noreply.github.com>
Co-authored-by: Alex Tran <alex.tran1502@gmail.com>
2024-01-16 21:28:23 -06:00

323 lines
11 KiB
Dart

import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:immich_mobile/extensions/build_context_extensions.dart';
import 'package:immich_mobile/modules/album/providers/album.provider.dart';
import 'package:immich_mobile/modules/album/providers/shared_album.provider.dart';
import 'package:immich_mobile/modules/album/ui/add_to_album_sliverlist.dart';
import 'package:immich_mobile/modules/home/models/selection_state.dart';
import 'package:immich_mobile/modules/home/ui/delete_dialog.dart';
import 'package:immich_mobile/modules/home/ui/upload_dialog.dart';
import 'package:immich_mobile/shared/providers/server_info.provider.dart';
import 'package:immich_mobile/shared/ui/drag_sheet.dart';
import 'package:immich_mobile/shared/models/album.dart';
class ControlBottomAppBar extends ConsumerWidget {
final void Function(bool shareLocal) onShare;
final void Function()? onFavorite;
final void Function()? onArchive;
final void Function([bool force])? onDelete;
final void Function([bool force])? onDeleteServer;
final void Function(bool onlyBackedUp)? onDeleteLocal;
final Function(Album album) onAddToAlbum;
final void Function() onCreateNewAlbum;
final void Function() onUpload;
final void Function()? onStack;
final void Function()? onEditTime;
final void Function()? onEditLocation;
final void Function()? onRemoveFromAlbum;
final bool enabled;
final bool unfavorite;
final bool unarchive;
final SelectionAssetState selectionAssetState;
const ControlBottomAppBar({
Key? key,
required this.onShare,
this.onFavorite,
this.onArchive,
this.onDelete,
this.onDeleteServer,
this.onDeleteLocal,
required this.onAddToAlbum,
required this.onCreateNewAlbum,
required this.onUpload,
this.onStack,
this.onEditTime,
this.onEditLocation,
this.onRemoveFromAlbum,
this.selectionAssetState = const SelectionAssetState(),
this.enabled = true,
this.unarchive = false,
this.unfavorite = false,
}) : super(key: key);
@override
Widget build(BuildContext context, WidgetRef ref) {
final hasRemote =
selectionAssetState.hasRemote || selectionAssetState.hasMerged;
final hasLocal =
selectionAssetState.hasLocal || selectionAssetState.hasMerged;
final trashEnabled =
ref.watch(serverInfoProvider.select((v) => v.serverFeatures.trash));
final albums = ref.watch(albumProvider).where((a) => a.isRemote).toList();
final sharedAlbums = ref.watch(sharedAlbumProvider);
void handleRemoteDelete(
bool force,
Function(bool) deleteCb, {
String? alertMsg,
}) {
if (!force) {
deleteCb(force);
return;
}
showDialog(
context: context,
builder: (BuildContext context) {
return DeleteDialog(
alert: alertMsg,
onDelete: () => deleteCb(force),
);
},
);
}
List<Widget> renderActionButtons() {
return [
if (hasRemote)
ControlBoxButton(
iconData: Icons.share_rounded,
label: "control_bottom_app_bar_share".tr(),
onPressed: enabled ? () => onShare(false) : null,
),
ControlBoxButton(
iconData: Icons.ios_share_rounded,
label: "control_bottom_app_bar_share_to".tr(),
onPressed: enabled ? () => onShare(true) : null,
),
if (hasRemote && onArchive != null)
ControlBoxButton(
iconData: unarchive ? Icons.unarchive : Icons.archive,
label: (unarchive
? "control_bottom_app_bar_unarchive"
: "control_bottom_app_bar_archive")
.tr(),
onPressed: enabled ? onArchive : null,
),
if (hasRemote && onFavorite != null)
ControlBoxButton(
iconData: unfavorite
? Icons.favorite_border_rounded
: Icons.favorite_rounded,
label: (unfavorite
? "control_bottom_app_bar_unfavorite"
: "control_bottom_app_bar_favorite")
.tr(),
onPressed: enabled ? onFavorite : null,
),
if (hasRemote && onDeleteServer != null)
ConstrainedBox(
constraints: const BoxConstraints(maxWidth: 85),
child: ControlBoxButton(
iconData: Icons.cloud_off_outlined,
label: "control_bottom_app_bar_delete_from_immich".tr(),
onPressed: enabled
? () => handleRemoteDelete(
!trashEnabled,
onDeleteServer!,
alertMsg: "delete_dialog_alert_remote",
)
: null,
),
),
if (hasLocal && onDeleteLocal != null)
ConstrainedBox(
constraints: const BoxConstraints(maxWidth: 85),
child: ControlBoxButton(
iconData: Icons.no_cell_rounded,
label: "control_bottom_app_bar_delete_from_local".tr(),
onPressed: enabled
? () {
if (!selectionAssetState.hasLocal) {
return onDeleteLocal?.call(true);
}
showDialog(
context: context,
builder: (BuildContext context) {
return DeleteLocalOnlyDialog(
onDeleteLocal: onDeleteLocal!,
);
},
);
}
: null,
),
),
if (hasLocal && hasRemote && onDelete != null)
ConstrainedBox(
constraints: const BoxConstraints(maxWidth: 90),
child: ControlBoxButton(
iconData: Icons.delete_sweep_outlined,
label: "control_bottom_app_bar_delete".tr(),
onPressed: enabled
? () => handleRemoteDelete(!trashEnabled, onDelete!)
: null,
),
),
if (hasRemote && onEditTime != null)
ControlBoxButton(
iconData: Icons.edit_calendar_outlined,
label: "control_bottom_app_bar_edit_time".tr(),
onPressed: enabled ? onEditTime : null,
),
if (hasRemote && onEditLocation != null)
ControlBoxButton(
iconData: Icons.edit_location_alt_outlined,
label: "control_bottom_app_bar_edit_location".tr(),
onPressed: enabled ? onEditLocation : null,
),
if (!hasLocal &&
selectionAssetState.selectedCount > 1 &&
onStack != null)
ControlBoxButton(
iconData: Icons.filter_none_rounded,
label: "control_bottom_app_bar_stack".tr(),
onPressed: enabled ? onStack : null,
),
if (onRemoveFromAlbum != null)
ControlBoxButton(
iconData: Icons.delete_sweep_rounded,
label: 'album_viewer_appbar_share_remove'.tr(),
onPressed: enabled ? onRemoveFromAlbum : null,
),
if (hasLocal)
ControlBoxButton(
iconData: Icons.backup_outlined,
label: "Upload",
onPressed: enabled
? () => showDialog(
context: context,
builder: (BuildContext context) {
return UploadDialog(
onUpload: onUpload,
);
},
)
: null,
),
];
}
return DraggableScrollableSheet(
initialChildSize: hasRemote ? 0.30 : 0.18,
minChildSize: 0.18,
maxChildSize: hasRemote ? 0.60 : 0.18,
snap: true,
builder: (
BuildContext context,
ScrollController scrollController,
) {
return Card(
color: context.isDarkTheme ? Colors.grey[900] : Colors.grey[100],
surfaceTintColor: Colors.transparent,
elevation: 18.0,
shape: const RoundedRectangleBorder(
borderRadius: BorderRadius.only(
topLeft: Radius.circular(12),
topRight: Radius.circular(12),
),
),
margin: const EdgeInsets.all(0),
child: CustomScrollView(
controller: scrollController,
slivers: [
SliverToBoxAdapter(
child: Column(
children: <Widget>[
const SizedBox(height: 12),
const CustomDraggingHandle(),
const SizedBox(height: 24),
SizedBox(
height: 90,
child: ListView(
shrinkWrap: true,
scrollDirection: Axis.horizontal,
children: renderActionButtons(),
),
),
if (hasRemote)
const Divider(
indent: 16,
endIndent: 16,
thickness: 1,
),
if (hasRemote)
_AddToAlbumTitleRow(
onCreateNewAlbum: enabled ? onCreateNewAlbum : null,
),
],
),
),
if (hasRemote)
SliverPadding(
padding: const EdgeInsets.symmetric(horizontal: 16),
sliver: AddToAlbumSliverList(
albums: albums,
sharedAlbums: sharedAlbums,
onAddToAlbum: onAddToAlbum,
enabled: enabled,
),
),
],
),
);
},
);
}
}
class _AddToAlbumTitleRow extends StatelessWidget {
const _AddToAlbumTitleRow({
required this.onCreateNewAlbum,
});
final VoidCallback? onCreateNewAlbum;
@override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.symmetric(horizontal: 16),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
const Text(
"common_add_to_album",
style: TextStyle(
fontSize: 14,
fontWeight: FontWeight.bold,
),
).tr(),
TextButton.icon(
onPressed: onCreateNewAlbum,
icon: Icon(
Icons.add,
color: context.primaryColor,
),
label: Text(
"common_create_new_album",
style: TextStyle(
color: context.primaryColor,
fontWeight: FontWeight.bold,
fontSize: 14,
),
).tr(),
),
],
),
);
}
}