bd1bcab12b
AdminRequestsScreen renders rows from adminRequestsProvider, joining requester user_id → username via adminUsersProvider for display (falls back to UUID prefix when the users list hasn't loaded yet). Approve fires immediately; Reject confirms via dialog. Both are optimistic with rollback in the controller. Adds AdminUsersController build-only (the read side) so the row join works; mutation methods land in Slice 3 alongside the Users screen. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
109 lines
3.4 KiB
Dart
109 lines
3.4 KiB
Dart
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
|
|
|
import '../api/endpoints/admin_invites.dart';
|
|
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';
|
|
import '../models/admin_user.dart';
|
|
|
|
final adminRequestsApiProvider = FutureProvider<AdminRequestsApi>((ref) async {
|
|
return AdminRequestsApi(await ref.watch(dioProvider.future));
|
|
});
|
|
|
|
final adminQuarantineApiProvider =
|
|
FutureProvider<AdminQuarantineApi>((ref) async {
|
|
return AdminQuarantineApi(await ref.watch(dioProvider.future));
|
|
});
|
|
|
|
final adminUsersApiProvider = FutureProvider<AdminUsersApi>((ref) async {
|
|
return AdminUsersApi(await ref.watch(dioProvider.future));
|
|
});
|
|
|
|
final adminInvitesApiProvider = FutureProvider<AdminInvitesApi>((ref) async {
|
|
return AdminInvitesApi(await ref.watch(dioProvider.future));
|
|
});
|
|
|
|
class AdminCounts {
|
|
const AdminCounts({
|
|
required this.requests,
|
|
required this.quarantine,
|
|
required this.users,
|
|
});
|
|
final int requests;
|
|
final int quarantine;
|
|
final int users;
|
|
}
|
|
|
|
/// Fans out to the three list endpoints in parallel and rolls them up
|
|
/// into `{requests, quarantine, users}` for the landing screen badges.
|
|
final adminCountsProvider = FutureProvider<AdminCounts>((ref) async {
|
|
final requestsApi = await ref.watch(adminRequestsApiProvider.future);
|
|
final quarantineApi = await ref.watch(adminQuarantineApiProvider.future);
|
|
final usersApi = await ref.watch(adminUsersApiProvider.future);
|
|
final results = await Future.wait([
|
|
requestsApi.list(),
|
|
quarantineApi.list(),
|
|
usersApi.list(),
|
|
]);
|
|
return AdminCounts(
|
|
requests: (results[0] as List).length,
|
|
quarantine: (results[1] as List).length,
|
|
users: (results[2] as List).length,
|
|
);
|
|
});
|
|
|
|
class AdminRequestsController extends AsyncNotifier<List<AdminRequest>> {
|
|
@override
|
|
Future<List<AdminRequest>> build() async {
|
|
final api = await ref.watch(adminRequestsApiProvider.future);
|
|
return api.list();
|
|
}
|
|
|
|
Future<void> approve(String id) async {
|
|
await _decide(id, (api) => api.approve(id));
|
|
}
|
|
|
|
Future<void> reject(String id) async {
|
|
await _decide(id, (api) => api.reject(id));
|
|
}
|
|
|
|
Future<void> _decide(
|
|
String id,
|
|
Future<void> Function(AdminRequestsApi api) action,
|
|
) async {
|
|
final api = await ref.read(adminRequestsApiProvider.future);
|
|
final current = state.value ?? const <AdminRequest>[];
|
|
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, List<AdminRequest>>(
|
|
AdminRequestsController.new);
|
|
|
|
/// Read side defined here so the Requests screen can join requester
|
|
/// UUID → username for display. Mutation methods (setAdmin,
|
|
/// setAutoApprove, resetPassword, delete) are appended in Slice 3
|
|
/// alongside the Users screen.
|
|
class AdminUsersController extends AsyncNotifier<List<AdminUser>> {
|
|
@override
|
|
Future<List<AdminUser>> build() async {
|
|
final api = await ref.watch(adminUsersApiProvider.future);
|
|
return api.list();
|
|
}
|
|
}
|
|
|
|
final adminUsersProvider =
|
|
AsyncNotifierProvider<AdminUsersController, List<AdminUser>>(
|
|
AdminUsersController.new);
|