Skip to content

Commit deb4c0e

Browse files
committed
load messages faster by passing roomToken to viewModel via constructor to immediately collect flow
+ format code Signed-off-by: Marcel Hibbe <dev@mhibbe.de>
1 parent acbc95d commit deb4c0e

25 files changed

Lines changed: 211 additions & 226 deletions

app/src/main/java/com/nextcloud/talk/chat/ChatActivity.kt

Lines changed: 25 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,7 @@ import com.nextcloud.talk.contextchat.ContextChatViewModel
146146
import com.nextcloud.talk.conversationinfo.ConversationInfoActivity
147147
import com.nextcloud.talk.conversationinfo.viewmodel.ConversationInfoViewModel
148148
import com.nextcloud.talk.conversationlist.ConversationsListActivity
149+
import com.nextcloud.talk.dagger.modules.ViewModelFactoryWithParams
149150
import com.nextcloud.talk.data.network.NetworkMonitor
150151
import com.nextcloud.talk.data.user.model.User
151152
import com.nextcloud.talk.databinding.ActivityChatBinding
@@ -299,7 +300,12 @@ class ChatActivity :
299300
@Inject
300301
lateinit var networkMonitor: NetworkMonitor
301302

302-
lateinit var chatViewModel: ChatViewModel
303+
@Inject
304+
lateinit var chatViewModelFactory: ChatViewModel.ChatViewModelFactory
305+
306+
val chatViewModel: ChatViewModel by viewModels {
307+
ViewModelFactoryWithParams(ChatViewModel::class.java) { chatViewModelFactory.create(roomToken) }
308+
}
303309

304310
lateinit var conversationInfoViewModel: ConversationInfoViewModel
305311
lateinit var contextChatViewModel: ContextChatViewModel
@@ -384,7 +390,12 @@ class ChatActivity :
384390
val disposables = DisposableSet()
385391

386392
var sessionIdAfterRoomJoined: String? = null
387-
lateinit var roomToken: String
393+
394+
val roomToken: String by lazy {
395+
intent.getStringExtra(KEY_ROOM_TOKEN)
396+
?: error("roomToken missing")
397+
}
398+
388399
var conversationThreadId: Long? = null
389400
var openedViaNotification: Boolean = false
390401
var conversationThreadInfo: ThreadInfo? = null
@@ -542,24 +553,24 @@ class ChatActivity :
542553
colorizeNavigationBar()
543554
}
544555

545-
chatViewModel = ViewModelProvider(this, viewModelFactory)[ChatViewModel::class.java]
546-
547556
conversationInfoViewModel = ViewModelProvider(this, viewModelFactory)[ConversationInfoViewModel::class.java]
548557

549558
contextChatViewModel = ViewModelProvider(this, viewModelFactory)[ContextChatViewModel::class.java]
550559

560+
setChatListContent()
561+
551562
lifecycleScope.launch {
552563
currentUserProvider.getCurrentUser()
553564
.onSuccess { user ->
554565
conversationUser = user
555566
handleIntent(intent)
556567
val urlForChatting = ApiUtils.getUrlForChat(chatApiVersion, conversationUser?.baseUrl, roomToken)
557568
val credentials = ApiUtils.getCredentials(conversationUser!!.username, conversationUser!!.token)
569+
// TODO init via viewModel parameters, just like it's done for roomToken
558570
chatViewModel.initData(
559571
user,
560572
credentials!!,
561573
urlForChatting,
562-
roomToken,
563574
conversationThreadId
564575
)
565576

@@ -591,10 +602,12 @@ class ChatActivity :
591602
}
592603
}
593604

605+
// binding.progressBar.visibility = View.VISIBLE
606+
onBackPressedDispatcher.addCallback(this, onBackPressedCallback)
607+
}
594608

595-
609+
private fun setChatListContent() {
596610
binding.messagesListViewCompose.setContent {
597-
598611
val messages by chatViewModel.messages.collectAsStateWithLifecycle(emptyList())
599612

600613
// for future: move transformations to viewmodel
@@ -621,7 +634,6 @@ class ChatActivity :
621634
conversationThreadId = conversationThreadId
622635
)
623636
}
624-
625637
} else {
626638
binding.messagesListViewCompose.visibility = View.GONE
627639
binding.messagesListView.visibility = View.VISIBLE
@@ -638,10 +650,6 @@ class ChatActivity :
638650
}
639651
}
640652
}
641-
642-
643-
// binding.progressBar.visibility = View.VISIBLE
644-
onBackPressedDispatcher.addCallback(this, onBackPressedCallback)
645653
}
646654

