From 918659422bad1684646e98e98f657d7d29424e28 Mon Sep 17 00:00:00 2001 From: Bruno Deanoz Date: Sun, 21 Jun 2026 18:16:51 +0200 Subject: [PATCH] feat: background prefetch of all conversation messages for offline access On app start, after syncing the conversation list, prefetch messages for any conversation not yet cached in Room. This ensures all chats are readable offline, not just previously opened ones. --- app/src/main/java/dev/kaizen/app/chat/ChatScreen.kt | 4 ++++ app/src/main/java/dev/kaizen/app/db/ConversationDao.kt | 3 +++ .../java/dev/kaizen/app/db/ConversationRepository.kt | 2 ++ app/src/main/java/dev/kaizen/app/db/MessageDao.kt | 3 +++ .../main/java/dev/kaizen/app/db/MessageRepository.kt | 10 ++++++++++ 5 files changed, 22 insertions(+) diff --git a/app/src/main/java/dev/kaizen/app/chat/ChatScreen.kt b/app/src/main/java/dev/kaizen/app/chat/ChatScreen.kt index 6579fdc..9f4ffed 100644 --- a/app/src/main/java/dev/kaizen/app/chat/ChatScreen.kt +++ b/app/src/main/java/dev/kaizen/app/chat/ChatScreen.kt @@ -166,6 +166,10 @@ fun ChatScreen( if (errors.isNotEmpty()) { loadError = errors.joinToString(" · ") } + scope.launch { + val ids = chat.conversationRepo.allUnlockedIds() + chat.messageRepo.prefetchMissing(cfg.baseUrl, cfg.token, ids) + } } // Warm the HTTP/2 connection pool on every app resume (covers background → foreground diff --git a/app/src/main/java/dev/kaizen/app/db/ConversationDao.kt b/app/src/main/java/dev/kaizen/app/db/ConversationDao.kt index 644b523..01b5936 100644 --- a/app/src/main/java/dev/kaizen/app/db/ConversationDao.kt +++ b/app/src/main/java/dev/kaizen/app/db/ConversationDao.kt @@ -25,4 +25,7 @@ interface ConversationDao { @Query("SELECT cachedAt FROM conversations LIMIT 1") suspend fun lastCachedAt(): Long? + + @Query("SELECT id FROM conversations WHERE locked = 0") + suspend fun allUnlockedIds(): List } diff --git a/app/src/main/java/dev/kaizen/app/db/ConversationRepository.kt b/app/src/main/java/dev/kaizen/app/db/ConversationRepository.kt index 9558461..870074e 100644 --- a/app/src/main/java/dev/kaizen/app/db/ConversationRepository.kt +++ b/app/src/main/java/dev/kaizen/app/db/ConversationRepository.kt @@ -22,4 +22,6 @@ class ConversationRepository(private val dao: ConversationDao) { } suspend fun lastCachedAt(): Long? = dao.lastCachedAt() + + suspend fun allUnlockedIds(): List = dao.allUnlockedIds() } diff --git a/app/src/main/java/dev/kaizen/app/db/MessageDao.kt b/app/src/main/java/dev/kaizen/app/db/MessageDao.kt index 9e39e23..6f61fbf 100644 --- a/app/src/main/java/dev/kaizen/app/db/MessageDao.kt +++ b/app/src/main/java/dev/kaizen/app/db/MessageDao.kt @@ -18,4 +18,7 @@ interface MessageDao { @Query("DELETE FROM messages WHERE conversationId = :convId") suspend fun deleteByConversation(convId: String) + + @Query("SELECT DISTINCT conversationId FROM messages") + suspend fun cachedConversationIds(): List } diff --git a/app/src/main/java/dev/kaizen/app/db/MessageRepository.kt b/app/src/main/java/dev/kaizen/app/db/MessageRepository.kt index 1e5fa17..b90df6d 100644 --- a/app/src/main/java/dev/kaizen/app/db/MessageRepository.kt +++ b/app/src/main/java/dev/kaizen/app/db/MessageRepository.kt @@ -21,4 +21,14 @@ class MessageRepository(private val dao: MessageDao) { suspend fun cacheMessages(convId: String, messages: List) { dao.upsertAll(messages) } + + suspend fun prefetchMissing(baseUrl: String, token: String, conversationIds: List) { + val cached = dao.cachedConversationIds().toSet() + for (id in conversationIds) { + if (id in cached) continue + try { + fetchAndCache(baseUrl, token, id) + } catch (_: Exception) { } + } + } }