import 'dart:async'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import '../api/endpoints/requests.dart'; import '../cache/mutation_queue.dart'; import '../library/library_providers.dart' show dioProvider; import '../models/admin_request.dart'; final requestsApiProvider = FutureProvider((ref) async { return RequestsApi(await ref.watch(dioProvider.future)); }); /// Mirrors the web's createMyRequestsQuery auto-poll (#369): refresh /// every 12s while any row is mid-ingest (status='approved'), stop when /// all rows settle. Polls regardless of app foreground/background — a /// future enhancement could pause via WidgetsBindingObserver, but for /// v1 the small extra refresh is acceptable. class MyRequestsController extends AsyncNotifier> { static const Duration _pollInterval = Duration(seconds: 12); Timer? _pollTimer; @override Future> build() async { ref.onDispose(() { _pollTimer?.cancel(); _pollTimer = null; }); final api = await ref.watch(requestsApiProvider.future); final rows = await api.listMine(); _maybeStartPolling(rows); return rows; } void _maybeStartPolling(List rows) { _pollTimer?.cancel(); if (rows.any((r) => r.status == 'approved')) { _pollTimer = Timer.periodic(_pollInterval, (_) { ref.invalidateSelf(); }); } } /// Optimistic remove + REST cancel. On failure the row stays /// removed in-memory and the cancel is queued for replay — this /// keeps the user's intent visible across network loss instead of /// restoring a row they explicitly asked to remove. Future cancel(String id) async { final api = await ref.read(requestsApiProvider.future); final current = state.value ?? const []; state = AsyncData(current.where((r) => r.id != id).toList()); try { await api.cancel(id); } catch (_) { await ref .read(mutationQueueProvider) .enqueue(MutationKinds.requestCancel, {'id': id}); } } } final myRequestsProvider = AsyncNotifierProvider>( MyRequestsController.new);