feat(flutter/admin): quarantine screen + typed-confirm sheet
ExpansionTile per aggregated quarantine row — collapsed shows track,
artist · album, and "{N} reports: {top reason}". Expanded reveals the
nested per-user reports with their reasons + optional notes.
Three-dot menu offers Resolve / Delete file / Delete via Lidarr; both
deletes route through TypedConfirmSheet which requires the user to
type "DELETE" before the action button enables.
Adds AdminQuarantineController with optimistic-removal + rollback,
mirroring AdminRequestsController's pattern.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -5,6 +5,7 @@ import '../api/endpoints/admin_quarantine.dart';
|
||||
import '../api/endpoints/admin_requests.dart';
|
||||
import '../api/endpoints/admin_users.dart';
|
||||
import '../library/library_providers.dart' show dioProvider;
|
||||
import '../models/admin_quarantine_item.dart';
|
||||
import '../models/admin_request.dart';
|
||||
import '../models/admin_user.dart';
|
||||
|
||||
@@ -106,3 +107,38 @@ class AdminUsersController extends AsyncNotifier<List<AdminUser>> {
|
||||
final adminUsersProvider =
|
||||
AsyncNotifierProvider<AdminUsersController, List<AdminUser>>(
|
||||
AdminUsersController.new);
|
||||
|
||||
class AdminQuarantineController
|
||||
extends AsyncNotifier<List<AdminQuarantineItem>> {
|
||||
@override
|
||||
Future<List<AdminQuarantineItem>> build() async {
|
||||
final api = await ref.watch(adminQuarantineApiProvider.future);
|
||||
return api.list();
|
||||
}
|
||||
|
||||
Future<void> resolve(String trackId) =>
|
||||
_act(trackId, (api) => api.resolve(trackId));
|
||||
Future<void> deleteFile(String trackId) =>
|
||||
_act(trackId, (api) => api.deleteFile(trackId));
|
||||
Future<void> deleteViaLidarr(String trackId) =>
|
||||
_act(trackId, (api) => api.deleteViaLidarr(trackId));
|
||||
|
||||
Future<void> _act(
|
||||
String trackId,
|
||||
Future<void> Function(AdminQuarantineApi api) action,
|
||||
) async {
|
||||
final api = await ref.read(adminQuarantineApiProvider.future);
|
||||
final current = state.value ?? const <AdminQuarantineItem>[];
|
||||
state = AsyncData(current.where((q) => q.trackId != trackId).toList());
|
||||
try {
|
||||
await action(api);
|
||||
ref.invalidate(adminCountsProvider);
|
||||
} catch (e, st) {
|
||||
state = AsyncData(current);
|
||||
Error.throwWithStackTrace(e, st);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
final adminQuarantineProvider = AsyncNotifierProvider<AdminQuarantineController,
|
||||
List<AdminQuarantineItem>>(AdminQuarantineController.new);
|
||||
|
||||
Reference in New Issue
Block a user