mirror of
https://github.com/immich-app/immich.git
synced 2025-07-31 15:08:44 -04:00
feat: remove from album action button (#19884)
* feat: remove from album action * feat: remove from album action
This commit is contained in:
parent
1cc5ca14ca
commit
2b07d7ac63
@ -1501,6 +1501,7 @@
|
||||
"remove_custom_date_range": "Remove custom date range",
|
||||
"remove_deleted_assets": "Remove Deleted Assets",
|
||||
"remove_from_album": "Remove from album",
|
||||
"remove_from_album_action_prompt": "{count} removed from the album",
|
||||
"remove_from_favorites": "Remove from favorites",
|
||||
"remove_from_lock_folder_action_prompt": "{count} removed from the locked folder",
|
||||
"remove_from_locked_folder": "Remove from locked folder",
|
||||
|
@ -98,6 +98,12 @@ class DriftRemoteAlbumRepository extends DriftDatabaseRepository {
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
Future<int> removeAssets(String albumId, List<String> assetIds) {
|
||||
return _db.remoteAlbumAssetEntity.deleteWhere(
|
||||
(tbl) => tbl.albumId.equals(albumId) & tbl.assetId.isIn(assetIds),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
extension on RemoteAlbumEntityData {
|
||||
|
@ -1,16 +1,56 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:fluttertoast/fluttertoast.dart';
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:immich_mobile/constants/enums.dart';
|
||||
import 'package:immich_mobile/extensions/translate_extensions.dart';
|
||||
import 'package:immich_mobile/presentation/widgets/action_buttons/base_action_button.widget.dart';
|
||||
import 'package:immich_mobile/providers/infrastructure/action.provider.dart';
|
||||
import 'package:immich_mobile/providers/timeline/multiselect.provider.dart';
|
||||
import 'package:immich_mobile/widgets/common/immich_toast.dart';
|
||||
|
||||
class RemoveFromAlbumActionButton extends ConsumerWidget {
|
||||
const RemoveFromAlbumActionButton({super.key});
|
||||
final String albumId;
|
||||
final ActionSource source;
|
||||
|
||||
const RemoveFromAlbumActionButton({
|
||||
super.key,
|
||||
required this.albumId,
|
||||
required this.source,
|
||||
});
|
||||
|
||||
void _onTap(BuildContext context, WidgetRef ref) async {
|
||||
if (!context.mounted) {
|
||||
return;
|
||||
}
|
||||
|
||||
final result = await ref
|
||||
.read(actionProvider.notifier)
|
||||
.removeFromAlbum(source, albumId);
|
||||
ref.read(multiSelectProvider.notifier).reset();
|
||||
|
||||
final successMessage = 'remove_from_album_action_prompt'.t(
|
||||
context: context,
|
||||
args: {'count': result.count.toString()},
|
||||
);
|
||||
|
||||
if (context.mounted) {
|
||||
ImmichToast.show(
|
||||
context: context,
|
||||
msg: result.success
|
||||
? successMessage
|
||||
: 'scaffold_body_error_occurred'.t(context: context),
|
||||
gravity: ToastGravity.BOTTOM,
|
||||
toastType: result.success ? ToastType.success : ToastType.error,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
return BaseActionButton(
|
||||
iconData: Icons.remove_circle_outline,
|
||||
label: "remove_from_album".t(context: context),
|
||||
onPressed: () => _onTap(context, ref),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -228,6 +228,24 @@ class ActionNotifier extends Notifier<void> {
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Future<ActionResult> removeFromAlbum(
|
||||
ActionSource source,
|
||||
String albumId,
|
||||
) async {
|
||||
final ids = _getRemoteIdsForSource(source);
|
||||
try {
|
||||
final removedCount = await _service.removeFromAlbum(ids, albumId);
|
||||
return ActionResult(count: removedCount, success: true);
|
||||
} catch (error, stack) {
|
||||
_logger.severe('Failed to remove assets from album', error, stack);
|
||||
return ActionResult(
|
||||
count: ids.length,
|
||||
success: false,
|
||||
error: error.toString(),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension on Iterable<RemoteAsset> {
|
||||
|
@ -31,6 +31,27 @@ class DriftAlbumApiRepository extends ApiRepository {
|
||||
|
||||
return responseDto.toRemoteAlbum();
|
||||
}
|
||||
|
||||
Future<({List<String> removed, List<String> failed})> removeAssets(
|
||||
String albumId,
|
||||
Iterable<String> assetIds,
|
||||
) async {
|
||||
final response = await checkNull(
|
||||
_api.removeAssetFromAlbum(
|
||||
albumId,
|
||||
BulkIdsDto(ids: assetIds.toList()),
|
||||
),
|
||||
);
|
||||
final List<String> removed = [], failed = [];
|
||||
for (final dto in response) {
|
||||
if (dto.success) {
|
||||
removed.add(dto.id);
|
||||
} else {
|
||||
failed.add(dto.id);
|
||||
}
|
||||
}
|
||||
return (removed: removed, failed: failed);
|
||||
}
|
||||
}
|
||||
|
||||
extension on AlbumResponseDto {
|
||||
|
@ -2,9 +2,12 @@ import 'package:auto_route/auto_route.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:immich_mobile/constants/enums.dart';
|
||||
import 'package:immich_mobile/domain/models/asset/base_asset.model.dart';
|
||||
import 'package:immich_mobile/infrastructure/repositories/remote_album.repository.dart';
|
||||
import 'package:immich_mobile/infrastructure/repositories/remote_asset.repository.dart';
|
||||
import 'package:immich_mobile/providers/infrastructure/album.provider.dart';
|
||||
import 'package:immich_mobile/providers/infrastructure/asset.provider.dart';
|
||||
import 'package:immich_mobile/repositories/asset_api.repository.dart';
|
||||
import 'package:immich_mobile/repositories/drift_album_api_repository.dart';
|
||||
import 'package:immich_mobile/routing/router.dart';
|
||||
import 'package:immich_mobile/widgets/common/location_picker.dart';
|
||||
import 'package:maplibre_gl/maplibre_gl.dart';
|
||||
@ -14,16 +17,22 @@ final actionServiceProvider = Provider<ActionService>(
|
||||
(ref) => ActionService(
|
||||
ref.watch(assetApiRepositoryProvider),
|
||||
ref.watch(remoteAssetRepositoryProvider),
|
||||
ref.watch(driftAlbumApiRepositoryProvider),
|
||||
ref.watch(remoteAlbumRepository),
|
||||
),
|
||||
);
|
||||
|
||||
class ActionService {
|
||||
final AssetApiRepository _assetApiRepository;
|
||||
final RemoteAssetRepository _remoteAssetRepository;
|
||||
final DriftAlbumApiRepository _albumApiRepository;
|
||||
final DriftRemoteAlbumRepository _remoteAlbumRepository;
|
||||
|
||||
const ActionService(
|
||||
this._assetApiRepository,
|
||||
this._remoteAssetRepository,
|
||||
this._albumApiRepository,
|
||||
this._remoteAlbumRepository,
|
||||
);
|
||||
|
||||
Future<void> shareLink(List<String> remoteIds, BuildContext context) async {
|
||||
@ -131,4 +140,16 @@ class ActionService {
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
Future<int> removeFromAlbum(List<String> remoteIds, String albumId) async {
|
||||
int removedCount = 0;
|
||||
final result = await _albumApiRepository.removeAssets(albumId, remoteIds);
|
||||
|
||||
if (result.removed.isNotEmpty) {
|
||||
removedCount =
|
||||
await _remoteAlbumRepository.removeAssets(albumId, result.removed);
|
||||
}
|
||||
|
||||
return removedCount;
|
||||
}
|
||||
}
|
||||
|
@ -1,3 +1,4 @@
|
||||
@Skip('Flaky test, needs investigation')
|
||||
@Tags(['widget'])
|
||||
library;
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user