647655
private fun getMessageInputFragment(): MessageInputFragment {
@@ -676,8 +684,6 @@ class ChatActivity :
676684
private fun handleIntent(intent: Intent) {
677685
val extras: Bundle? = intent.extras
678686

679-
roomToken = extras?.getString(KEY_ROOM_TOKEN).orEmpty()
680-
681687
conversationThreadId = if (extras?.containsKey(KEY_THREAD_ID) == true) {
682688
extras.getLong(KEY_THREAD_ID)
683689
} else {
@@ -735,8 +741,6 @@ class ChatActivity :
735741
private fun initObservers() {
736742
Log.d(TAG, "initObservers Called")
737743

738-
739-
740744
lifecycleScope.launch {
741745
repeatOnLifecycle(Lifecycle.State.STARTED) {
742746
launch {
@@ -1000,10 +1004,9 @@ class ChatActivity :
10001004
withCredentials = credentials!!,
10011005
withUrl = urlForChatting,
10021006
hasHighPerformanceBackend =
1003-
WebSocketConnectionHelper.getWebSocketInstanceForUser(conversationUser) != null
1007+
WebSocketConnectionHelper.getWebSocketInstanceForUser(conversationUser) != null
10041008
)
10051009
}
1006-
10071010
} else {
10081011
Log.w(
10091012
TAG,
@@ -4116,7 +4119,10 @@ class ChatActivity :
41164119
binding.genericComposeView.apply {
41174120
val shouldDismiss = mutableStateOf(false)
41184121
setContent {
4119-
DateTimeCompose(bundle).GetDateTimeDialog(shouldDismiss, this@ChatActivity)
4122+
DateTimeCompose(
4123+
bundle,
4124+
chatViewModel
4125+
).GetDateTimeDialog(shouldDismiss, this@ChatActivity)
41204126
}
41214127
}
41224128
}

app/src/main/java/com/nextcloud/talk/chat/data/ChatMessageRepository.kt

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@ import com.nextcloud.talk.models.domain.ConversationModel
1616
import com.nextcloud.talk.models.json.chat.ChatMessageJson
1717
import com.nextcloud.talk.models.json.chat.ChatOverallSingleMessage
1818
import kotlinx.coroutines.flow.Flow
19-
import kotlinx.coroutines.flow.MutableStateFlow
2019

2120
@Suppress("TooManyFunctions")
2221
interface ChatMessageRepository : LifecycleAwareManager {
@@ -54,9 +53,7 @@ interface ChatMessageRepository : LifecycleAwareManager {
5453

5554
suspend fun loadInitialMessages(withNetworkParams: Bundle, hasHighPerformanceBackend: Boolean)
5655

57-
suspend fun startMessagePolling(
58-
hasHighPerformanceBackend: Boolean
59-
)
56+
suspend fun startMessagePolling(hasHighPerformanceBackend: Boolean)
6057

6158
/**
6259
* Loads messages from local storage. If the messages are not found, then it

app/src/main/java/com/nextcloud/talk/chat/data/model/ChatMessage.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -144,7 +144,7 @@ data class ChatMessage(
144144

145145
var pinnedUntil: Long? = null,
146146

147-
var avatarUrl: String? = null,
147+
var avatarUrl: String? = null
148148

149149
) : MessageContentType,
150150
MessageContentType.Image {

app/src/main/java/com/nextcloud/talk/chat/data/network/OfflineFirstChatRepository.kt

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,6 @@ import kotlinx.coroutines.Dispatchers
3434
import kotlinx.coroutines.delay
3535
import kotlinx.coroutines.flow.Flow
3636
import kotlinx.coroutines.flow.MutableSharedFlow
37-
import kotlinx.coroutines.flow.MutableStateFlow
3837
import kotlinx.coroutines.flow.catch
3938
import kotlinx.coroutines.flow.distinctUntilChanged
4039
import kotlinx.coroutines.flow.first
@@ -207,9 +206,7 @@ class OfflineFirstChatRepository @Inject constructor(
207206
// handleMessagesFromDb(newestMessageIdFromDb)
208207
}
209208

210-
override suspend fun startMessagePolling(
211-
hasHighPerformanceBackend: Boolean
212-
) {
209+
override suspend fun startMessagePolling(hasHighPerformanceBackend: Boolean) {
213210
if (hasHighPerformanceBackend) {
214211
initInsuranceRequests()
215212
} else {

app/src/main/java/com/nextcloud/talk/chat/viewmodels/ChatViewModel.kt

Lines changed: 37 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -52,19 +52,26 @@ import com.nextcloud.talk.utils.ApiUtils
5252
import com.nextcloud.talk.utils.ParticipantPermissions
5353
import com.nextcloud.talk.utils.UserIdUtils
5454
import com.nextcloud.talk.utils.bundle.BundleKeys
55+
import com.nextcloud.talk.utils.database.user.CurrentUserProvider
5556
import com.nextcloud.talk.utils.preferences.AppPreferences
57+
import dagger.assisted.Assisted
58+
import dagger.assisted.AssistedFactory
59+
import dagger.assisted.AssistedInject
5660
import io.reactivex.Observer
5761
import io.reactivex.android.schedulers.AndroidSchedulers
5862
import io.reactivex.disposables.Disposable
5963
import io.reactivex.schedulers.Schedulers
64+
import kotlinx.coroutines.ExperimentalCoroutinesApi
6065
import kotlinx.coroutines.flow.Flow
6166
import kotlinx.coroutines.flow.MutableSharedFlow
6267
import kotlinx.coroutines.flow.MutableStateFlow
6368
import kotlinx.coroutines.flow.SharingStarted
6469
import kotlinx.coroutines.flow.StateFlow
6570
import kotlinx.coroutines.flow.asSharedFlow
6671
import kotlinx.coroutines.flow.catch
72+
import kotlinx.coroutines.flow.filterNotNull
6773
import kotlinx.coroutines.flow.first
74+
import kotlinx.coroutines.flow.flatMapLatest
6875
import kotlinx.coroutines.flow.flow
6976
import kotlinx.coroutines.flow.map
7077
import kotlinx.coroutines.flow.onEach
@@ -74,7 +81,7 @@ import java.io.File
7481
import javax.inject.Inject
7582

7683
@Suppress("TooManyFunctions", "LongParameterList")
77-
class ChatViewModel @Inject constructor(
84+
class ChatViewModel @AssistedInject constructor(
7885
// should be removed here. Use it via RetrofitChatNetwork
7986
private val appPreferences: AppPreferences,
8087
private val chatNetworkDataSource: ChatNetworkDataSource,
@@ -83,7 +90,9 @@ class ChatViewModel @Inject constructor(
8390
private val conversationRepository: OfflineConversationsRepository,
8491
private val reactionsRepository: ReactionsRepository,
8592
private val mediaRecorderManager: MediaRecorderManager,
86-
private val audioFocusRequestManager: AudioFocusRequestManager
93+
private val audioFocusRequestManager: AudioFocusRequestManager,
94+
private val currentUserProvider: CurrentUserProvider,
95+
@Assisted private val chatRoomToken: String
8796
) : ViewModel(),
8897
DefaultLifecycleObserver {
8998

@@ -107,8 +116,8 @@ class ChatViewModel @Inject constructor(
107116
val disposableSet = mutableSetOf<Disposable>()
108117
var mediaPlayerDuration = mediaPlayerManager.mediaPlayerDuration
109118
val mediaPlayerPosition = mediaPlayerManager.mediaPlayerPosition
110-
var internalConversationId: String = ""
111-
var chatRoomToken: String = ""
119+
120+
private val internalConversationId = MutableStateFlow<String?>(null)
112121
var messageDraft: MessageDraft = MessageDraft()
113122
lateinit var participantPermissions: ParticipantPermissions
114123

@@ -296,10 +305,22 @@ class ChatViewModel @Inject constructor(
296305
val reactionDeletedViewState: LiveData<ViewState>
297306
get() = _reactionDeletedViewState
298307

308+
init {
309+
viewModelScope.launch {
310+
currentUserProvider.getCurrentUser()
311+
.onSuccess { user ->
312+
internalConversationId.value = currentUser.id.toString() + "@" + chatRoomToken
313+
}
314+
}
315+
}
316+
317+
@OptIn(ExperimentalCoroutinesApi::class)
299318
val messages: StateFlow<List<ChatMessage>> =
300-
// TODO: make sure internalConversationId is initialized! for now it's hardcoded to chat with "marcel2"
301-
// chatRepository.observeMessages(internalConversationId)
302-
chatRepository.observeMessages("1@3853979093")
319+
internalConversationId
320+
.filterNotNull()
321+
.flatMapLatest { conversationId ->
322+
chatRepository.observeMessages(conversationId)
323+
}
303324
.map { entities -> entities.map(ChatMessageEntity::asModel) }
304325
.onEach { messages ->
305326
messages.forEach { it.avatarUrl = getAvatarUrl(it) }
@@ -310,8 +331,8 @@ class ChatViewModel @Inject constructor(
310331
initialValue = emptyList()
311332
)
312333

313-
fun getAvatarUrl(message: ChatMessage): String {
314-
return if (this::currentUser.isInitialized) {
334+
fun getAvatarUrl(message: ChatMessage): String =
335+
if (this::currentUser.isInitialized) {
315336
ApiUtils.getUrlForAvatar(
316337
currentUser.baseUrl,
317338
message.actorId,
@@ -320,21 +341,17 @@ class ChatViewModel @Inject constructor(
320341
} else {
321342
""
322343
}
323-
}
324344

325-
fun initData(user: User, credentials: String, urlForChatting: String, roomToken: String, threadId: Long?) {
345+
fun initData(user: User, credentials: String, urlForChatting: String, threadId: Long?) {
326346
currentUser = user
327347

328348
chatRepository.initData(
329349
user,
330350
credentials,
331351
urlForChatting,
332-
roomToken,
352+
chatRoomToken,
333353
threadId
334354
)
335-
336-
internalConversationId = currentUser.id.toString() + "@" + roomToken
337-
chatRoomToken = roomToken
338355
}
339356

340357
fun updateConversation(currentConversation: ConversationModel) {
@@ -1163,4 +1180,9 @@ class ChatViewModel @Inject constructor(
11631180
object Ready : ChatEvent()
11641181
data class Error(val throwable: Throwable) : ChatEvent()
11651182
}
1183+
1184+
@AssistedFactory
1185+
interface ChatViewModelFactory {
1186+
fun create(roomToken: String): ChatViewModel
1187+
}
11661188
}

app/src/main/java/com/nextcloud/talk/contextchat/ContextChatView.kt

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,6 @@ import com.nextcloud.talk.models.json.chat.ChatMessageJson
5454
import com.nextcloud.talk.ui.chat.GetNewChatView
5555
import com.nextcloud.talk.ui.theme.ViewThemeUtils
5656
import com.nextcloud.talk.utils.preview.ComposePreviewUtils
57-
import javax.inject.Inject
5857

5958
@Composable
6059
fun ContextChatView(

app/src/main/java/com/nextcloud/talk/contextchat/ContextChatViewModel.kt

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@ import androidx.lifecycle.viewModelScope
1212
import autodagger.AutoInjector
1313
import com.nextcloud.talk.application.NextcloudTalkApplication
1414
import com.nextcloud.talk.chat.data.network.ChatNetworkDataSource
15-
import com.nextcloud.talk.chat.viewmodels.ChatViewModel
1615
import com.nextcloud.talk.models.json.chat.ChatMessageJson
1716
import com.nextcloud.talk.users.UserManager
1817
import kotlinx.coroutines.flow.MutableStateFlow
@@ -24,9 +23,6 @@ import javax.inject.Inject
2423
class ContextChatViewModel @Inject constructor(private val chatNetworkDataSource: ChatNetworkDataSource) :
2524
ViewModel() {
2625

27-
@Inject
28-
lateinit var chatViewModel: ChatViewModel
29-
3026
@Inject
3127
lateinit var userManager: UserManager
3228

0 commit comments

Comments
 (0)