mirror of
https://github.com/nextcloud/talk-android
synced 2025-06-20 03:59:35 +01:00
Follow up bug fixes for offline support
Got join conversation to work Unread message popup should work when entering a conversation now "Delete All Messages" now works without breaking the initMessagePolling linter Signed-off-by: rapterjet2004 <juliuslinus1@gmail.com> Signed-off-by: Marcel Hibbe <dev@mhibbe.de>
This commit is contained in:
parent
822af2c967
commit
ea453dba3e
@ -323,6 +323,7 @@ class ChatActivity :
|
|||||||
|
|
||||||
private val onBackPressedCallback = object : OnBackPressedCallback(true) {
|
private val onBackPressedCallback = object : OnBackPressedCallback(true) {
|
||||||
override fun handleOnBackPressed() {
|
override fun handleOnBackPressed() {
|
||||||
|
chatViewModel.handleChatOnBackPress()
|
||||||
if (currentlyPlayedVoiceMessage != null) {
|
if (currentlyPlayedVoiceMessage != null) {
|
||||||
stopMediaPlayer(currentlyPlayedVoiceMessage!!)
|
stopMediaPlayer(currentlyPlayedVoiceMessage!!)
|
||||||
}
|
}
|
||||||
@ -601,11 +602,13 @@ class ChatActivity :
|
|||||||
|
|
||||||
val urlForChatting = ApiUtils.getUrlForChat(chatApiVersion, conversationUser?.baseUrl, roomToken)
|
val urlForChatting = ApiUtils.getUrlForChat(chatApiVersion, conversationUser?.baseUrl, roomToken)
|
||||||
|
|
||||||
|
if (adapter?.isEmpty == true) {
|
||||||
chatViewModel.loadMessages(
|
chatViewModel.loadMessages(
|
||||||
withCredentials = credentials!!,
|
withCredentials = credentials!!,
|
||||||
withUrl = urlForChatting
|
withUrl = urlForChatting
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
is ChatViewModel.GetCapabilitiesErrorState -> {
|
is ChatViewModel.GetCapabilitiesErrorState -> {
|
||||||
Snackbar.make(binding.root, R.string.nc_common_error_sorry, Snackbar.LENGTH_LONG).show()
|
Snackbar.make(binding.root, R.string.nc_common_error_sorry, Snackbar.LENGTH_LONG).show()
|
||||||
@ -2717,7 +2720,7 @@ class ChatActivity :
|
|||||||
withUrl = urlForChatting,
|
withUrl = urlForChatting,
|
||||||
withCredentials = credentials!!,
|
withCredentials = credentials!!,
|
||||||
withMessageLimit = MESSAGE_PULL_LIMIT,
|
withMessageLimit = MESSAGE_PULL_LIMIT,
|
||||||
roomToken = currentConversation!!.token!!
|
roomToken = currentConversation!!.token
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -65,4 +65,9 @@ interface ChatMessageRepository : LifecycleAwareManager {
|
|||||||
* Gets a individual message.
|
* Gets a individual message.
|
||||||
*/
|
*/
|
||||||
suspend fun getMessage(messageId: Long, bundle: Bundle): Flow<ChatMessage>
|
suspend fun getMessage(messageId: Long, bundle: Bundle): Flow<ChatMessage>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Destroys unused resources.
|
||||||
|
*/
|
||||||
|
fun handleChatOnBackPress()
|
||||||
}
|
}
|
||||||
|
@ -31,6 +31,7 @@ import io.reactivex.schedulers.Schedulers
|
|||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.Job
|
import kotlinx.coroutines.Job
|
||||||
|
import kotlinx.coroutines.cancel
|
||||||
import kotlinx.coroutines.delay
|
import kotlinx.coroutines.delay
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
import kotlinx.coroutines.flow.MutableSharedFlow
|
import kotlinx.coroutines.flow.MutableSharedFlow
|
||||||
@ -197,8 +198,8 @@ class OfflineFirstChatRepository @Inject constructor(
|
|||||||
|
|
||||||
val networkParams = Bundle()
|
val networkParams = Bundle()
|
||||||
|
|
||||||
while (!itIsPaused) {
|
while (true) {
|
||||||
if (!monitor.isOnline.first()) Thread.sleep(500)
|
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)
|
// sync database with server (This is a long blocking call because long polling (lookIntoFuture) is set)
|
||||||
networkParams.putSerializable(BundleKeys.KEY_FIELD_MAP, fieldMap)
|
networkParams.putSerializable(BundleKeys.KEY_FIELD_MAP, fieldMap)
|
||||||
@ -457,11 +458,13 @@ class OfflineFirstChatRepository @Inject constructor(
|
|||||||
// the parent message is always the newest state, no matter how old the system message is.
|
// 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
|
// that's why we can just take the parent, update it in DB and update the UI
|
||||||
messageJson.parentMessage?.let { parentMessageJson ->
|
messageJson.parentMessage?.let { parentMessageJson ->
|
||||||
|
parentMessageJson.message?.let {
|
||||||
val parentMessageEntity = parentMessageJson.asEntity(currentUser.id!!)
|
val parentMessageEntity = parentMessageJson.asEntity(currentUser.id!!)
|
||||||
chatDao.upsertChatMessage(parentMessageEntity)
|
chatDao.upsertChatMessage(parentMessageEntity)
|
||||||
_updateMessageFlow.emit(parentMessageEntity.asModel())
|
_updateMessageFlow.emit(parentMessageEntity.asModel())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
ChatMessage.SystemMessageType.CLEARED_CHAT -> {
|
ChatMessage.SystemMessageType.CLEARED_CHAT -> {
|
||||||
// for lookIntoFuture just deleting everything would be fine.
|
// for lookIntoFuture just deleting everything would be fine.
|
||||||
@ -622,11 +625,16 @@ class OfflineFirstChatRepository @Inject constructor(
|
|||||||
// unused atm
|
// unused atm
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun handleChatOnBackPress() {
|
||||||
|
scope.cancel()
|
||||||
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
val TAG = OfflineFirstChatRepository::class.simpleName
|
val TAG = OfflineFirstChatRepository::class.simpleName
|
||||||
private const val HTTP_CODE_OK: Int = 200
|
private const val HTTP_CODE_OK: Int = 200
|
||||||
private const val HTTP_CODE_NOT_MODIFIED = 304
|
private const val HTTP_CODE_NOT_MODIFIED = 304
|
||||||
private const val HTTP_CODE_PRECONDITION_FAILED = 412
|
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
|
private const val DELAY_TO_ENSURE_MESSAGES_ARE_ADDED: Long = 100
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -626,6 +626,10 @@ class ChatViewModel @Inject constructor(
|
|||||||
_getCapabilitiesViewState.value = GetCapabilitiesStartState
|
_getCapabilitiesViewState.value = GetCapabilitiesStartState
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun handleChatOnBackPress() {
|
||||||
|
chatRepository.handleChatOnBackPress()
|
||||||
|
}
|
||||||
|
|
||||||
suspend fun getMessageById(url: String, conversationModel: ConversationModel, messageId: Long): Flow<ChatMessage> =
|
suspend fun getMessageById(url: String, conversationModel: ConversationModel, messageId: Long): Flow<ChatMessage> =
|
||||||
flow {
|
flow {
|
||||||
val bundle = Bundle()
|
val bundle = Bundle()
|
||||||
@ -634,7 +638,7 @@ class ChatViewModel @Inject constructor(
|
|||||||
BundleKeys.KEY_CREDENTIALS,
|
BundleKeys.KEY_CREDENTIALS,
|
||||||
userProvider.currentUser.blockingGet().getCredentials()
|
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)
|
val message = chatRepository.getMessage(messageId, bundle)
|
||||||
emit(message.first())
|
emit(message.first())
|
||||||
|
@ -373,8 +373,8 @@ class ConversationsListActivity :
|
|||||||
conversationsListViewModel.getRoomsFlow
|
conversationsListViewModel.getRoomsFlow
|
||||||
.onEach { list ->
|
.onEach { list ->
|
||||||
// Update Conversations
|
// Update Conversations
|
||||||
|
conversationItems.clear()
|
||||||
conversationItemsWithHeader.clear()
|
conversationItemsWithHeader.clear()
|
||||||
conversationItems.clear() // fixme remove this
|
|
||||||
for (conversation in list) {
|
for (conversation in list) {
|
||||||
addToConversationItems(conversation)
|
addToConversationItems(conversation)
|
||||||
}
|
}
|
||||||
|
@ -9,6 +9,7 @@
|
|||||||
package com.nextcloud.talk.conversationlist.data.network
|
package com.nextcloud.talk.conversationlist.data.network
|
||||||
|
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
|
import com.nextcloud.talk.chat.data.network.ChatNetworkDataSource
|
||||||
import com.nextcloud.talk.chat.data.network.OfflineFirstChatRepository
|
import com.nextcloud.talk.chat.data.network.OfflineFirstChatRepository
|
||||||
import com.nextcloud.talk.conversationlist.data.OfflineConversationsRepository
|
import com.nextcloud.talk.conversationlist.data.OfflineConversationsRepository
|
||||||
import com.nextcloud.talk.data.database.dao.ConversationsDao
|
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.data.user.model.User
|
||||||
import com.nextcloud.talk.models.domain.ConversationModel
|
import com.nextcloud.talk.models.domain.ConversationModel
|
||||||
import com.nextcloud.talk.utils.database.user.CurrentUserProviderNew
|
import com.nextcloud.talk.utils.database.user.CurrentUserProviderNew
|
||||||
|
import io.reactivex.Observer
|
||||||
import io.reactivex.android.schedulers.AndroidSchedulers
|
import io.reactivex.android.schedulers.AndroidSchedulers
|
||||||
|
import io.reactivex.disposables.Disposable
|
||||||
import io.reactivex.schedulers.Schedulers
|
import io.reactivex.schedulers.Schedulers
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
@ -29,11 +32,13 @@ import kotlinx.coroutines.flow.MutableSharedFlow
|
|||||||
import kotlinx.coroutines.flow.first
|
import kotlinx.coroutines.flow.first
|
||||||
import kotlinx.coroutines.flow.map
|
import kotlinx.coroutines.flow.map
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
import kotlinx.coroutines.runBlocking
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
class OfflineFirstConversationsRepository @Inject constructor(
|
class OfflineFirstConversationsRepository @Inject constructor(
|
||||||
private val dao: ConversationsDao,
|
private val dao: ConversationsDao,
|
||||||
private val network: ConversationsNetworkDataSource,
|
private val network: ConversationsNetworkDataSource,
|
||||||
|
private val chatNetworkDataSource: ChatNetworkDataSource,
|
||||||
private val monitor: NetworkMonitor,
|
private val monitor: NetworkMonitor,
|
||||||
private val currentUserProviderNew: CurrentUserProviderNew
|
private val currentUserProviderNew: CurrentUserProviderNew
|
||||||
) : OfflineConversationsRepository {
|
) : OfflineConversationsRepository {
|
||||||
@ -64,7 +69,34 @@ class OfflineFirstConversationsRepository @Inject constructor(
|
|||||||
scope.launch {
|
scope.launch {
|
||||||
val id = user.id!!
|
val id = user.id!!
|
||||||
val model = getConversation(id, roomToken)
|
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>? {
|
private suspend fun sync(): List<ConversationEntity>? {
|
||||||
@ -107,9 +139,9 @@ class OfflineFirstConversationsRepository @Inject constructor(
|
|||||||
it.map(ConversationEntity::asModel)
|
it.map(ConversationEntity::asModel)
|
||||||
}.first()
|
}.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()
|
val entity = dao.getConversationForUser(accountId, token).first()
|
||||||
return entity.asModel()
|
return entity?.asModel()
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
@ -10,11 +10,11 @@
|
|||||||
package com.nextcloud.talk.dagger.modules
|
package com.nextcloud.talk.dagger.modules
|
||||||
|
|
||||||
import com.nextcloud.talk.api.NcApi
|
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.ChatMessageRepository
|
||||||
import com.nextcloud.talk.chat.data.network.ChatNetworkDataSource
|
import com.nextcloud.talk.chat.data.network.ChatNetworkDataSource
|
||||||
import com.nextcloud.talk.chat.data.network.OfflineFirstChatRepository
|
import com.nextcloud.talk.chat.data.network.OfflineFirstChatRepository
|
||||||
import com.nextcloud.talk.chat.data.network.RetrofitChatNetwork
|
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.ContactsRepository
|
||||||
import com.nextcloud.talk.contacts.ContactsRepositoryImpl
|
import com.nextcloud.talk.contacts.ContactsRepositoryImpl
|
||||||
import com.nextcloud.talk.conversation.repository.ConversationRepository
|
import com.nextcloud.talk.conversation.repository.ConversationRepository
|
||||||
@ -191,10 +191,17 @@ class RepositoryModule {
|
|||||||
fun provideOfflineFirstConversationsRepository(
|
fun provideOfflineFirstConversationsRepository(
|
||||||
dao: ConversationsDao,
|
dao: ConversationsDao,
|
||||||
dataSource: ConversationsNetworkDataSource,
|
dataSource: ConversationsNetworkDataSource,
|
||||||
|
chatNetworkDataSource: ChatNetworkDataSource,
|
||||||
networkMonitor: NetworkMonitor,
|
networkMonitor: NetworkMonitor,
|
||||||
currentUserProviderNew: CurrentUserProviderNew
|
currentUserProviderNew: CurrentUserProviderNew
|
||||||
): OfflineConversationsRepository {
|
): OfflineConversationsRepository {
|
||||||
return OfflineFirstConversationsRepository(dao, dataSource, networkMonitor, currentUserProviderNew)
|
return OfflineFirstConversationsRepository(
|
||||||
|
dao,
|
||||||
|
dataSource,
|
||||||
|
chatNetworkDataSource,
|
||||||
|
networkMonitor,
|
||||||
|
currentUserProviderNew
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Provides
|
@Provides
|
||||||
|
@ -20,7 +20,7 @@ interface ConversationsDao {
|
|||||||
fun getConversationsForUser(accountId: Long): Flow<List<ConversationEntity>>
|
fun getConversationsForUser(accountId: Long): Flow<List<ConversationEntity>>
|
||||||
|
|
||||||
@Query("SELECT * FROM Conversations where accountId = :accountId AND token = :token")
|
@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
|
@Upsert
|
||||||
fun upsertConversations(conversationEntities: List<ConversationEntity>)
|
fun upsertConversations(conversationEntities: List<ConversationEntity>)
|
||||||
|
Loading…
Reference in New Issue
Block a user