@@ -102,7 +102,10 @@ open class SessionRepository(
103 fun schedule(snapshot: List<MessageWithParts>) {
103 fun schedule(snapshot: List<MessageWithParts>) {
104 val cache = messageCache ?: return
104 val cache = messageCache ?: return
105− if (snapshot.isEmpty()) return
105+ // Don't early-return on an empty snapshot: MessageCacheStore.save deletes the file
106+ // for an empty list, which is exactly what we want when a conversation becomes empty
107+ // via MessageRemoved events. Skipping here would leave stale messages cached on disk
108+ // until an explicit remove() on session deletion.
106 latest = snapshot
109 latest = snapshot
107 val now = System.currentTimeMillis()
110 val now = System.currentTimeMillis()
108 pending?.cancel()
111 pending?.cancel()
@@ -179,7 +182,11 @@ open class SessionRepository(
179 if (cached.isNotEmpty()) {
182 if (cached.isNotEmpty()) {
180 val changed = lock.withLock {
183 val changed = lock.withLock {
181 if (seedGeneration == 0) {
184 if (seedGeneration == 0) {
185+ // Store the synthetic key for UnknownMessages (which have empty ids) so
186+ // pruneStaleCacheSeeded's messages.remove(id) hits the actual map key
187+ // the store used ("unknown-c<created>" / "unknown-h<hash>") instead of
188+ // the empty id that never matches and leaves ghost entries un-pruned.
182− cacheSeededIds = cached.mapTo(mutableSetOf()) { it.info.id }
189+ cacheSeededIds = cached.mapTo(mutableSetOf()) { store.syntheticKeyFor(it) }
183 store.seed(cached, clearPivot = false)
190 store.seed(cached, clearPivot = false)
184 } else false
191 } else false
@@ -522,7 +529,7 @@ internal class MessageStore {
522 * synthetic key ("unknown-c<created>" or "unknown-h<hash>"); derive the same key here so
529 * synthetic key ("unknown-c<created>" or "unknown-h<hash>"); derive the same key here so
523 * a REST seed merges onto the existing holder instead of missing it and inserting a
530 * a REST seed merges onto the existing holder instead of missing it and inserting a
524 * duplicate under key "". */
531 * duplicate under key "". */
525− private fun syntheticKeyFor(m: MessageWithParts): String =
532+ internal fun syntheticKeyFor(m: MessageWithParts): String =
526 if (m.info.id.isEmpty() && m.info is UnknownMessage) {
533 if (m.info.id.isEmpty() && m.info is UnknownMessage) {
527 m.info.time?.created?.let { "unknown-c$it" } ?: "unknown-h${m.info.hashCode()}"
534 m.info.time?.created?.let { "unknown-c$it" } ?: "unknown-h${m.info.hashCode()}"