Browse Source

Merge pull request #4069 from nextcloud/offline-support-bug-fixes

Bug fixes for offline support
Marcel Hibbe 9 months ago
parent
commit
9e48c9a40f

+ 8 - 5
app/src/main/java/com/nextcloud/talk/chat/ChatActivity.kt

@@ -323,6 +323,7 @@ class ChatActivity :
 
     private val onBackPressedCallback = object : OnBackPressedCallback(true) {
         override fun handleOnBackPressed() {
+            chatViewModel.handleChatOnBackPress()
             if (currentlyPlayedVoiceMessage != null) {
                 stopMediaPlayer(currentlyPlayedVoiceMessage!!)
             }
@@ -601,10 +602,12 @@ class ChatActivity :
 
                     val urlForChatting = ApiUtils.getUrlForChat(chatApiVersion, conversationUser?.baseUrl, roomToken)
 
-                    chatViewModel.loadMessages(
-                        withCredentials = credentials!!,
-                        withUrl = urlForChatting
-                    )
+                    if (adapter?.isEmpty == true) {
+                        chatViewModel.loadMessages(
+                            withCredentials = credentials!!,
+                            withUrl = urlForChatting
+                        )
+                    }
                 }
 
                 is ChatViewModel.GetCapabilitiesErrorState -> {
@@ -2717,7 +2720,7 @@ class ChatActivity :
             withUrl = urlForChatting,
             withCredentials = credentials!!,
             withMessageLimit = MESSAGE_PULL_LIMIT,
-            roomToken = currentConversation!!.token!!
+            roomToken = currentConversation!!.token
         )
     }
 

+ 5 - 0
app/src/main/java/com/nextcloud/talk/chat/data/ChatMessageRepository.kt

@@ -65,4 +65,9 @@ interface ChatMessageRepository : LifecycleAwareManager {
      * Gets a individual message.
      */
     suspend fun getMessage(messageId: Long, bundle: Bundle): Flow<ChatMessage>
+
+    /**
+     * Destroys unused resources.
+     */
+    fun handleChatOnBackPress()
 }

+ 13 - 5
app/src/main/java/com/nextcloud/talk/chat/data/network/OfflineFirstChatRepository.kt

@@ -31,6 +31,7 @@ import io.reactivex.schedulers.Schedulers
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.Dispatchers
 import kotlinx.coroutines.Job
