@@ -12,6 +12,7 @@ import com.anytypeio.anytype.domain.debugging.Logger
1212import com.anytypeio.anytype.domain.library.StoreSearchByIdsParams
1313import com.anytypeio.anytype.domain.library.StorelessSubscriptionContainer
1414import java.util.concurrent.ConcurrentHashMap
15+ import java.util.concurrent.ConcurrentLinkedDeque
1516import javax.inject.Inject
1617import kotlinx.coroutines.CoroutineScope
1718import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -76,6 +77,9 @@ interface ChatPreviewContainer {
7677 private val previews = MutableStateFlow <List <Chat .Preview >? > (null )
7778 private val attachmentIds = MutableStateFlow <Map <SpaceId , Set <Id >>>(emptyMap())
7879
80+ // Buffer of last N preview messages per chat for fallback on deletion (thread-safe)
81+ private val messageHistory = ConcurrentHashMap <Id , ConcurrentLinkedDeque <Chat .Message >>()
82+
7983 // Hot shared state for UI collectors
8084 @OptIn(ExperimentalCoroutinesApi ::class )
8185 private val previewsState: StateFlow <PreviewState > = previews
@@ -115,13 +119,22 @@ interface ChatPreviewContainer {
115119
116120 attachmentIds.value = emptyMap() // Reset attachment tracking
117121 previews.value = null // Reset previews
122+ messageHistory.clear() // Reset message history
118123
119124 val initial = runCatching {
120125 repo.subscribeToMessagePreviews(SUBSCRIPTION_ID )
121126 }.onFailure {
122127 logger.logWarning(" Error while getting initial previews: ${it.message} " )
123128 }.getOrDefault(emptyList())
124129
130+ // Initialize history from initial previews
131+ initial.forEach { preview ->
132+ preview.message?.let { message ->
133+ val history = messageHistory.getOrPut(preview.chat) { ConcurrentLinkedDeque () }
134+ history.addLast(message)
135+ }
136+ }
137+
125138 previews.value = initial // ← Ready (may be empty)
126139 trackMissingAttachments(initial)
127140 collectEvents(initial)
@@ -133,6 +146,7 @@ interface ChatPreviewContainer {
133146 job?.cancel()
134147 job = null
135148 previews.value = null // back to Loading for next start()
149+ messageHistory.clear() // clear message history
136150 unsubscribeAll()
137151 attachmentFlows.clear() // let the cached StateFlows be GC-ed
138152 }
@@ -200,7 +214,11 @@ interface ChatPreviewContainer {
200214
201215 is Event .Command .Chats .Delete -> state.map { preview ->
202216 if (preview.chat == event.context && preview.message?.id == event.message) {
203- preview.copy(message = null )
217+ // Remove deleted message from history and get previous message
218+ val history = messageHistory[event.context]
219+ history?.removeIf { it.id == event.message }
220+ val previousMessage = history?.lastOrNull()
221+ preview.copy(message = previousMessage)
204222 } else preview
205223 }
206224
@@ -214,6 +232,14 @@ interface ChatPreviewContainer {
214232 state : List <Chat .Preview >,
215233 event : Event .Command .Chats .Add
216234 ): List <Chat .Preview > {
235+ // Store message in history buffer for fallback on deletion
236+ val chatId = event.context
237+ val history = messageHistory.getOrPut(chatId) { ConcurrentLinkedDeque () }
238+ if (history.size >= MAX_MESSAGE_HISTORY ) {
239+ history.removeFirst()
240+ }
241+ history.addLast(event.message)
242+
217243 // Extract attachment IDs
218244 val messageAttachmentIds = event.message.attachments.map { it.target }.toSet()
219245 val dependencyIds = event.dependencies.map { it.id }.toSet()
@@ -359,6 +385,7 @@ interface ChatPreviewContainer {
359385 companion object {
360386 private const val SUBSCRIPTION_ID = " chat-previews-subscription"
361387 private const val ATTACHMENT_SUBSCRIPTION_POSTFIX = " chat-previews-attachments"
388+ private const val MAX_MESSAGE_HISTORY = 10
362389 }
363390 }
364391}
0 commit comments