Fix bugs found in full code audit

- Edit screens (note, task): cache _initFuture in initState instead of
  calling _loadExisting() from FutureBuilder on every rebuild. Prevents
  race condition where multiple concurrent loads could overwrite edits.
  Also removes unnecessary _loaded flag and simplifies the pattern.

- Note editor: remove onChanged: (_) => setState((){}) on the body
  TextField — triggered a full rebuild on every keystroke for no reason.

- SSE streaming: use utf8.decode(chunk, allowMalformed: true) so a
  malformed byte sequence from the server skips rather than throwing an
  unhandled FormatException that escaped all catch blocks.

- Chat provider: guard msgs.isEmpty before accessing msgs.last during
  SSE chunk processing to prevent a potential index error on empty state.

- Offline queue drain: check mounted before and after each async gap in
  _drainQueue so ref is never accessed on a disposed widget.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-01 11:56:26 -05:00
parent 647b36837e
commit eef101bef3
5 changed files with 30 additions and 18 deletions
+4 -1
View File
@@ -245,12 +245,15 @@ class _QuickCaptureBarState extends ConsumerState<_QuickCaptureBar> {
}
Future<void> _drainQueue() async {
if (!mounted) return;
final queue = ref.read(captureQueueProvider);
if (queue.isEmpty) return;
final api = ref.read(quickCaptureApiProvider);
for (final text in List<String>.from(queue)) {
if (!mounted) break;
try {
final result = await api.capture(text);
if (!mounted) break;
await ref.read(captureQueueProvider.notifier).dequeue(text);
switch (result.type) {
case 'note':
@@ -263,7 +266,7 @@ class _QuickCaptureBarState extends ConsumerState<_QuickCaptureBar> {
break; // Still offline — stop draining.
} catch (_) {
// Server/parse error — remove to avoid infinite retries.
await ref.read(captureQueueProvider.notifier).dequeue(text);
if (mounted) await ref.read(captureQueueProvider.notifier).dequeue(text);
}
}
}