From 8bb192fe54e7b7f4fb393612ecc4c0035fd6f942 Mon Sep 17 00:00:00 2001 From: Bryan Van Deusen Date: Fri, 8 May 2026 21:54:05 -0400 Subject: [PATCH] feat(flutter/admin): adminRequestsProvider with optimistic approve/reject AsyncNotifier removes the row optimistically on approve/reject; rolls back to the pre-mutation list on server error. Invalidates adminCountsProvider so the landing badge stays accurate. Co-Authored-By: Claude Opus 4.7 (1M context) --- flutter_client/lib/admin/admin_providers.dart | 38 +++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/flutter_client/lib/admin/admin_providers.dart b/flutter_client/lib/admin/admin_providers.dart index a812380e..75b24ba5 100644 --- a/flutter_client/lib/admin/admin_providers.dart +++ b/flutter_client/lib/admin/admin_providers.dart @@ -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_request.dart'; final adminRequestsApiProvider = FutureProvider((ref) async { return AdminRequestsApi(await ref.watch(dioProvider.future)); @@ -51,3 +52,40 @@ final adminCountsProvider = FutureProvider((ref) async { users: (results[2] as List).length, ); }); + +class AdminRequestsController extends AsyncNotifier> { + @override + Future> build() async { + final api = await ref.watch(adminRequestsApiProvider.future); + return api.list(); + } + + Future approve(String id) async { + await _decide(id, (api) => api.approve(id)); + } + + Future reject(String id) async { + await _decide(id, (api) => api.reject(id)); + } + + Future _decide( + String id, + Future Function(AdminRequestsApi api) action, + ) async { + final api = await ref.read(adminRequestsApiProvider.future); + final current = state.value ?? const []; + state = AsyncData(current.where((r) => r.id != id).toList()); + try { + await action(api); + // Refresh landing badge. + ref.invalidate(adminCountsProvider); + } catch (e, st) { + state = AsyncData(current); + Error.throwWithStackTrace(e, st); + } + } +} + +final adminRequestsProvider = + AsyncNotifierProvider>( + AdminRequestsController.new);