From fa34201bc07e5312ff0c6825933cd02ce7900254 Mon Sep 17 00:00:00 2001
From: Matthias Nott <mnott@mnsoft.org>
Date: Sat, 21 Mar 2026 20:55:10 +0100
Subject: [PATCH] fix: voice caption ordering, background audio, image persistence

---
 lib/services/message_store.dart |   27 +++++++++++++++++++++++++--
 1 files changed, 25 insertions(+), 2 deletions(-)

diff --git a/lib/services/message_store.dart b/lib/services/message_store.dart
index 448390e..424d73a 100644
--- a/lib/services/message_store.dart
+++ b/lib/services/message_store.dart
@@ -82,7 +82,7 @@
       final jsonStr = await file.readAsString();
       final List<dynamic> jsonList = jsonDecode(jsonStr) as List<dynamic>;
       final allMessages = jsonList
-          .map((j) => Message.fromJson(j as Map<String, dynamic>))
+          .map((j) => _messageFromJson(j as Map<String, dynamic>))
           .where((m) => !m.isEmptyVoice) // Filter out voice msgs with no content
           .toList();
 
@@ -106,7 +106,7 @@
       final jsonStr = await file.readAsString();
       final List<dynamic> jsonList = jsonDecode(jsonStr) as List<dynamic>;
       return jsonList
-          .map((j) => Message.fromJson(j as Map<String, dynamic>))
+          .map((j) => _messageFromJson(j as Map<String, dynamic>))
           .where((m) => !m.isEmptyVoice)
           .toList();
     } catch (e) {
@@ -114,6 +114,29 @@
     }
   }
 
+  /// Deserialize a message from JSON, applying migration rules:
+  /// - Voice messages without audioUri are downgraded to text (transcript only).
+  ///   This handles messages saved before a restart, where the temp audio file
+  ///   is no longer available. The transcript (content) is preserved.
+  static Message _messageFromJson(Map<String, dynamic> json) {
+    final raw = Message.fromJson(json);
+    if (raw.type == MessageType.voice &&
+        (raw.audioUri == null || raw.audioUri!.isEmpty)) {
+      // Downgrade to text so the bubble shows the transcript instead of a
+      // broken play button.
+      return Message(
+        id: raw.id,
+        role: raw.role,
+        type: MessageType.text,
+        content: raw.content,
+        timestamp: raw.timestamp,
+        status: raw.status,
+        duration: raw.duration,
+      );
+    }
+    return raw;
+  }
+
   /// Delete stored messages for a session.
   static Future<void> delete(String sessionId) async {
     try {

--
Gitblit v1.3.1