From 5fee401e856ec3193dd2a4806556c469e9755350 Mon Sep 17 00:00:00 2001 From: Boris Safonov Date: Tue, 17 Dec 2024 16:55:31 +0200 Subject: [PATCH] finished JumpToPlayingAudioButton --- .../android/di/accountScoped/MessageModule.kt | 6 + .../home/conversations/ConversationScreen.kt | 49 ++++---- .../messages/ConversationMessagesViewModel.kt | 41 +++--- .../messages/ConversationMessagesViewState.kt | 6 +- ...onversationMessagesViewModelArrangement.kt | 12 +- .../ConversationMessagesViewModelTest.kt | 118 ++++++++++++++++-- kalium | 2 +- 7 files changed, 174 insertions(+), 60 deletions(-) diff --git a/app/src/main/kotlin/com/wire/android/di/accountScoped/MessageModule.kt b/app/src/main/kotlin/com/wire/android/di/accountScoped/MessageModule.kt index ad72e7b2fea..506dcbc30f5 100644 --- a/app/src/main/kotlin/com/wire/android/di/accountScoped/MessageModule.kt +++ b/app/src/main/kotlin/com/wire/android/di/accountScoped/MessageModule.kt @@ -34,6 +34,7 @@ import com.wire.kalium.logic.feature.message.GetNotificationsUseCase import com.wire.kalium.logic.feature.message.GetPaginatedFlowOfMessagesByConversationUseCase import com.wire.kalium.logic.feature.message.GetPaginatedFlowOfMessagesBySearchQueryAndConversationIdUseCase import com.wire.kalium.logic.feature.message.GetSearchedConversationMessagePositionUseCase +import com.wire.kalium.logic.feature.message.GetSenderNameByMessageIdUseCase import com.wire.kalium.logic.feature.message.MarkMessagesAsNotifiedUseCase import com.wire.kalium.logic.feature.message.MessageScope import com.wire.kalium.logic.feature.message.ObserveMessageReactionsUseCase @@ -216,4 +217,9 @@ class MessageModule { @Provides fun provideRemoveMessageDraftUseCase(messageScope: MessageScope): RemoveMessageDraftUseCase = messageScope.removeMessageDraftUseCase + + @ViewModelScoped + @Provides + fun provideGetSenderNameByMessageIdUseCase(messageScope: MessageScope): GetSenderNameByMessageIdUseCase = + messageScope.getSenderNameByMessageId } diff --git a/app/src/main/kotlin/com/wire/android/ui/home/conversations/ConversationScreen.kt b/app/src/main/kotlin/com/wire/android/ui/home/conversations/ConversationScreen.kt index 560914f6d6d..d624f997f1f 100644 --- a/app/src/main/kotlin/com/wire/android/ui/home/conversations/ConversationScreen.kt +++ b/app/src/main/kotlin/com/wire/android/ui/home/conversations/ConversationScreen.kt @@ -33,14 +33,13 @@ import androidx.compose.foundation.layout.BoxScope import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.Row -import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.consumeWindowInsets import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size -import androidx.compose.foundation.layout.width +import androidx.compose.foundation.layout.wrapContentWidth import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.LazyListState import androidx.compose.foundation.shape.CircleShape @@ -72,6 +71,7 @@ import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalUriHandler import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.style.TextOverflow import androidx.hilt.navigation.compose.hiltViewModel import androidx.paging.PagingData import androidx.paging.compose.LazyPagingItems @@ -170,7 +170,6 @@ import com.wire.android.ui.home.messagecomposer.state.rememberMessageComposerSta import com.wire.android.ui.legalhold.dialog.subject.LegalHoldSubjectMessageDialog import com.wire.android.ui.theme.WireTheme import com.wire.android.ui.theme.wireColorScheme -import com.wire.android.ui.theme.wireDimensions import com.wire.android.ui.theme.wireTypography import com.wire.android.util.DateAndTimeParsers import com.wire.android.util.normalizeLink @@ -937,7 +936,6 @@ private fun ConversationScreen( selectedMessageId = conversationMessagesViewState.searchedMessageId, messageComposerStateHolder = messageComposerStateHolder, messages = conversationMessagesViewState.messages, - playingAudiMessage = conversationMessagesViewState.playingAudiMessage, onSendMessage = onSendMessage, onPingOptionClicked = onPingOptionClicked, onImagesPicked = onImagesPicked, @@ -1044,7 +1042,6 @@ private fun ConversationScreenContent( onNavigateToReplyOriginalMessage: (UIMessage) -> Unit, openDrawingCanvas: () -> Unit, currentTimeInMillisFlow: Flow = flow {}, - playingAudiMessage: PlayingAudiMessage?, ) { val lazyPagingMessages = messages.collectAsLazyPagingItems() @@ -1084,8 +1081,7 @@ private fun ConversationScreenContent( conversationDetailsData = conversationDetailsData, selectedMessageId = selectedMessageId, interactionAvailability = messageComposerStateHolder.messageComposerViewState.value.interactionAvailability, - currentTimeInMillisFlow = currentTimeInMillisFlow, - playingAudiMessage = playingAudiMessage + currentTimeInMillisFlow = currentTimeInMillisFlow ) }, onChangeSelfDeletionClicked = onChangeSelfDeletionClicked, @@ -1151,8 +1147,7 @@ fun MessageList( interactionAvailability: InteractionAvailability, clickActions: MessageClickActions.Content, modifier: Modifier = Modifier, - currentTimeInMillisFlow: Flow = flow { }, - playingAudiMessage: PlayingAudiMessage? + currentTimeInMillisFlow: Flow = flow { } ) { val prevItemCount = remember { mutableStateOf(lazyPagingMessages.itemCount) } val readLastMessageAtStartTriggered = remember { mutableStateOf(false) } @@ -1281,9 +1276,9 @@ fun MessageList( } } JumpToPlayingAudioButton( - lazyPagingMessages = lazyPagingMessages, lazyListState = lazyListState, - playingAudiMessage = playingAudiMessage + lazyPagingMessages = lazyPagingMessages, + playingAudiMessage = audioMessagesState.playingAudiMessage ) JumpToLastMessageButton(lazyListState = lazyListState) } @@ -1423,8 +1418,8 @@ fun JumpToLastMessageButton( fun BoxScope.JumpToPlayingAudioButton( lazyListState: LazyListState, playingAudiMessage: PlayingAudiMessage?, - modifier: Modifier = Modifier, lazyPagingMessages: LazyPagingItems, + modifier: Modifier = Modifier, coroutineScope: CoroutineScope = rememberCoroutineScope() ) { val indexOfPlayedMessage = playingAudiMessage?.let { @@ -1434,43 +1429,45 @@ fun BoxScope.JumpToPlayingAudioButton( if (indexOfPlayedMessage < 0) return - // todo cyka try to remember indexes - val visible = playingAudiMessage?.let { - val firstVisibleIndex = lazyListState.firstVisibleItemIndex - val lastVisibleIndex = firstVisibleIndex + lazyListState.layoutInfo.visibleItemsInfo.size - indexOfPlayedMessage in firstVisibleIndex..lastVisibleIndex - } ?: false + val firstVisibleIndex = lazyListState.firstVisibleItemIndex + val lastVisibleIndex = lazyListState.layoutInfo.visibleItemsInfo.lastOrNull()?.index ?: firstVisibleIndex - if (!visible) return + if (indexOfPlayedMessage in firstVisibleIndex..lastVisibleIndex) return Row( + verticalAlignment = Alignment.CenterVertically, modifier = modifier + .wrapContentWidth() .align(Alignment.TopCenter) + .padding(all = dimensions().spacing8x) .clickable { coroutineScope.launch { lazyListState.animateScrollToItem(indexOfPlayedMessage) } } - .padding(horizontal = dimensions().spacing16x, vertical = dimensions().spacing8x) .background( color = colorsScheme().secondaryText, - shape = RoundedCornerShape(MaterialTheme.wireDimensions.buttonCornerSize) + shape = RoundedCornerShape(dimensions().corner16x) ) + .padding(horizontal = dimensions().spacing16x, vertical = dimensions().spacing8x) ) { Icon( - modifier = Modifier.weight(1f), + modifier = Modifier.size(dimensions().systemMessageIconSize), painter = painterResource(id = R.drawable.ic_play), contentDescription = null, tint = MaterialTheme.wireColorScheme.onPrimaryButtonEnabled ) - Spacer(Modifier.width(dimensions().spacing8x)) Text( + modifier = Modifier + .padding(horizontal = dimensions().spacing8x) + .weight(1f, fill = false), text = playingAudiMessage!!.authorName, + maxLines = 1, + overflow = TextOverflow.Ellipsis, color = colorsScheme().onPrimaryButtonEnabled, style = MaterialTheme.wireTypography.body04, ) - Spacer(Modifier.width(dimensions().spacing8x)) Text( - modifier = Modifier.weight(1f), + modifier = Modifier, text = DateAndTimeParsers.audioMessageTime(playingAudiMessage.currentTimeMs.toLong()), color = colorsScheme().onPrimaryButtonEnabled, - style = MaterialTheme.wireTypography.body04, + style = MaterialTheme.wireTypography.label03, ) } } diff --git a/app/src/main/kotlin/com/wire/android/ui/home/conversations/messages/ConversationMessagesViewModel.kt b/app/src/main/kotlin/com/wire/android/ui/home/conversations/messages/ConversationMessagesViewModel.kt index b0c88f048f5..f7d59bedb2e 100644 --- a/app/src/main/kotlin/com/wire/android/ui/home/conversations/messages/ConversationMessagesViewModel.kt +++ b/app/src/main/kotlin/com/wire/android/ui/home/conversations/messages/ConversationMessagesViewModel.kt @@ -65,6 +65,7 @@ import com.wire.kalium.logic.feature.conversation.ObserveConversationDetailsUseC import com.wire.kalium.logic.feature.message.DeleteMessageUseCase import com.wire.kalium.logic.feature.message.GetMessageByIdUseCase import com.wire.kalium.logic.feature.message.GetSearchedConversationMessagePositionUseCase +import com.wire.kalium.logic.feature.message.GetSenderNameByMessageIdUseCase import com.wire.kalium.logic.feature.message.ToggleReactionUseCase import com.wire.kalium.logic.feature.sessionreset.ResetSessionResult import com.wire.kalium.logic.feature.sessionreset.ResetSessionUseCase @@ -80,7 +81,6 @@ import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.asSharedFlow import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.distinctUntilChanged -import kotlinx.coroutines.flow.filterIsInstance import kotlinx.coroutines.flow.flow import kotlinx.coroutines.flow.flowOn import kotlinx.coroutines.flow.map @@ -110,7 +110,8 @@ class ConversationMessagesViewModel @Inject constructor( private val getConversationUnreadEventsCount: GetConversationUnreadEventsCountUseCase, private val clearUsersTypingEvents: ClearUsersTypingEventsUseCase, private val getSearchedConversationMessagePosition: GetSearchedConversationMessagePositionUseCase, - private val deleteMessage: DeleteMessageUseCase + private val deleteMessage: DeleteMessageUseCase, + private val getSenderNameByMessageId: GetSenderNameByMessageIdUseCase ) : SavedStateViewModel(savedStateHandle) { private val conversationNavArgs: ConversationNavArgs = savedStateHandle.navArgs() @@ -195,36 +196,34 @@ class ConversationMessagesViewModel @Inject constructor( val playingMessageData = observableAudioMessagesState .map { audioMessageStates -> audioMessageStates.firstNotNullOfOrNull { (messageId, audioState) -> - if (audioState.audioMediaPlayingState == AudioMediaPlayingState.Playing) messageId - else null + if (audioState.audioMediaPlayingState == AudioMediaPlayingState.Playing) messageId else null } - }.distinctUntilChanged() - .map { messageId -> messageId?.let { getMessageByIdUseCase(conversationId, it) } } - .filterIsInstance() - .map { it?.message } + } + .distinctUntilChanged() + .map { messageId -> + val senderNameResult = messageId?.let { getSenderNameByMessageId(conversationId, it) } + val senderName = if (senderNameResult is GetSenderNameByMessageIdUseCase.Result.Success) senderNameResult.name + else null + + messageId to senderName + } viewModelScope.launch { combine( observableAudioMessagesState, conversationAudioMessagePlayer.audioSpeed, playingMessageData - ) { audioMessageStates, audioSpeed, playingMessage -> - val audioMessagesState = AudioMessagesState(audioMessageStates.toPersistentMap(), audioSpeed) - val playingAudiMessage = playingMessage?.let { + ) { audioMessageStates, audioSpeed, (playingMessageId, playingMessageSenderName) -> + val playingAudiMessage = playingMessageId?.let { PlayingAudiMessage( - messageId = playingMessage.id, - authorName = playingMessage.sender?.name ?: "", - currentTimeMs = audioMessageStates[playingMessage.id]?.currentPositionInMs ?: 0 + messageId = playingMessageId, + authorName = playingMessageSenderName.orEmpty(), + currentTimeMs = audioMessageStates[playingMessageId]?.currentPositionInMs ?: 0 ) } - audioMessagesState to playingAudiMessage + AudioMessagesState(audioMessageStates.toPersistentMap(), audioSpeed, playingAudiMessage) } - .collect { (audioMessagesState, playingAudiMessage) -> - conversationViewState = conversationViewState.copy( - audioMessagesState = audioMessagesState, - playingAudiMessage = playingAudiMessage - ) - } + .collect { conversationViewState = conversationViewState.copy(audioMessagesState = it) } } } diff --git a/app/src/main/kotlin/com/wire/android/ui/home/conversations/messages/ConversationMessagesViewState.kt b/app/src/main/kotlin/com/wire/android/ui/home/conversations/messages/ConversationMessagesViewState.kt index 84fd447120b..119139691d5 100644 --- a/app/src/main/kotlin/com/wire/android/ui/home/conversations/messages/ConversationMessagesViewState.kt +++ b/app/src/main/kotlin/com/wire/android/ui/home/conversations/messages/ConversationMessagesViewState.kt @@ -37,13 +37,13 @@ data class ConversationMessagesViewState( val downloadedAssetDialogState: DownloadedAssetDialogVisibilityState = DownloadedAssetDialogVisibilityState.Hidden, val audioMessagesState: AudioMessagesState = AudioMessagesState(), val assetStatuses: PersistentMap = persistentMapOf(), - val searchedMessageId: String? = null, - val playingAudiMessage: PlayingAudiMessage? = null + val searchedMessageId: String? = null ) data class AudioMessagesState( val audioStates: PersistentMap = persistentMapOf(), - val audioSpeed: AudioSpeed = AudioSpeed.NORMAL + val audioSpeed: AudioSpeed = AudioSpeed.NORMAL, + val playingAudiMessage: PlayingAudiMessage? = null ) data class PlayingAudiMessage( diff --git a/app/src/test/kotlin/com/wire/android/ui/home/conversations/messages/ConversationMessagesViewModelArrangement.kt b/app/src/test/kotlin/com/wire/android/ui/home/conversations/messages/ConversationMessagesViewModelArrangement.kt index 3a5994d572d..01f5cb4d252 100644 --- a/app/src/test/kotlin/com/wire/android/ui/home/conversations/messages/ConversationMessagesViewModelArrangement.kt +++ b/app/src/test/kotlin/com/wire/android/ui/home/conversations/messages/ConversationMessagesViewModelArrangement.kt @@ -47,6 +47,7 @@ import com.wire.kalium.logic.feature.conversation.ObserveConversationDetailsUseC import com.wire.kalium.logic.feature.message.DeleteMessageUseCase import com.wire.kalium.logic.feature.message.GetMessageByIdUseCase import com.wire.kalium.logic.feature.message.GetSearchedConversationMessagePositionUseCase +import com.wire.kalium.logic.feature.message.GetSenderNameByMessageIdUseCase import com.wire.kalium.logic.feature.message.ToggleReactionUseCase import com.wire.kalium.logic.feature.sessionreset.ResetSessionResult import com.wire.kalium.logic.feature.sessionreset.ResetSessionUseCase @@ -116,6 +117,9 @@ class ConversationMessagesViewModelArrangement { @MockK lateinit var deleteMessage: DeleteMessageUseCase + @MockK + lateinit var getSenderNameByMessageId: GetSenderNameByMessageIdUseCase + private val viewModel: ConversationMessagesViewModel by lazy { ConversationMessagesViewModel( savedStateHandle, @@ -133,7 +137,8 @@ class ConversationMessagesViewModelArrangement { getConversationUnreadEventsCount, clearUsersTypingEvents, getSearchedConversationMessagePosition, - deleteMessage + deleteMessage, + getSenderNameByMessageId ) } @@ -158,6 +163,7 @@ class ConversationMessagesViewModelArrangement { coEvery { conversationAudioMessagePlayer.audioSpeed } returns flowOf(AudioSpeed.NORMAL) coEvery { conversationAudioMessagePlayer.fetchWavesMask(any(), any()) } returns Unit + coEvery { getSenderNameByMessageId(any(), any()) } returns GetSenderNameByMessageIdUseCase.Result.Success("User Name") } fun withSuccessfulViewModelInit() = apply { @@ -231,5 +237,9 @@ class ConversationMessagesViewModelArrangement { return this } + fun withGetSenderNameByMessageId(result: GetSenderNameByMessageIdUseCase.Result) = apply { + coEvery { getSenderNameByMessageId(any(), any()) } returns result + } + fun arrange() = this to viewModel } diff --git a/app/src/test/kotlin/com/wire/android/ui/home/conversations/messages/ConversationMessagesViewModelTest.kt b/app/src/test/kotlin/com/wire/android/ui/home/conversations/messages/ConversationMessagesViewModelTest.kt index d854a692c67..18f19efd891 100644 --- a/app/src/test/kotlin/com/wire/android/ui/home/conversations/messages/ConversationMessagesViewModelTest.kt +++ b/app/src/test/kotlin/com/wire/android/ui/home/conversations/messages/ConversationMessagesViewModelTest.kt @@ -26,17 +26,23 @@ import com.wire.android.config.CoroutineTestExtension import com.wire.android.config.NavigationTestExtension import com.wire.android.framework.TestMessage import com.wire.android.framework.TestMessage.GENERIC_ASSET_CONTENT +import com.wire.android.media.audiomessage.AudioMediaPlayingState +import com.wire.android.media.audiomessage.AudioSpeed +import com.wire.android.media.audiomessage.AudioState import com.wire.android.ui.home.conversations.ConversationSnackbarMessages import com.wire.android.ui.home.conversations.composer.mockUIAudioMessage import com.wire.android.ui.home.conversations.composer.mockUITextMessage import com.wire.android.ui.home.conversations.delete.DeleteMessageDialogActiveState import com.wire.android.ui.home.conversations.delete.DeleteMessageDialogsState +import com.wire.kalium.logic.CoreFailure import com.wire.kalium.logic.StorageFailure import com.wire.kalium.logic.data.message.MessageContent import com.wire.kalium.logic.data.user.UserId import com.wire.kalium.logic.feature.conversation.GetConversationUnreadEventsCountUseCase +import com.wire.kalium.logic.feature.message.GetSenderNameByMessageIdUseCase import io.mockk.coVerify import io.mockk.verify +import kotlinx.collections.immutable.persistentMapOf import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.launch @@ -57,7 +63,7 @@ class ConversationMessagesViewModelTest { fun `given an message ID, when downloading or fetching into internal storage, then should get message details by ID`() = runTest { val message = TestMessage.ASSET_MESSAGE val (arrangement, viewModel) = ConversationMessagesViewModelArrangement() - .withObservableAudioMessagesState(flowOf()) + .withSuccessfulViewModelInit() .withGetMessageAssetUseCaseReturning("path".toPath(), 42L) .withGetMessageByIdReturning(message) .arrange() @@ -81,7 +87,7 @@ class ConversationMessagesViewModelTest { ) val (arrangement, viewModel) = ConversationMessagesViewModelArrangement() .withGetMessageByIdReturning(message) - .withObservableAudioMessagesState(flowOf()) + .withSuccessfulViewModelInit() .withGetMessageAssetUseCaseReturning(assetDataPath, assetSize) .withSuccessfulOpenAssetMessage(assetMimeType, assetName, assetDataPath, assetSize, messageId) .arrange() @@ -109,7 +115,7 @@ class ConversationMessagesViewModelTest { content = MessageContent.Asset(GENERIC_ASSET_CONTENT.copy(name = assetName, mimeType = mimeType, sizeInBytes = assetSize)) ) val (arrangement, viewModel) = ConversationMessagesViewModelArrangement() - .withObservableAudioMessagesState(flowOf()) + .withSuccessfulViewModelInit() .withGetMessageByIdReturning(message) .withGetMessageAssetUseCaseReturning(dataPath, assetSize) .withSuccessfulSaveAssetMessage(mimeType, assetName, dataPath, assetSize, messageId) @@ -133,7 +139,7 @@ class ConversationMessagesViewModelTest { val updatedPagingData = PagingData.from(listOf(secondMessage)) val (arrangement, viewModel) = ConversationMessagesViewModelArrangement() - .withObservableAudioMessagesState(flowOf()) + .withSuccessfulViewModelInit() .arrange() viewModel.conversationViewState.messages.test { @@ -147,7 +153,7 @@ class ConversationMessagesViewModelTest { @Test fun `given a message and a reaction, when toggleReaction is called, then should call ToggleReactionUseCase`() = runTest { val (arrangement, viewModel) = ConversationMessagesViewModelArrangement() - .withObservableAudioMessagesState(flowOf()) + .withSuccessfulViewModelInit() .arrange() val messageId = "mID" @@ -164,7 +170,7 @@ class ConversationMessagesViewModelTest { fun `given getting UnreadEventsCount failed, then messages requested anyway`() = runTest { val (arrangement, _) = ConversationMessagesViewModelArrangement() .withConversationUnreadEventsCount(GetConversationUnreadEventsCountUseCase.Result.Failure(StorageFailure.DataNotFound)) - .withObservableAudioMessagesState(flowOf()) + .withSuccessfulViewModelInit() .arrange() coVerify(exactly = 1) { arrangement.getMessagesForConversationUseCase(any(), 0) } @@ -174,7 +180,7 @@ class ConversationMessagesViewModelTest { fun `given getting UnreadEventsCount succeed, then messages requested with corresponding lastReadIndex`() = runTest { val (arrangement, _) = ConversationMessagesViewModelArrangement() .withConversationUnreadEventsCount(GetConversationUnreadEventsCountUseCase.Result.Success(12)) - .withObservableAudioMessagesState(flowOf()) + .withSuccessfulViewModelInit() .arrange() coVerify(exactly = 1) { arrangement.getMessagesForConversationUseCase(any(), 12) } @@ -185,7 +191,7 @@ class ConversationMessagesViewModelTest { val userId = UserId("someID", "someDomain") val clientId = "someClientId" val (arrangement, viewModel) = ConversationMessagesViewModelArrangement() - .withObservableAudioMessagesState(flowOf()) + .withSuccessfulViewModelInit() .withResetSessionResult() .arrange() @@ -323,4 +329,100 @@ class ConversationMessagesViewModelTest { job.cancel() } + + @Test + fun `given an message ID, when some Audio is played, then should get message sender name by message ID`() = runTest { + val message = TestMessage.ASSET_MESSAGE + val audioState = AudioState.DEFAULT.copy( + audioMediaPlayingState = AudioMediaPlayingState.Playing, + totalTimeInMs = AudioState.TotalTimeInMs.Known(10000), + currentPositionInMs = 300 + ) + val userName = "some name" + val expectedAudioMessagesState = AudioMessagesState( + audioStates = persistentMapOf(message.id to audioState), + audioSpeed = AudioSpeed.NORMAL, + playingAudiMessage = PlayingAudiMessage( + messageId = message.id, + authorName = userName, + currentTimeMs = audioState.currentPositionInMs + ) + ) + val (arrangement, viewModel) = ConversationMessagesViewModelArrangement() + .withSuccessfulViewModelInit() + .withGetSenderNameByMessageId(GetSenderNameByMessageIdUseCase.Result.Success(userName)) + .withObservableAudioMessagesState( + flowOf( + mapOf( + message.id to audioState.copy(currentPositionInMs = 100), + message.id to audioState + ) + ) + ) + .arrange() + + advanceUntilIdle() + + coVerify(exactly = 1) { arrangement.getSenderNameByMessageId(arrangement.conversationId, message.id) } + assertEquals(expectedAudioMessagesState, viewModel.conversationViewState.audioMessagesState) + } + + @Test + fun `given an message ID, when getSenderNameByMessageId fails, then senderName in PlayingAudiMessage is empty`() = runTest { + val message = TestMessage.ASSET_MESSAGE + val audioState = AudioState.DEFAULT.copy( + audioMediaPlayingState = AudioMediaPlayingState.Playing, + totalTimeInMs = AudioState.TotalTimeInMs.Known(10000), + currentPositionInMs = 300 + ) + val expectedAudioMessagesState = AudioMessagesState( + audioStates = persistentMapOf(message.id to audioState), + audioSpeed = AudioSpeed.NORMAL, + playingAudiMessage = PlayingAudiMessage( + messageId = message.id, + authorName = "", + currentTimeMs = audioState.currentPositionInMs + ) + ) + val (arrangement, viewModel) = ConversationMessagesViewModelArrangement() + .withSuccessfulViewModelInit() + .withGetSenderNameByMessageId(GetSenderNameByMessageIdUseCase.Result.Failure(CoreFailure.Unknown(null))) + .withObservableAudioMessagesState( + flowOf( + mapOf( + message.id to audioState.copy(currentPositionInMs = 100), + message.id to audioState + ) + ) + ) + .arrange() + + advanceUntilIdle() + + assertEquals(expectedAudioMessagesState, viewModel.conversationViewState.audioMessagesState) + } + + @Test + fun `given an message ID, when no playing Audio message, then PlayingAudiMessage is null`() = runTest { + val message = TestMessage.ASSET_MESSAGE + val audioState = AudioState.DEFAULT.copy( + audioMediaPlayingState = AudioMediaPlayingState.Stopped, + totalTimeInMs = AudioState.TotalTimeInMs.Known(10000), + currentPositionInMs = 300 + ) + val expectedAudioMessagesState = AudioMessagesState( + audioStates = persistentMapOf(message.id to audioState), + audioSpeed = AudioSpeed.NORMAL, + playingAudiMessage = null + ) + val (arrangement, viewModel) = ConversationMessagesViewModelArrangement() + .withSuccessfulViewModelInit() + .withObservableAudioMessagesState(flowOf(mapOf(message.id to audioState))) + .arrange() + + advanceUntilIdle() + + coVerify(exactly = 0) { arrangement.getSenderNameByMessageId(arrangement.conversationId, message.id) } + assertEquals(expectedAudioMessagesState, viewModel.conversationViewState.audioMessagesState) + } } diff --git a/kalium b/kalium index 0667f9b780a..9e38f7d8410 160000 --- a/kalium +++ b/kalium @@ -1 +1 @@ -Subproject commit 0667f9b780a8262768b0c37af3d49d4f83c55701 +Subproject commit 9e38f7d84108797c36171b621eb716bc51bbe707