forked from Cutlery/immich
		
	* dep(mobile): update flutter and deps * chore: dart analyzer * chore: update flutter workflow version * chore: dart format * fix: gallery_viewer PopScope --------- Co-authored-by: shenlong-tanwen <139912620+shalong-tanwen@users.noreply.github.com>
		
			
				
	
	
		
			341 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			Dart
		
	
	
	
	
	
			
		
		
	
	
			341 lines
		
	
	
		
			12 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({
 | |
|     super.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,
 | |
|   });
 | |
| 
 | |
|   @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);
 | |
|     const bottomPadding = 0.20;
 | |
| 
 | |
|     void showForceDeleteDialog(
 | |
|       Function(bool) deleteCb, {
 | |
|       String? alertMsg,
 | |
|     }) {
 | |
|       showDialog(
 | |
|         context: context,
 | |
|         builder: (BuildContext context) {
 | |
|           return DeleteDialog(
 | |
|             alert: alertMsg,
 | |
|             onDelete: () => deleteCb(true),
 | |
|           );
 | |
|         },
 | |
|       );
 | |
|     }
 | |
| 
 | |
|     void handleRemoteDelete(
 | |
|       bool force,
 | |
|       Function(bool) deleteCb, {
 | |
|       String? alertMsg,
 | |
|     }) {
 | |
|       if (!force) {
 | |
|         deleteCb(force);
 | |
|         return;
 | |
|       }
 | |
|       return showForceDeleteDialog(deleteCb, alertMsg: alertMsg);
 | |
|     }
 | |
| 
 | |
|     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: trashEnabled
 | |
|                   ? "control_bottom_app_bar_trash_from_immich".tr()
 | |
|                   : "control_bottom_app_bar_delete_from_immich".tr(),
 | |
|               onPressed: enabled
 | |
|                   ? () => handleRemoteDelete(
 | |
|                         !trashEnabled,
 | |
|                         onDeleteServer!,
 | |
|                         alertMsg: "delete_dialog_alert_remote",
 | |
|                       )
 | |
|                   : null,
 | |
|               onLongPressed: enabled
 | |
|                   ? () => showForceDeleteDialog(
 | |
|                         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,
 | |
|               onLongPressed:
 | |
|                   enabled ? () => showForceDeleteDialog(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 (!selectionAssetState.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 (selectionAssetState.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 : bottomPadding,
 | |
|       minChildSize: bottomPadding,
 | |
|       maxChildSize: hasRemote ? 0.60 : bottomPadding,
 | |
|       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(),
 | |
|           ),
 | |
|         ],
 | |
|       ),
 | |
|     );
 | |
|   }
 | |
| }
 |