@@ -37,6 +37,7 @@ import com.nextcloud.talk.data.database.mappers.toDomainModel
3737import com.nextcloud.talk.data.database.model.ChatMessageEntity
3838import com.nextcloud.talk.data.user.model.User
3939import com.nextcloud.talk.extensions.toIntOrZero
40+ import com.nextcloud.talk.jobs.ShareOperationWorker
4041import com.nextcloud.talk.jobs.UploadAndShareFilesWorker
4142import com.nextcloud.talk.messagesearch.MessageSearchHelper
4243import com.nextcloud.talk.models.MessageDraft
@@ -83,6 +84,7 @@ import kotlinx.coroutines.flow.combine
8384import kotlinx.coroutines.flow.debounce
8485import kotlinx.coroutines.flow.distinctUntilChanged
8586import kotlinx.coroutines.flow.distinctUntilChangedBy
87+ import kotlinx.coroutines.flow.filter
8688import kotlinx.coroutines.flow.filterNotNull
8789import kotlinx.coroutines.flow.first
8890import kotlinx.coroutines.flow.flatMapLatest
@@ -1200,6 +1202,50 @@ class ChatViewModel @AssistedInject constructor(
12001202 .launchIn(viewModelScope)
12011203 }
12021204
1205+ private fun observeShareCompleted () {
1206+ ShareOperationWorker .shareCompletedFlow
1207+ .filter { it == chatRoomToken }
1208+ .onEach {
1209+ Log .d(TAG , " Share completed for room $chatRoomToken — fetching new messages immediately" )
1210+ fetchNewMessagesWithRetry()
1211+ }
1212+ .launchIn(viewModelScope)
1213+
1214+ UploadAndShareFilesWorker .uploadCompletedFlow
1215+ .filter { it == chatRoomToken }
1216+ .onEach {
1217+ Log .d(TAG , " Upload completed for room $chatRoomToken — fetching new messages immediately" )
1218+ UploadAndShareFilesWorker .clearUploadCompletedReplay()
1219+ fetchNewMessagesWithRetry()
1220+ }
1221+ .launchIn(viewModelScope)
1222+ }
1223+
1224+ /* *
1225+ * Retries [ChatMessageRepository.fetchNewMessages] up to [POST_UPLOAD_FETCH_MAX_ATTEMPTS] times,
1226+ * waiting [POST_UPLOAD_FETCH_RETRY_DELAY_MS] ms between attempts. Stops as soon as at least one
1227+ * new message is received so that the happy-path (server responds quickly) has no unnecessary
1228+ * delay, while a slow server still gets a few extra chances before we fall back to the regular
1229+ * insurance-request cycle.
1230+ */
1231+ private suspend fun fetchNewMessagesWithRetry () {
1232+ repeat(POST_UPLOAD_FETCH_MAX_ATTEMPTS ) { attempt ->
1233+ if (attempt > 0 ) {
1234+ Log .d(
1235+ TAG ,
1236+ " fetchNewMessagesWithRetry: attempt ${attempt + 1 } , " +
1237+ " waiting ${POST_UPLOAD_FETCH_RETRY_DELAY_MS } ms"
1238+ )
1239+ delay(POST_UPLOAD_FETCH_RETRY_DELAY_MS )
1240+ }
1241+ val gotMessages = chatRepository.fetchNewMessages()
1242+ if (gotMessages) {
1243+ Log .d(TAG , " fetchNewMessagesWithRetry: new messages received on attempt ${attempt + 1 } " )
1244+ return
1245+ }
1246+ }
1247+ Log .d(TAG , " fetchNewMessagesWithRetry: no new messages after $POST_UPLOAD_FETCH_MAX_ATTEMPTS attempts" )
1248+ }
12031249 private fun handleSystemMessages (chatMessageList : List <ChatMessage >): List <ChatMessage > {
12041250 fun shouldRemoveMessage (currentMessage : MutableMap .MutableEntry <Int , ChatMessage >): Boolean =
12051251 isInfoMessageAboutDeletion(currentMessage) ||
@@ -1289,6 +1335,7 @@ class ChatViewModel @AssistedInject constructor(
12891335
12901336 observeConversationAndUserFirstTime()
12911337 observeConversationAndUserEveryTime()
1338+ observeShareCompleted()
12921339 }
12931340
12941341 fun ConversationModel?.isOneToOneConversation (): Boolean =
@@ -2181,6 +2228,8 @@ class ChatViewModel @AssistedInject constructor(
21812228 private const val MIN_CHARS_FOR_SEARCH = 2
21822229 private const val CONTEXT_MESSAGES_LIMIT = 50
21832230 private const val LOAD_MORE_MESSAGES_LIMIT = 100
2231+ private const val POST_UPLOAD_FETCH_MAX_ATTEMPTS = 4
2232+ private const val POST_UPLOAD_FETCH_RETRY_DELAY_MS = 1_500L
21842233 }
21852234
21862235 sealed class OutOfOfficeUIState {
0 commit comments