mirror of
https://github.com/immich-app/immich
synced 2025-11-07 17:27:20 +00:00
feat(mobile): add to albums from existing albums (#21554)
* feat(mobile): add to albums from existing albums * formatted files * used the new t() method for translation * removed unused import
This commit is contained in:
parent
ff19aea4ac
commit
b82e29fbb4
3 changed files with 89 additions and 5 deletions
|
|
@ -1,7 +1,9 @@
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
|
import 'package:immich_mobile/extensions/translate_extensions.dart';
|
||||||
import 'package:immich_mobile/constants/enums.dart';
|
import 'package:immich_mobile/constants/enums.dart';
|
||||||
import 'package:immich_mobile/domain/models/album/album.model.dart';
|
import 'package:immich_mobile/domain/models/album/album.model.dart';
|
||||||
|
import 'package:immich_mobile/domain/models/asset/base_asset.model.dart';
|
||||||
import 'package:immich_mobile/presentation/widgets/action_buttons/archive_action_button.widget.dart';
|
import 'package:immich_mobile/presentation/widgets/action_buttons/archive_action_button.widget.dart';
|
||||||
import 'package:immich_mobile/presentation/widgets/action_buttons/delete_permanent_action_button.widget.dart';
|
import 'package:immich_mobile/presentation/widgets/action_buttons/delete_permanent_action_button.widget.dart';
|
||||||
import 'package:immich_mobile/presentation/widgets/action_buttons/delete_local_action_button.widget.dart';
|
import 'package:immich_mobile/presentation/widgets/action_buttons/delete_local_action_button.widget.dart';
|
||||||
|
|
@ -16,22 +18,74 @@ import 'package:immich_mobile/presentation/widgets/action_buttons/share_link_act
|
||||||
import 'package:immich_mobile/presentation/widgets/action_buttons/stack_action_button.widget.dart';
|
import 'package:immich_mobile/presentation/widgets/action_buttons/stack_action_button.widget.dart';
|
||||||
import 'package:immich_mobile/presentation/widgets/action_buttons/trash_action_button.widget.dart';
|
import 'package:immich_mobile/presentation/widgets/action_buttons/trash_action_button.widget.dart';
|
||||||
import 'package:immich_mobile/presentation/widgets/action_buttons/upload_action_button.widget.dart';
|
import 'package:immich_mobile/presentation/widgets/action_buttons/upload_action_button.widget.dart';
|
||||||
|
import 'package:immich_mobile/presentation/widgets/album/album_selector.widget.dart';
|
||||||
import 'package:immich_mobile/presentation/widgets/bottom_sheet/base_bottom_sheet.widget.dart';
|
import 'package:immich_mobile/presentation/widgets/bottom_sheet/base_bottom_sheet.widget.dart';
|
||||||
|
import 'package:immich_mobile/providers/infrastructure/album.provider.dart';
|
||||||
import 'package:immich_mobile/providers/server_info.provider.dart';
|
import 'package:immich_mobile/providers/server_info.provider.dart';
|
||||||
import 'package:immich_mobile/providers/timeline/multiselect.provider.dart';
|
import 'package:immich_mobile/providers/timeline/multiselect.provider.dart';
|
||||||
|
import 'package:immich_mobile/widgets/common/immich_toast.dart';
|
||||||
|
|
||||||
class RemoteAlbumBottomSheet extends ConsumerWidget {
|
class RemoteAlbumBottomSheet extends ConsumerStatefulWidget {
|
||||||
final RemoteAlbum album;
|
final RemoteAlbum album;
|
||||||
const RemoteAlbumBottomSheet({super.key, required this.album});
|
const RemoteAlbumBottomSheet({super.key, required this.album});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context, WidgetRef ref) {
|
ConsumerState<RemoteAlbumBottomSheet> createState() => _RemoteAlbumBottomSheetState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _RemoteAlbumBottomSheetState extends ConsumerState<RemoteAlbumBottomSheet> {
|
||||||
|
late DraggableScrollableController sheetController;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
sheetController = DraggableScrollableController();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
sheetController.dispose();
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
final multiselect = ref.watch(multiSelectProvider);
|
final multiselect = ref.watch(multiSelectProvider);
|
||||||
final isTrashEnable = ref.watch(serverInfoProvider.select((state) => state.serverFeatures.trash));
|
final isTrashEnable = ref.watch(serverInfoProvider.select((state) => state.serverFeatures.trash));
|
||||||
|
|
||||||
|
Future<void> addAssetsToAlbum(RemoteAlbum album) async {
|
||||||
|
final selectedAssets = multiselect.selectedAssets;
|
||||||
|
if (selectedAssets.isEmpty) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
final addedCount = await ref
|
||||||
|
.read(remoteAlbumProvider.notifier)
|
||||||
|
.addAssets(album.id, selectedAssets.map((e) => (e as RemoteAsset).id).toList());
|
||||||
|
|
||||||
|
if (addedCount != selectedAssets.length) {
|
||||||
|
ImmichToast.show(
|
||||||
|
context: context,
|
||||||
|
msg: 'add_to_album_bottom_sheet_already_exists'.t(context: context, args: {"album": album.name}),
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
ImmichToast.show(
|
||||||
|
context: context,
|
||||||
|
msg: 'add_to_album_bottom_sheet_added'.t(context: context, args: {"album": album.name}),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
ref.read(multiSelectProvider.notifier).reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> onKeyboardExpand() {
|
||||||
|
return sheetController.animateTo(0.85, duration: const Duration(milliseconds: 200), curve: Curves.easeInOut);
|
||||||
|
}
|
||||||
|
|
||||||
return BaseBottomSheet(
|
return BaseBottomSheet(
|
||||||
initialChildSize: 0.25,
|
controller: sheetController,
|
||||||
maxChildSize: 0.4,
|
initialChildSize: 0.45,
|
||||||
|
maxChildSize: 0.85,
|
||||||
shouldCloseOnMinExtent: false,
|
shouldCloseOnMinExtent: false,
|
||||||
actions: [
|
actions: [
|
||||||
const ShareActionButton(source: ActionSource.timeline),
|
const ShareActionButton(source: ActionSource.timeline),
|
||||||
|
|
@ -52,7 +106,11 @@ class RemoteAlbumBottomSheet extends ConsumerWidget {
|
||||||
const DeleteLocalActionButton(source: ActionSource.timeline),
|
const DeleteLocalActionButton(source: ActionSource.timeline),
|
||||||
const UploadActionButton(source: ActionSource.timeline),
|
const UploadActionButton(source: ActionSource.timeline),
|
||||||
],
|
],
|
||||||
RemoveFromAlbumActionButton(source: ActionSource.timeline, albumId: album.id),
|
RemoveFromAlbumActionButton(source: ActionSource.timeline, albumId: widget.album.id),
|
||||||
|
],
|
||||||
|
slivers: [
|
||||||
|
const AddToAlbumHeader(),
|
||||||
|
AlbumSelector(onAlbumSelected: addAssetsToAlbum, onKeyboardExpanded: onKeyboardExpand),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -8,12 +8,14 @@ import 'package:immich_mobile/extensions/build_context_extensions.dart';
|
||||||
import 'package:immich_mobile/providers/album/album.provider.dart';
|
import 'package:immich_mobile/providers/album/album.provider.dart';
|
||||||
import 'package:immich_mobile/providers/routes.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/widgets/album/add_to_album_sliverlist.dart';
|
||||||
|
import 'package:immich_mobile/widgets/album/add_to_album_bottom_sheet.dart';
|
||||||
import 'package:immich_mobile/models/asset_selection_state.dart';
|
import 'package:immich_mobile/models/asset_selection_state.dart';
|
||||||
import 'package:immich_mobile/widgets/asset_grid/delete_dialog.dart';
|
import 'package:immich_mobile/widgets/asset_grid/delete_dialog.dart';
|
||||||
import 'package:immich_mobile/widgets/asset_grid/upload_dialog.dart';
|
import 'package:immich_mobile/widgets/asset_grid/upload_dialog.dart';
|
||||||
import 'package:immich_mobile/providers/server_info.provider.dart';
|
import 'package:immich_mobile/providers/server_info.provider.dart';
|
||||||
import 'package:immich_mobile/widgets/common/drag_sheet.dart';
|
import 'package:immich_mobile/widgets/common/drag_sheet.dart';
|
||||||
import 'package:immich_mobile/entities/album.entity.dart';
|
import 'package:immich_mobile/entities/album.entity.dart';
|
||||||
|
import 'package:immich_mobile/entities/asset.entity.dart';
|
||||||
import 'package:immich_mobile/utils/draggable_scroll_controller.dart';
|
import 'package:immich_mobile/utils/draggable_scroll_controller.dart';
|
||||||
|
|
||||||
final controlBottomAppBarNotifier = ControlBottomAppBarNotifier();
|
final controlBottomAppBarNotifier = ControlBottomAppBarNotifier();
|
||||||
|
|
@ -45,6 +47,7 @@ class ControlBottomAppBar extends HookConsumerWidget {
|
||||||
final bool unfavorite;
|
final bool unfavorite;
|
||||||
final bool unarchive;
|
final bool unarchive;
|
||||||
final AssetSelectionState selectionAssetState;
|
final AssetSelectionState selectionAssetState;
|
||||||
|
final List<Asset> selectedAssets;
|
||||||
|
|
||||||
const ControlBottomAppBar({
|
const ControlBottomAppBar({
|
||||||
super.key,
|
super.key,
|
||||||
|
|
@ -64,6 +67,7 @@ class ControlBottomAppBar extends HookConsumerWidget {
|
||||||
this.onRemoveFromAlbum,
|
this.onRemoveFromAlbum,
|
||||||
this.onToggleLocked,
|
this.onToggleLocked,
|
||||||
this.selectionAssetState = const AssetSelectionState(),
|
this.selectionAssetState = const AssetSelectionState(),
|
||||||
|
this.selectedAssets = const [],
|
||||||
this.enabled = true,
|
this.enabled = true,
|
||||||
this.unarchive = false,
|
this.unarchive = false,
|
||||||
this.unfavorite = false,
|
this.unfavorite = false,
|
||||||
|
|
@ -100,6 +104,18 @@ class ControlBottomAppBar extends HookConsumerWidget {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Show existing AddToAlbumBottomSheet
|
||||||
|
void showAddToAlbumBottomSheet() {
|
||||||
|
showModalBottomSheet(
|
||||||
|
elevation: 0,
|
||||||
|
shape: const RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(15.0))),
|
||||||
|
context: context,
|
||||||
|
builder: (BuildContext _) {
|
||||||
|
return AddToAlbumBottomSheet(assets: selectedAssets);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
void handleRemoteDelete(bool force, Function(bool) deleteCb, {String? alertMsg}) {
|
void handleRemoteDelete(bool force, Function(bool) deleteCb, {String? alertMsg}) {
|
||||||
if (!force) {
|
if (!force) {
|
||||||
deleteCb(force);
|
deleteCb(force);
|
||||||
|
|
@ -121,6 +137,15 @@ class ControlBottomAppBar extends HookConsumerWidget {
|
||||||
label: "share_link".tr(),
|
label: "share_link".tr(),
|
||||||
onPressed: enabled ? () => onShare(false) : null,
|
onPressed: enabled ? () => onShare(false) : null,
|
||||||
),
|
),
|
||||||
|
if (!isInLockedView && hasRemote && albums.isNotEmpty)
|
||||||
|
ConstrainedBox(
|
||||||
|
constraints: const BoxConstraints(maxWidth: 100),
|
||||||
|
child: ControlBoxButton(
|
||||||
|
iconData: Icons.photo_album,
|
||||||
|
label: "add_to_album".tr(),
|
||||||
|
onPressed: enabled ? showAddToAlbumBottomSheet : null,
|
||||||
|
),
|
||||||
|
),
|
||||||
if (hasRemote && onArchive != null)
|
if (hasRemote && onArchive != null)
|
||||||
ControlBoxButton(
|
ControlBoxButton(
|
||||||
iconData: unarchive ? Icons.unarchive_outlined : Icons.archive_outlined,
|
iconData: unarchive ? Icons.unarchive_outlined : Icons.archive_outlined,
|
||||||
|
|
|
||||||
|
|
@ -440,6 +440,7 @@ class MultiselectGrid extends HookConsumerWidget {
|
||||||
onUpload: onUpload,
|
onUpload: onUpload,
|
||||||
enabled: !processing.value,
|
enabled: !processing.value,
|
||||||
selectionAssetState: selectionAssetState.value,
|
selectionAssetState: selectionAssetState.value,
|
||||||
|
selectedAssets: selection.value.toList(),
|
||||||
onStack: stackEnabled ? onStack : null,
|
onStack: stackEnabled ? onStack : null,
|
||||||
onEditTime: editEnabled ? onEditTime : null,
|
onEditTime: editEnabled ? onEditTime : null,
|
||||||
onEditLocation: editEnabled ? onEditLocation : null,
|
onEditLocation: editEnabled ? onEditLocation : null,
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue