immich/mobile/lib/presentation/widgets/action_buttons/add_action_button.widget.dart
shenlong 835c35e940
chore: migrate mobile translations accessor (2) (#30668)
Co-authored-by: shenlong-tanwen <139912620+shalong-tanwen@users.noreply.github.com>
2026-08-11 14:41:12 +05:30

191 lines
6.5 KiB
Dart

import 'dart:async';
import 'package:flutter/material.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:immich_mobile/constants/enums.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/extensions/build_context_extensions.dart';
import 'package:immich_mobile/generated/translations.g.dart';
import 'package:immich_mobile/presentation/actions/action.widget.dart';
import 'package:immich_mobile/presentation/actions/archive.action.dart';
import 'package:immich_mobile/presentation/actions/lock.action.dart';
import 'package:immich_mobile/presentation/widgets/action_buttons/base_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/providers/asset_viewer/asset_viewer.provider.dart';
import 'package:immich_mobile/providers/infrastructure/action.provider.dart';
import 'package:immich_mobile/providers/infrastructure/album.provider.dart';
import 'package:immich_mobile/providers/user.provider.dart';
import 'package:immich_mobile/widgets/common/immich_toast.dart';
import 'package:immich_ui/immich_ui.dart';
enum AddToMenuItem { album }
class AddActionButton extends ConsumerStatefulWidget {
const AddActionButton({super.key, this.originalTheme});
final ThemeData? originalTheme;
@override
ConsumerState<AddActionButton> createState() => _AddActionButtonState();
}
class _AddActionButtonState extends ConsumerState<AddActionButton> {
void _handleMenuSelection(AddToMenuItem selected) {
switch (selected) {
case AddToMenuItem.album:
_openAlbumSelector();
}
}
List<Widget> _buildMenuChildren() {
final asset = ref.read(assetViewerProvider).currentAsset;
if (asset == null) {
return [];
}
final user = ref.read(currentUserProvider);
final isOwner = asset is RemoteAsset && asset.ownerId == user?.id;
return [
Padding(
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
child: Text(context.t.add_to_bottom_bar, style: context.textTheme.labelMedium),
),
BaseActionButton(
iconData: Icons.photo_album_outlined,
label: context.t.album,
menuItem: true,
onPressed: () => _handleMenuSelection(AddToMenuItem.album),
),
if (isOwner) ...[
const Divider(),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
child: Text(context.t.move_to, style: context.textTheme.labelMedium),
),
const ActionMenuItem(action: ArchiveAction(source: .viewer)),
const ActionMenuItem(action: LockAction(source: .viewer)),
],
];
}
void _openAlbumSelector() {
final currentAsset = ref.read(assetViewerProvider).currentAsset;
if (currentAsset == null) {
ImmichToast.show(context: context, msg: "Cannot load asset information.", toastType: ToastType.error);
return;
}
final List<Widget> slivers = [
const CreateAlbumButton(),
AlbumSelector(onAlbumSelected: (album) => _addCurrentAssetToAlbum(album)),
];
unawaited(
showModalBottomSheet(
context: context,
isScrollControlled: true,
backgroundColor: Colors.transparent,
builder: (_) {
return BaseBottomSheet(
actions: const [],
slivers: slivers,
initialChildSize: 0.6,
minChildSize: 0.3,
maxChildSize: 0.95,
expand: false,
backgroundColor: context.isDarkTheme ? Colors.black : Colors.white,
);
},
),
);
}
Future<void> _addCurrentAssetToAlbum(RemoteAlbum album) async {
final latest = ref.read(assetViewerProvider).currentAsset;
if (latest == null) {
ImmichToast.show(context: context, msg: "Cannot load asset information.", toastType: ToastType.error);
return;
}
final result = await ref.read(actionProvider.notifier).addToAlbum(ActionSource.viewer, album);
if (!mounted) {
return;
}
if (!result.success) {
ImmichToast.show(context: context, msg: context.t.scaffold_body_error_occurred, toastType: ToastType.error);
return;
}
// Only report the failure when nothing was added; if some succeeded we show "added".
if (result.count > 0) {
ImmichToast.show(
context: context,
msg: context.t.add_to_album_bottom_sheet_added(album: album.name),
);
// Refresh the "Appears in" list on the asset's info panel.
ref.invalidate(albumsContainingAssetProvider(latest.remoteId!));
} else if (result.failedCount > 0) {
ImmichToast.show(
context: context,
msg: context.t.assets_cannot_be_added_to_album_count(count: result.failedCount),
toastType: ToastType.error,
);
} else {
ImmichToast.show(
context: context,
msg: context.t.add_to_album_bottom_sheet_already_exists(album: album.name),
);
}
if (!mounted) {
return;
}
await Navigator.of(context).maybePop();
}
@override
Widget build(BuildContext context) {
final asset = ref.watch(assetViewerProvider.select((s) => s.currentAsset));
if (asset == null) {
return const SizedBox.shrink();
}
final themeData = widget.originalTheme ?? context.themeData;
return ImmichMenu(
consumeOutsideTap: true,
style: MenuStyle(
backgroundColor: WidgetStatePropertyAll(themeData.scaffoldBackgroundColor),
surfaceTintColor: const WidgetStatePropertyAll(Colors.grey),
elevation: const WidgetStatePropertyAll(4),
shape: const WidgetStatePropertyAll(
RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(12))),
),
padding: const WidgetStatePropertyAll(EdgeInsets.symmetric(vertical: 6)),
),
children: widget.originalTheme != null
? [
Theme(
data: widget.originalTheme!,
child: Column(crossAxisAlignment: CrossAxisAlignment.start, children: _buildMenuChildren()),
),
]
: _buildMenuChildren(),
builder: (context, controller, child) {
return BaseActionButton(
iconData: Icons.add,
label: context.t.add_to_bottom_bar,
onPressed: () => controller.isOpen ? controller.close() : controller.open(),
);
},
);
}
}