+import kotlinx.coroutines.cancel
 import kotlinx.coroutines.delay
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.MutableSharedFlow
@@ -197,8 +198,8 @@ class OfflineFirstChatRepository @Inject constructor(
 
             val networkParams = Bundle()
 
-            while (!itIsPaused) {
-                if (!monitor.isOnline.first()) Thread.sleep(500)
+            while (true) {
+                if (!monitor.isOnline.first() || itIsPaused) Thread.sleep(HALF_SECOND)
 
                 // sync database with server (This is a long blocking call because long polling (lookIntoFuture) is set)
                 networkParams.putSerializable(BundleKeys.KEY_FIELD_MAP, fieldMap)
@@ -457,9 +458,11 @@ class OfflineFirstChatRepository @Inject constructor(
                     // the parent message is always the newest state, no matter how old the system message is.
                     // that's why we can just take the parent, update it in DB and update the UI
                     messageJson.parentMessage?.let { parentMessageJson ->
-                        val parentMessageEntity = parentMessageJson.asEntity(currentUser.id!!)
-                        chatDao.upsertChatMessage(parentMessageEntity)
-                        _updateMessageFlow.emit(parentMessageEntity.asModel())
+                        parentMessageJson.message?.let {
+                            val parentMessageEntity = parentMessageJson.asEntity(currentUser.id!!)
+                            chatDao.upsertChatMessage(parentMessageEntity)
+                            _updateMessageFlow.emit(parentMessageEntity.asModel())
+                        }
                     }
                 }
 
@@ -622,11 +625,16 @@ class OfflineFirstChatRepository @Inject constructor(
         // unused atm
     }
 
+    override fun handleChatOnBackPress() {
+        scope.cancel()
+    }
+
     companion object {
         val TAG = OfflineFirstChatRepository::class.simpleName
         private const val HTTP_CODE_OK: Int = 200
         private const val HTTP_CODE_NOT_MODIFIED = 304
         private const val HTTP_CODE_PRECONDITION_FAILED = 412
+        private const val HALF_SECOND = 500L
         private const val DELAY_TO_ENSURE_MESSAGES_ARE_ADDED: Long = 100
     }
 }

+ 5 - 1
app/src/main/java/com/nextcloud/talk/chat/viewmodels/ChatViewModel.kt

@@ -626,6 +626,10 @@ class ChatViewModel @Inject constructor(
         _getCapabilitiesViewState.value = GetCapabilitiesStartState
     }
 
+    fun handleChatOnBackPress() {
+        chatRepository.handleChatOnBackPress()
+    }
+
     suspend fun getMessageById(url: String, conversationModel: ConversationModel, messageId: Long): Flow<ChatMessage> =
         flow {
             val bundle = Bundle()
@@ -634,7 +638,7 @@ class ChatViewModel @Inject constructor(
                 BundleKeys.KEY_CREDENTIALS,
                 userProvider.currentUser.blockingGet().getCredentials()
             )
-            bundle.putString(BundleKeys.KEY_ROOM_TOKEN, conversationModel.token!!)
+            bundle.putString(BundleKeys.KEY_ROOM_TOKEN, conversationModel.token)
 
             val message = chatRepository.getMessage(messageId, bundle)
             emit(message.first())

+ 1 - 1
app/src/main/java/com/nextcloud/talk/conversationlist/ConversationsListActivity.kt

@@ -373,8 +373,8 @@ class ConversationsListActivity :
             conversationsListViewModel.getRoomsFlow
                 .onEach { list ->
                     // Update Conversations
+                    conversationItems.clear()
                     conversationItemsWithHeader.clear()
-                    conversationItems.clear() // fixme remove this
                     for (conversation in list) {
                         addToConversationItems(conversation)
                     }

+ 35 - 3
app/src/main/java/com/nextcloud/talk/conversationlist/data/network/OfflineFirstConversationsRepository.kt

@@ -9,6 +9,7 @@
 package com.nextcloud.talk.conversationlist.data.network
 
 import android.util.Log
+import com.nextcloud.talk.chat.data.network.ChatNetworkDataSource
 import com.nextcloud.talk.chat.data.network.OfflineFirstChatRepository
 import com.nextcloud.talk.conversationlist.data.OfflineConversationsRepository
 import com.nextcloud.talk.data.database.dao.ConversationsDao
@@ -19,7 +20,9 @@ import com.nextcloud.talk.data.network.NetworkMonitor
 import com.nextcloud.talk.data.user.model.User
 import com.nextcloud.talk.models.domain.ConversationModel
 import com.nextcloud.talk.utils.database.user.CurrentUserProviderNew
+import io.reactivex.Observer
 import io.reactivex.android.schedulers.AndroidSchedulers
+import io.reactivex.disposables.Disposable
 import io.reactivex.schedulers.Schedulers
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.Dispatchers
@@ -29,11 +32,13 @@ import kotlinx.coroutines.flow.MutableSharedFlow
 import kotlinx.coroutines.flow.first
 import kotlinx.coroutines.flow.map
 import kotlinx.coroutines.launch
+import kotlinx.coroutines.runBlocking
 import javax.inject.Inject
 
 class OfflineFirstConversationsRepository @Inject constructor(
     private val dao: ConversationsDao,
     private val network: ConversationsNetworkDataSource,
+    private val chatNetworkDataSource: ChatNetworkDataSource,
     private val monitor: NetworkMonitor,
     private val currentUserProviderNew: CurrentUserProviderNew
 ) : OfflineConversationsRepository {
@@ -64,7 +69,34 @@ class OfflineFirstConversationsRepository @Inject constructor(
         scope.launch {
             val id = user.id!!
             val model = getConversation(id, roomToken)
-            model.let { _conversationFlow.emit(model) }
+            if (model != null) {
+                _conversationFlow.emit(model)
+            } else {
+                chatNetworkDataSource.getRoom(user, roomToken)
+                    .subscribeOn(Schedulers.io())
+                    ?.observeOn(AndroidSchedulers.mainThread())
+                    ?.subscribe(object : Observer<ConversationModel> {
+                        override fun onSubscribe(p0: Disposable) {
+                            // unused atm
+                        }
+
+                        override fun onError(e: Throwable) {
+                            // unused atm
+                        }
+
+                        override fun onComplete() {
+                            // unused atm
+                        }
+
+                        override fun onNext(model: ConversationModel) {
+                            runBlocking {
+                                _conversationFlow.emit(model)
+                                val entityList = listOf(model.asEntity())
+                                dao.upsertConversations(entityList)
+                            }
+                        }
+                    })
+            }
         }
 
     private suspend fun sync(): List<ConversationEntity>? {
@@ -107,9 +139,9 @@ class OfflineFirstConversationsRepository @Inject constructor(
             it.map(ConversationEntity::asModel)
         }.first()
 
-    private suspend fun getConversation(accountId: Long, token: String): ConversationModel {
+    private suspend fun getConversation(accountId: Long, token: String): ConversationModel? {
         val entity = dao.getConversationForUser(accountId, token).first()
-        return entity.asModel()
+        return entity?.asModel()
     }
 
     companion object {

+ 9 - 2
app/src/main/java/com/nextcloud/talk/dagger/modules/RepositoryModule.kt

@@ -10,11 +10,11 @@
 package com.nextcloud.talk.dagger.modules
 
 import com.nextcloud.talk.api.NcApi
+import com.nextcloud.talk.api.NcApiCoroutines
 import com.nextcloud.talk.chat.data.ChatMessageRepository
 import com.nextcloud.talk.chat.data.network.ChatNetworkDataSource
 import com.nextcloud.talk.chat.data.network.OfflineFirstChatRepository
 import com.nextcloud.talk.chat.data.network.RetrofitChatNetwork
-import com.nextcloud.talk.api.NcApiCoroutines
 import com.nextcloud.talk.contacts.ContactsRepository
 import com.nextcloud.talk.contacts.ContactsRepositoryImpl
 import com.nextcloud.talk.conversation.repository.ConversationRepository
@@ -191,10 +191,17 @@ class RepositoryModule {
     fun provideOfflineFirstConversationsRepository(
         dao: ConversationsDao,
         dataSource: ConversationsNetworkDataSource,
+        chatNetworkDataSource: ChatNetworkDataSource,
         networkMonitor: NetworkMonitor,
         currentUserProviderNew: CurrentUserProviderNew
     ): OfflineConversationsRepository {
-        return OfflineFirstConversationsRepository(dao, dataSource, networkMonitor, currentUserProviderNew)
+        return OfflineFirstConversationsRepository(
+            dao,
+            dataSource,
+            chatNetworkDataSource,
+            networkMonitor,
+            currentUserProviderNew
+        )
     }
 
     @Provides

+ 1 - 1
app/src/main/java/com/nextcloud/talk/data/database/dao/ConversationsDao.kt

@@ -20,7 +20,7 @@ interface ConversationsDao {
     fun getConversationsForUser(accountId: Long): Flow<List<ConversationEntity>>
 
     @Query("SELECT * FROM Conversations where accountId = :accountId AND token = :token")
-    fun getConversationForUser(accountId: Long, token: String): Flow<ConversationEntity>
+    fun getConversationForUser(accountId: Long, token: String): Flow<ConversationEntity?>
 
     @Upsert
     fun upsertConversations(conversationEntities: List<ConversationEntity>)