Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fetch messages in background #4475

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,8 @@ interface ChatMessageRepository : LifecycleAwareManager {
withNetworkParams: Bundle
): Job

suspend fun updateRoomMessages(internalConversationId: String, limit: Int)

/**
* Long polls the server for any updates to the chat, if found, it synchronizes
* the database with the server and emits the new messages to [messageFlow],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ package com.nextcloud.talk.chat.data.network

import android.os.Bundle
import android.util.Log
import androidx.core.os.bundleOf
import com.nextcloud.talk.chat.ChatActivity
import com.nextcloud.talk.chat.data.ChatMessageRepository
import com.nextcloud.talk.chat.data.model.ChatMessage
Expand Down Expand Up @@ -257,6 +258,23 @@ class OfflineFirstChatRepository @Inject constructor(
updateUiForLastCommonRead()
}

override suspend fun updateRoomMessages(internalConversationId: String, limit: Int) {
val lastKnown = chatDao.getNewestMessageId(internalConversationId)

Log.d(TAG, "---- updateRoomMessages ------------ with lastKnown: $lastKnown")
val fieldMap = getFieldMap(
lookIntoFuture = true,
timeout = 0,
includeLastKnown = false,
setReadMarker = true,
lastKnown = lastKnown.toInt()
)

val networkParams = bundleOf()
networkParams.putSerializable(BundleKeys.KEY_FIELD_MAP, fieldMap)
sync(networkParams)
}

override fun initMessagePolling(initialMessageId: Long): Job =
scope.launch {
Log.d(TAG, "---- initMessagePolling ------------")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -270,7 +270,7 @@ class ConversationsListActivity :
if (adapter == null) {
adapter = FlexibleAdapter(conversationItems, this, true)
} else {
binding.loadingContent?.visibility = View.GONE
binding.loadingContent.visibility = View.GONE
}
adapter!!.addListener(this)
prepareViews()
Expand Down Expand Up @@ -387,6 +387,9 @@ class ConversationsListActivity :
lifecycleScope.launch {
conversationsListViewModel.getRoomsFlow
.onEach { list ->
// Refreshes conversation messages in the background asynchronously
conversationsListViewModel.updateRoomMessages(credentials!!, conversationItems, list)

// Update Conversations
conversationItems.clear()
conversationItemsWithHeader.clear()
Expand Down Expand Up @@ -688,7 +691,7 @@ class ConversationsListActivity :
if (!hasFilterEnabled()) filterableConversationItems = searchableConversationItems
adapter!!.updateDataSet(filterableConversationItems, false)
adapter!!.showAllHeaders()
binding.swipeRefreshLayoutView?.isEnabled = false
binding.swipeRefreshLayoutView.isEnabled = false
searchBehaviorSubject.onNext(true)
return true
}
Expand All @@ -702,9 +705,9 @@ class ConversationsListActivity :
// cancel any pending searches
searchHelper!!.cancelSearch()
}
binding.swipeRefreshLayoutView?.isRefreshing = false
binding.swipeRefreshLayoutView.isRefreshing = false
searchBehaviorSubject.onNext(false)
binding.swipeRefreshLayoutView?.isEnabled = true
binding.swipeRefreshLayoutView.isEnabled = true
searchView!!.onActionViewCollapsed()

binding.conversationListAppbar.stateListAnimator = AnimatorInflater.loadStateListAnimator(
Expand All @@ -717,7 +720,7 @@ class ConversationsListActivity :
viewThemeUtils.platform.resetStatusBar(this@ConversationsListActivity)
}

val layoutManager = binding.recyclerView?.layoutManager as SmoothScrollLinearLayoutManager?
val layoutManager = binding.recyclerView.layoutManager as SmoothScrollLinearLayoutManager?
layoutManager?.scrollToPositionWithOffset(0, 0)
return true
}
Expand Down Expand Up @@ -810,18 +813,18 @@ class ConversationsListActivity :

private fun initOverallLayout(isConversationListNotEmpty: Boolean) {
if (isConversationListNotEmpty) {
if (binding.emptyLayout?.visibility != View.GONE) {
binding.emptyLayout?.visibility = View.GONE
if (binding.emptyLayout.visibility != View.GONE) {
binding.emptyLayout.visibility = View.GONE
}
if (binding.swipeRefreshLayoutView?.visibility != View.VISIBLE) {
binding.swipeRefreshLayoutView?.visibility = View.VISIBLE
if (binding.swipeRefreshLayoutView.visibility != View.VISIBLE) {
binding.swipeRefreshLayoutView.visibility = View.VISIBLE
}
} else {
if (binding.emptyLayout?.visibility != View.VISIBLE) {
binding.emptyLayout?.visibility = View.VISIBLE
if (binding.emptyLayout.visibility != View.VISIBLE) {
binding.emptyLayout.visibility = View.VISIBLE
}
if (binding.swipeRefreshLayoutView?.visibility != View.GONE) {
binding.swipeRefreshLayoutView?.visibility = View.GONE
if (binding.swipeRefreshLayoutView.visibility != View.GONE) {
binding.swipeRefreshLayoutView.visibility = View.GONE
}
}
}
Expand Down Expand Up @@ -1001,24 +1004,24 @@ class ConversationsListActivity :
}
}
})
binding.recyclerView?.setOnTouchListener { v: View, _: MotionEvent? ->
binding.recyclerView.setOnTouchListener { v: View, _: MotionEvent? ->
if (!isDestroyed) {
val imm = getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
imm.hideSoftInputFromWindow(v.windowToken, 0)
}
false
}
binding.swipeRefreshLayoutView?.setOnRefreshListener {
binding.swipeRefreshLayoutView.setOnRefreshListener {
fetchRooms()
fetchPendingInvitations()
}
binding.swipeRefreshLayoutView?.let { viewThemeUtils.androidx.themeSwipeRefreshLayout(it) }
binding.emptyLayout?.setOnClickListener { showNewConversationsScreen() }
binding.floatingActionButton?.setOnClickListener {
binding.swipeRefreshLayoutView.let { viewThemeUtils.androidx.themeSwipeRefreshLayout(it) }
binding.emptyLayout.setOnClickListener { showNewConversationsScreen() }
binding.floatingActionButton.setOnClickListener {
run(context)
showNewConversationsScreen()
}
binding.floatingActionButton?.let { viewThemeUtils.material.themeFAB(it) }
binding.floatingActionButton.let { viewThemeUtils.material.themeFAB(it) }

binding.switchAccountButton.setOnClickListener {
if (resources != null && resources!!.getBoolean(R.bool.multiaccount_support)) {
Expand Down Expand Up @@ -1190,7 +1193,7 @@ class ConversationsListActivity :

@SuppressLint("CheckResult") // handled by helper
private fun startMessageSearch(search: String?) {
binding.swipeRefreshLayoutView?.isRefreshing = true
binding.swipeRefreshLayoutView.isRefreshing = true
searchHelper?.startMessageSearch(search!!)
?.subscribeOn(Schedulers.io())
?.observeOn(AndroidSchedulers.mainThread())
Expand Down Expand Up @@ -1435,8 +1438,8 @@ class ConversationsListActivity :
filesToShare?.forEach {
UploadAndShareFilesWorker.upload(
it,
selectedConversation!!.token!!,
selectedConversation!!.displayName!!,
selectedConversation!!.token,
selectedConversation!!.displayName,
null
)
}
Expand Down Expand Up @@ -1909,15 +1912,15 @@ class ConversationsListActivity :
}
// add unified search result at the end of the list
adapter!!.addItems(adapter!!.mainItemCount + adapter!!.scrollableHeaders.size, adapterItems)
binding.recyclerView?.scrollToPosition(0)
binding.recyclerView.scrollToPosition(0)
}
}
binding.swipeRefreshLayoutView?.isRefreshing = false
binding.swipeRefreshLayoutView.isRefreshing = false
}

private fun onMessageSearchError(throwable: Throwable) {
handleHttpExceptions(throwable)
binding.swipeRefreshLayoutView?.isRefreshing = false
binding.swipeRefreshLayoutView.isRefreshing = false
showErrorDialog()
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,20 +10,29 @@ import android.util.Log
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.nextcloud.talk.adapters.items.ConversationItem
import com.nextcloud.talk.chat.data.ChatMessageRepository
import com.nextcloud.talk.conversationlist.data.OfflineConversationsRepository
import com.nextcloud.talk.invitation.data.InvitationsModel
import com.nextcloud.talk.invitation.data.InvitationsRepository
import com.nextcloud.talk.models.domain.ConversationModel
import com.nextcloud.talk.users.UserManager
import com.nextcloud.talk.utils.ApiUtils
import eu.davidea.flexibleadapter.items.AbstractFlexibleItem
import io.reactivex.Observer
import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.disposables.Disposable
import io.reactivex.schedulers.Schedulers
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.catch
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.launch
import javax.inject.Inject

class ConversationsListViewModel @Inject constructor(
private val repository: OfflineConversationsRepository,
private val chatRepository: ChatMessageRepository,
var userManager: UserManager
) :
ViewModel() {
Expand Down Expand Up @@ -84,6 +93,46 @@ class ConversationsListViewModel @Inject constructor(
repository.getRooms()
}

fun updateRoomMessages(
credentials: String,
oldList: MutableList<AbstractFlexibleItem<*>>,
list: List<ConversationModel>
) {
val previous = oldList.associate {
(it as ConversationItem)
val unreadMessages = it.model.unreadMessages
val roomToken = it.model.token
Pair(roomToken, unreadMessages)
}

val current = list.associateWith { model ->
val unreadMessages = model.unreadMessages
unreadMessages
}

val result = current.map { (model, unreadMessages) ->
val previousUnreadMessages = previous[model.token] // Check if this conversation exists in last list
previousUnreadMessages?.let {
Pair(model, unreadMessages - previousUnreadMessages)
}
}.filterNotNull()
val baseUrl = userManager.currentUser.blockingGet().baseUrl!!

viewModelScope.launch(Dispatchers.IO) {
for (pair in result) {
if (pair.second > 0) {
updateRoomMessage(pair.first, pair.second, credentials, baseUrl)
}
}
}
}

private suspend fun updateRoomMessage(model: ConversationModel, limit: Int, credentials: String, baseUrl: String) {
val urlForChatting = ApiUtils.getUrlForChat(1, baseUrl, model.token) // FIXME v1?
chatRepository.setData(model, credentials, urlForChatting)
chatRepository.updateRoomMessages(model.internalId, limit)
}

inner class FederatedInvitationsObserver : Observer<InvitationsModel> {
override fun onSubscribe(d: Disposable) {
// unused atm
Expand Down
Loading