| .. | .. |
|---|
| 6 | 6 | import 'package:path_provider/path_provider.dart'; |
|---|
| 7 | 7 | |
|---|
| 8 | 8 | import '../models/message.dart'; |
|---|
| 9 | +import 'trace_service.dart'; |
|---|
| 9 | 10 | |
|---|
| 10 | 11 | /// Per-session JSON file persistence with debounced saves. |
|---|
| 11 | 12 | class MessageStore { |
|---|
| .. | .. |
|---|
| 59 | 60 | |
|---|
| 60 | 61 | /// Write directly to disk, bypassing debounce. For critical saves. |
|---|
| 61 | 62 | static Future<void> writeDirect(String sessionId, List<Message> messages) async { |
|---|
| 63 | + // Cancel ALL pending debounce to prevent race with frozen iOS timers |
|---|
| 64 | + _debounceTimer?.cancel(); |
|---|
| 62 | 65 | _pendingSaves.remove(sessionId); |
|---|
| 63 | 66 | await _writeSession(sessionId, messages); |
|---|
| 64 | 67 | } |
|---|
| .. | .. |
|---|
| 85 | 88 | final file = File('${dir.path}/${_fileForSession(sessionId)}'); |
|---|
| 86 | 89 | // Strip heavy fields for persistence |
|---|
| 87 | 90 | final lightMessages = messages.map((m) => m.toJsonLight()).toList(); |
|---|
| 88 | | - await file.writeAsString(jsonEncode(lightMessages)); |
|---|
| 91 | + final json = jsonEncode(lightMessages); |
|---|
| 92 | + await file.writeAsString(json); |
|---|
| 93 | + TraceService.instance.addTrace('MsgStore WRITE', '${sessionId.substring(0, 8)}: ${messages.length} msgs'); |
|---|
| 89 | 94 | } catch (e) { |
|---|
| 90 | | - // Silently fail - message persistence is best-effort |
|---|
| 95 | + TraceService.instance.addTrace('MsgStore WRITE ERROR', '${sessionId.substring(0, 8)}: $e'); |
|---|
| 91 | 96 | } |
|---|
| 92 | 97 | } |
|---|
| 93 | 98 | |
|---|
| .. | .. |
|---|
| 130 | 135 | |
|---|
| 131 | 136 | final jsonStr = await file.readAsString(); |
|---|
| 132 | 137 | final List<dynamic> jsonList = jsonDecode(jsonStr) as List<dynamic>; |
|---|
| 133 | | - return jsonList |
|---|
| 138 | + final msgs = jsonList |
|---|
| 134 | 139 | .map((j) => _messageFromJson(j as Map<String, dynamic>)) |
|---|
| 135 | 140 | .where((m) => !m.isEmptyVoice && !m.isEmptyText) |
|---|
| 136 | 141 | .toList(); |
|---|
| 142 | + TraceService.instance.addTrace('MsgStore LOAD', '${sessionId.substring(0, 8)}: ${msgs.length} msgs'); |
|---|
| 143 | + return msgs; |
|---|
| 137 | 144 | } catch (e) { |
|---|
| 145 | + TraceService.instance.addTrace('MsgStore LOAD ERROR', '${sessionId.substring(0, 8)}: $e'); |
|---|
| 138 | 146 | return []; |
|---|
| 139 | 147 | } |
|---|
| 140 | 148 | } |
|---|