refactor: actions provider (#19651)

* refactor: actions provider

* chore: rename error and stack

* remove empty checks

---------

Co-authored-by: shenlong-tanwen <139912620+shalong-tanwen@users.noreply.github.com>
This commit is contained in:
shenlong
2025-07-01 08:10:25 +05:30
committed by GitHub
parent 5011636d95
commit 21f500191a
8 changed files with 159 additions and 140 deletions
@@ -1,14 +1,36 @@
import 'package:immich_mobile/constants/enums.dart';
import 'package:immich_mobile/domain/models/asset/base_asset.model.dart';
import 'package:immich_mobile/providers/infrastructure/timeline.provider.dart';
import 'package:immich_mobile/providers/multiselect.provider.dart';
import 'package:immich_mobile/providers/timeline/multiselect.provider.dart';
import 'package:immich_mobile/providers/user.provider.dart';
import 'package:immich_mobile/services/action.service.dart';
import 'package:logging/logging.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';
final actionProvider = NotifierProvider<ActionNotifier, void>(
ActionNotifier.new,
dependencies: [
actionServiceProvider,
timelineServiceProvider,
multiselectProvider,
],
);
class ActionResult {
final int count;
final bool success;
final String? error;
const ActionResult({required this.count, required this.success, this.error});
@override
String toString() =>
'ActionResult(count: $count, success: $success, error: $error)';
}
class ActionNotifier extends Notifier<void> {
final Logger _logger = Logger('ActionNotifier');
late final ActionService _service;
ActionNotifier() : super();
@@ -18,19 +40,89 @@ class ActionNotifier extends Notifier<void> {
_service = ref.watch(actionServiceProvider);
}
Future<void> favorite(List<String> ids) async {
await _service.favorite(ids);
List<String> _getIdsForSource<T extends BaseAsset>(ActionSource source) {
final currentUser = ref.read(currentUserProvider);
if (T is RemoteAsset && currentUser == null) {
return [];
}
final Set<BaseAsset> assets = switch (source) {
ActionSource.timeline =>
ref.read(multiSelectProvider.select((s) => s.selectedAssets)),
ActionSource.viewer => {},
};
return switch (T) {
const (RemoteAsset) => assets
.where(
(asset) => asset is RemoteAsset && asset.ownerId == currentUser!.id,
)
.cast<RemoteAsset>()
.map((asset) => asset.id)
.toList(),
const (LocalAsset) =>
assets.whereType<LocalAsset>().map((asset) => asset.id).toList(),
_ => [],
};
}
Future<void> unFavorite(List<String> ids) async {
await _service.unFavorite(ids);
Future<ActionResult> favorite(ActionSource source) async {
final ids = _getIdsForSource<RemoteAsset>(source);
try {
await _service.favorite(ids);
return ActionResult(count: ids.length, success: true);
} catch (error, stack) {
_logger.severe('Failed to favorite assets', error, stack);
return ActionResult(
count: ids.length,
success: false,
error: error.toString(),
);
}
}
Future<void> archive(List<String> ids) async {
await _service.archive(ids);
Future<ActionResult> unFavorite(ActionSource source) async {
final ids = _getIdsForSource<RemoteAsset>(source);
try {
await _service.unFavorite(ids);
return ActionResult(count: ids.length, success: true);
} catch (error, stack) {
_logger.severe('Failed to unfavorite assets', error, stack);
return ActionResult(
count: ids.length,
success: false,
error: error.toString(),
);
}
}
Future<void> unArchive(List<String> ids) async {
await _service.unArchive(ids);
Future<ActionResult> archive(ActionSource source) async {
final ids = _getIdsForSource<RemoteAsset>(source);
try {
await _service.archive(ids);
return ActionResult(count: ids.length, success: true);
} catch (error, stack) {
_logger.severe('Failed to archive assets', error, stack);
return ActionResult(
count: ids.length,
success: false,
error: error.toString(),
);
}
}
Future<ActionResult> unArchive(ActionSource source) async {
final ids = _getIdsForSource<RemoteAsset>(source);
try {
await _service.unArchive(ids);
return ActionResult(count: ids.length, success: true);
} catch (error, stack) {
_logger.severe('Failed to unarchive assets', error, stack);
return ActionResult(
count: ids.length,
success: false,
error: error.toString(),
);
}
}
}
@@ -1,6 +1,5 @@
import 'package:collection/collection.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:immich_mobile/domain/models/asset/base_asset.model.dart';
import 'package:immich_mobile/domain/services/timeline.service.dart';
import 'package:immich_mobile/providers/infrastructure/timeline.provider.dart';
@@ -13,12 +12,8 @@ final multiSelectProvider =
class MultiSelectState {
final Set<BaseAsset> selectedAssets;
final int lastUpdatedTime;
const MultiSelectState({
required this.selectedAssets,
required this.lastUpdatedTime,
});
const MultiSelectState({required this.selectedAssets});
bool get isEnabled => selectedAssets.isNotEmpty;
bool get hasRemote => selectedAssets.any(
@@ -30,31 +25,25 @@ class MultiSelectState {
(asset) => asset.storage == AssetState.local,
);
MultiSelectState copyWith({
Set<BaseAsset>? selectedAssets,
int? lastUpdatedTime,
}) {
MultiSelectState copyWith({Set<BaseAsset>? selectedAssets}) {
return MultiSelectState(
selectedAssets: selectedAssets ?? this.selectedAssets,
lastUpdatedTime: lastUpdatedTime ?? this.lastUpdatedTime,
);
}
@override
String toString() =>
'MultiSelectState(selectedAssets: $selectedAssets, lastUpdatedTime: $lastUpdatedTime)';
String toString() => 'MultiSelectState(selectedAssets: $selectedAssets)';
@override
bool operator ==(covariant MultiSelectState other) {
if (identical(this, other)) return true;
final listEquals = const DeepCollectionEquality().equals;
return listEquals(other.selectedAssets, selectedAssets) &&
other.lastUpdatedTime == lastUpdatedTime;
return listEquals(other.selectedAssets, selectedAssets);
}
@override
int get hashCode => selectedAssets.hashCode ^ lastUpdatedTime.hashCode;
int get hashCode => selectedAssets.hashCode;
}
class MultiSelectNotifier extends Notifier<MultiSelectState> {
@@ -64,10 +53,7 @@ class MultiSelectNotifier extends Notifier<MultiSelectState> {
MultiSelectState build() {
_timelineService = ref.read(timelineServiceProvider);
return const MultiSelectState(
selectedAssets: {},
lastUpdatedTime: 0,
);
return const MultiSelectState(selectedAssets: {});
}
void selectAsset(BaseAsset asset) {
@@ -98,17 +84,8 @@ class MultiSelectNotifier extends Notifier<MultiSelectState> {
}
}
void clearSelection() {
state = state.copyWith(
selectedAssets: {},
);
}
void reset() {
state = MultiSelectState(
selectedAssets: {},
lastUpdatedTime: DateTime.now().millisecondsSinceEpoch,
);
state = const MultiSelectState(selectedAssets: {});
}
/// Bucket bulk operations