import 'package:flutter_riverpod/flutter_riverpod.dart'; import '../core/exceptions.dart'; import 'api_client_provider.dart'; import 'capture_queue_provider.dart'; import 'notes_provider.dart'; import 'tasks_provider.dart'; /// Outcome of a single capture attempt — consumed by the UI for snackbars. class CaptureResult { final String message; final bool isError; const CaptureResult(this.message, {this.isError = false}); } /// The most recent capture result. UI watches this to show snackbars. /// Reset to null by the notifier before each new item so listeners always fire. final captureResultProvider = NotifierProvider<_CaptureResultNotifier, CaptureResult?>( _CaptureResultNotifier.new); class _CaptureResultNotifier extends Notifier { @override CaptureResult? build() => null; } /// In-memory sequential work queue for quick captures. /// Separate from [captureQueueProvider] (which is the offline persistence queue). final captureWorkQueueProvider = NotifierProvider>( CaptureWorkQueueNotifier.new, ); class CaptureWorkQueueNotifier extends Notifier> { bool _running = false; @override List build() => []; /// Add text to the queue and start the drain loop if not already running. void enqueue(String text) { state = [...state, text]; _drain(); } Future _drain() async { if (_running) return; _running = true; try { while (state.isNotEmpty) { final text = state.first; // Signal "no result yet" so the same result value can re-trigger watch. ref.read(captureResultProvider.notifier).state = null; try { final api = ref.read(quickCaptureApiProvider); final result = await api.capture(text); // Dequeue on success. state = state.length > 1 ? state.sublist(1) : []; // Invalidate content providers so lists refresh. switch (result.type) { case 'note': ref.invalidate(notesProvider); case 'task': case 'todo': ref.invalidate(tasksProvider); } // Publish result for snackbar. final msg = result.message.isNotEmpty ? result.message : '${_typeLabel(result.type)} created: ${result.title}'; ref.read(captureResultProvider.notifier).state = CaptureResult(msg); } on NetworkException catch (_) { // Persist to offline queue and stop draining — still offline. await ref.read(captureQueueProvider.notifier).enqueue(text); state = state.length > 1 ? state.sublist(1) : []; ref.read(captureResultProvider.notifier).state = CaptureResult( "You're offline — capture saved and will retry automatically.", ); break; } on AppException catch (e) { state = state.length > 1 ? state.sublist(1) : []; ref.read(captureResultProvider.notifier).state = CaptureResult(e.message, isError: true); } catch (_) { state = state.length > 1 ? state.sublist(1) : []; ref.read(captureResultProvider.notifier).state = CaptureResult('Capture failed. Please try again.', isError: true); } } } finally { _running = false; } } String _typeLabel(String type) => switch (type) { 'note' => 'Note', 'task' => 'Task', 'event' => 'Event', 'todo' => 'To-do', _ => type, }; }