Skip to content

Commit

Permalink
feat: Navigate to selected searched message (WPB-4986) (#2412)
Browse files Browse the repository at this point in the history
Co-authored-by: mohamad.jaara <[email protected]>
  • Loading branch information
alexandreferris and MohamadJaara authored Nov 10, 2023
1 parent 914723f commit faf8286
Show file tree
Hide file tree
Showing 16 changed files with 133 additions and 32 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ import com.wire.kalium.logic.feature.message.GetConversationMessagesFromSearchQu
import com.wire.kalium.logic.feature.message.GetMessageByIdUseCase
import com.wire.kalium.logic.feature.message.GetNotificationsUseCase
import com.wire.kalium.logic.feature.message.GetPaginatedFlowOfMessagesByConversationUseCase
import com.wire.kalium.logic.feature.message.GetSearchedConversationMessagePositionUseCase
import com.wire.kalium.logic.feature.message.MarkMessagesAsNotifiedUseCase
import com.wire.kalium.logic.feature.message.MessageScope
import com.wire.kalium.logic.feature.message.ObserveMessageReactionsUseCase
Expand Down Expand Up @@ -153,4 +154,9 @@ class MessageModule {
@Provides
fun provideGetConversationMessagesFromSearchQueryUseCase(messageScope: MessageScope): GetConversationMessagesFromSearchQueryUseCase =
messageScope.getConversationMessagesFromSearchQuery

@ViewModelScoped
@Provides
fun provideGetSearchedConversationMessagePositionUseCase(messageScope: MessageScope): GetSearchedConversationMessagePositionUseCase =
messageScope.getSearchedConversationMessagePosition
}
28 changes: 24 additions & 4 deletions app/src/main/kotlin/com/wire/android/ui/common/SearchBar.kt
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,9 @@ import androidx.compose.animation.fadeIn
import androidx.compose.animation.fadeOut
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.width
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.LocalTextStyle
Expand All @@ -38,9 +40,9 @@ import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.input.TextFieldValue
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import com.wire.android.R
import com.wire.android.ui.common.progress.WireCircularProgressIndicator
import com.wire.android.ui.common.textfield.WireTextField
import com.wire.android.ui.theme.WireTheme
import com.wire.android.ui.theme.wireColorScheme
Expand All @@ -56,6 +58,7 @@ fun SearchBarInput(
placeholderAlignment: Alignment.Horizontal = Alignment.CenterHorizontally,
interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
textStyle: TextStyle = LocalTextStyle.current,
isLoading: Boolean = false,
modifier: Modifier = Modifier,
) {

Expand All @@ -67,13 +70,30 @@ fun SearchBarInput(
leadingIcon()
},
trailingIcon = {
Box(modifier = Modifier.size(40.dp)) {
Box(
modifier = Modifier
.width(dimensions().spacing64x)
.height(dimensions().spacing40x),
contentAlignment = Alignment.CenterEnd
) {
AnimatedVisibility(
visible = text.text.isNotBlank(),
enter = fadeIn(),
exit = fadeOut()
) {
IconButton(onClick = {
if (isLoading) {
WireCircularProgressIndicator(
modifier = Modifier.padding(
top = dimensions().spacing12x,
bottom = dimensions().spacing12x,
end = dimensions().spacing32x
),
progressColor = MaterialTheme.wireColorScheme.onSurface
)
}
IconButton(
modifier = Modifier.padding(start = dimensions().spacing12x),
onClick = {
onTextTyped(TextFieldValue(""))
}) {
Icon(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ fun SearchTopBar(
isSearchActive: Boolean,
searchBarHint: String,
searchQuery: TextFieldValue = TextFieldValue(""),
isLoading: Boolean = false,
onSearchQueryChanged: (TextFieldValue) -> Unit,
onCloseSearchClicked: (() -> Unit)? = null,
onActiveChanged: (isActive: Boolean) -> Unit = {},
Expand Down Expand Up @@ -112,6 +113,7 @@ fun SearchTopBar(
placeholderText = searchBarHint,
text = searchQuery,
onTextTyped = onSearchQueryChanged,
isLoading = isLoading,
leadingIcon = {
AnimatedContent(!isSearchActive, label = "") { isVisible ->
if (isVisible) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,5 +20,6 @@ package com.wire.android.ui.home.conversations
import com.wire.kalium.logic.data.id.ConversationId

data class ConversationNavArgs(
val conversationId: ConversationId
val conversationId: ConversationId,
val searchedMessageId: String? = null
)
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,8 @@ import androidx.hilt.navigation.compose.hiltViewModel
import androidx.paging.PagingData
import androidx.paging.compose.LazyPagingItems
import androidx.paging.compose.collectAsLazyPagingItems
import androidx.paging.compose.itemsIndexed
import androidx.paging.compose.itemContentType
import androidx.paging.compose.itemKey
import com.ramcosta.composedestinations.annotation.Destination
import com.ramcosta.composedestinations.annotation.RootNavGraph
import com.ramcosta.composedestinations.result.NavResult.Canceled
Expand Down Expand Up @@ -849,12 +850,15 @@ fun MessageList(
modifier = Modifier
.fillMaxSize()
) {
itemsIndexed(lazyPagingMessages, key = { _, uiMessage ->
uiMessage.header.messageId
}) { index, message ->
items(
count = lazyPagingMessages.itemCount,
key = lazyPagingMessages.itemKey { it.header.messageId },
contentType = lazyPagingMessages.itemContentType { it }
) { index ->
val message: UIMessage? = lazyPagingMessages[index]
if (message == null) {
// We can draw a placeholder here, as we fetch the next page of messages
return@itemsIndexed
return@items
}
val showAuthor by remember {
mutableStateOf(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,7 @@ fun MessageItem(
onFailedMessageRetryClicked: (String) -> Unit = {},
onFailedMessageCancelClicked: (String) -> Unit = {},
onLinkClick: (String) -> Unit = {},
onMessageClick: (messageId: String) -> Unit = {},
defaultBackgroundColor: Color = Color.Transparent,
shouldDisplayMessageStatus: Boolean = true,
shouldDisplayFooter: Boolean = true
Expand Down Expand Up @@ -146,7 +147,7 @@ fun MessageItem(
.fillMaxWidth()
.combinedClickable(
enabled = message.isAvailable,
onClick = { }, // TODO: implement some action onClick
onClick = { onMessageClick(message.header.messageId) },
onLongClick = remember(message) { { onLongClicked(message) } }
)
.padding(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ import com.wire.kalium.logic.feature.conversation.ClearUsersTypingEventsUseCase
import com.wire.kalium.logic.feature.conversation.GetConversationUnreadEventsCountUseCase
import com.wire.kalium.logic.feature.conversation.ObserveConversationDetailsUseCase
import com.wire.kalium.logic.feature.message.GetMessageByIdUseCase
import com.wire.kalium.logic.feature.message.GetSearchedConversationMessagePositionUseCase
import com.wire.kalium.logic.feature.message.ToggleReactionUseCase
import com.wire.kalium.logic.feature.sessionreset.ResetSessionResult
import com.wire.kalium.logic.feature.sessionreset.ResetSessionUseCase
Expand Down Expand Up @@ -85,13 +86,19 @@ class ConversationMessagesViewModel @Inject constructor(
private val resetSession: ResetSessionUseCase,
private val conversationAudioMessagePlayer: ConversationAudioMessagePlayer,
private val getConversationUnreadEventsCount: GetConversationUnreadEventsCountUseCase,
private val clearUsersTypingEvents: ClearUsersTypingEventsUseCase
private val clearUsersTypingEvents: ClearUsersTypingEventsUseCase,
private val getSearchedConversationMessagePosition: GetSearchedConversationMessagePositionUseCase
) : SavedStateViewModel(savedStateHandle) {

private val conversationNavArgs: ConversationNavArgs = savedStateHandle.navArgs()
val conversationId: QualifiedID = conversationNavArgs.conversationId
private val searchedMessageIdNavArgs: String? = conversationNavArgs.searchedMessageId

var conversationViewState by mutableStateOf(ConversationMessagesViewState())
var conversationViewState by mutableStateOf(
ConversationMessagesViewState(
searchedMessageId = searchedMessageIdNavArgs
)
)
private set

private var lastImageMessageShownOnGallery: UIMessage.Regular? = null
Expand Down Expand Up @@ -120,7 +127,19 @@ class ConversationMessagesViewModel @Inject constructor(
}

private fun loadPaginatedMessages() = viewModelScope.launch {
val lastReadIndex = when (val result = getConversationUnreadEventsCount(conversationId)) {
val lastReadIndex = conversationViewState.searchedMessageId?.let { messageId ->
conversationViewState = conversationViewState.copy(
searchedMessageId = null
)

when (val result = getSearchedConversationMessagePosition(
conversationId = conversationId,
messageId = messageId
)) {
is GetSearchedConversationMessagePositionUseCase.Result.Success -> result.position
is GetSearchedConversationMessagePositionUseCase.Result.Failure -> 0
}
} ?: when (val result = getConversationUnreadEventsCount(conversationId)) {
is GetConversationUnreadEventsCountUseCase.Result.Success -> result.amount.toInt()
is GetConversationUnreadEventsCountUseCase.Result.Failure -> 0
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,8 @@ data class ConversationMessagesViewState(
val firstUnreadInstant: Instant? = null,
val firstuUnreadEventIndex: Int = 0,
val downloadedAssetDialogState: DownloadedAssetDialogVisibilityState = DownloadedAssetDialogVisibilityState.Hidden,
val audioMessagesState: Map<String, AudioState> = emptyMap()
val audioMessagesState: Map<String, AudioState> = emptyMap(),
val searchedMessageId: String? = null
)

sealed class DownloadedAssetDialogVisibilityState {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,8 @@ import com.wire.android.util.ui.PreviewMultipleThemes

@Composable
fun SearchConversationMessagesResultsScreen(
searchResult: List<UIMessage>
searchResult: List<UIMessage>,
onMessageClick: (messageId: String) -> Unit
) {
LazyColumn {
items(searchResult) { message ->
Expand All @@ -51,7 +52,8 @@ fun SearchConversationMessagesResultsScreen(
onSelfDeletingMessageRead = { },
defaultBackgroundColor = colorsScheme().backgroundVariant,
shouldDisplayMessageStatus = false,
shouldDisplayFooter = false
shouldDisplayFooter = false,
onMessageClick = onMessageClick
)
}
is UIMessage.System -> { }
Expand All @@ -68,7 +70,8 @@ fun previewSearchConversationMessagesResultsScreen() {
searchResult = listOf(
mockMessageWithText,
mockMessageWithText,
)
),
onMessageClick = {}
)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,15 @@ import androidx.hilt.navigation.compose.hiltViewModel
import com.ramcosta.composedestinations.annotation.Destination
import com.ramcosta.composedestinations.annotation.RootNavGraph
import com.wire.android.R
import com.wire.android.navigation.BackStackMode
import com.wire.android.navigation.NavigationCommand
import com.wire.android.navigation.Navigator
import com.wire.android.navigation.style.PopUpNavigationAnimation
import com.wire.android.ui.common.CollapsingTopBarScaffold
import com.wire.android.ui.common.dimensions
import com.wire.android.ui.common.topappbar.search.SearchTopBar
import com.wire.android.ui.destinations.ConversationScreenDestination
import com.wire.android.ui.home.conversations.ConversationNavArgs
import com.wire.android.ui.home.conversations.model.UIMessage

@RootNavGraph
Expand All @@ -53,13 +57,28 @@ fun SearchConversationMessagesScreen(
onSearchQueryChanged = searchConversationMessagesViewModel::searchQueryChanged,
modifier = Modifier.padding(top = dimensions().spacing24x),
onCloseSearchClicked = navigator::navigateBack,
isLoading = isLoading
)
},
content = {
SearchConversationMessagesResultContent(
searchQuery = searchQuery.text,
noneSearchSucceed = isEmptyResult,
searchResult = searchResult
searchResult = searchResult,
isLoading = isLoading,
onMessageClick = { messageId ->
navigator.navigate(
NavigationCommand(
ConversationScreenDestination(
navArgs = ConversationNavArgs(
conversationId = conversationId,
searchedMessageId = messageId
)
),
BackStackMode.UPDATE_EXISTED
)
)
}
)
},
bottomBar = { },
Expand All @@ -73,16 +92,19 @@ fun SearchConversationMessagesScreen(
fun SearchConversationMessagesResultContent(
searchQuery: String,
noneSearchSucceed: Boolean,
searchResult: List<UIMessage>
searchResult: List<UIMessage>,
isLoading: Boolean,
onMessageClick: (messageId: String) -> Unit
) {
if (searchQuery.isEmpty()) {
SearchConversationMessagesEmptyScreen()
} else {
if (noneSearchSucceed) {
if (noneSearchSucceed && !isLoading) {
SearchConversationMessagesNoResultsScreen()
} else {
SearchConversationMessagesResultsScreen(
searchResult = searchResult
searchResult = searchResult,
onMessageClick = onMessageClick
)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,14 @@ package com.wire.android.ui.home.conversations.search.messages

import androidx.compose.ui.text.input.TextFieldValue
import com.wire.android.ui.home.conversations.model.UIMessage
import com.wire.kalium.logic.data.id.ConversationId
import kotlinx.collections.immutable.ImmutableList
import kotlinx.collections.immutable.persistentListOf

data class SearchConversationMessagesState(
val conversationId: ConversationId,
val searchQuery: TextFieldValue = TextFieldValue(""),
val searchResult: ImmutableList<UIMessage> = persistentListOf(),
val isEmptyResult: Boolean = false
val isEmptyResult: Boolean = false,
val isLoading: Boolean = false
)
Original file line number Diff line number Diff line change
Expand Up @@ -51,9 +51,14 @@ class SearchConversationMessagesViewModel @Inject constructor(
var searchConversationMessagesState by savedStateHandle.saveable(
stateSaver = Saver<SearchConversationMessagesState, String>(
save = { it.searchQuery.text },
restore = { SearchConversationMessagesState(searchQuery = TextFieldValue(it)) }
restore = {
SearchConversationMessagesState(
conversationId = conversationId,
searchQuery = TextFieldValue(it)
)
}
)
) { mutableStateOf(SearchConversationMessagesState()) }
) { mutableStateOf(SearchConversationMessagesState(conversationId)) }

private val mutableSearchQueryFlow = MutableStateFlow(searchConversationMessagesState.searchQuery.text)

Expand All @@ -62,13 +67,18 @@ class SearchConversationMessagesViewModel @Inject constructor(
mutableSearchQueryFlow
.debounce(SearchPeopleViewModel.DEFAULT_SEARCH_QUERY_DEBOUNCE)
.collectLatest { searchTerm ->
searchConversationMessagesState = searchConversationMessagesState.copy(
isLoading = true
)

getSearchMessagesForConversation(
searchTerm = searchTerm,
conversationId = conversationId
).onSuccess { uiMessages ->
searchConversationMessagesState = searchConversationMessagesState.copy(
searchResult = uiMessages.toPersistentList(),
isEmptyResult = uiMessages.isEmpty()
isEmptyResult = uiMessages.isEmpty(),
isLoading = false
)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,11 +50,12 @@ class GetMessagesForConversationUseCase @Inject constructor(
val pagingConfig = PagingConfig(
pageSize = PAGE_SIZE,
prefetchDistance = PREFETCH_DISTANCE,
initialLoadSize = max(INITIAL_LOAD_SIZE, lastReadIndex + PREFETCH_DISTANCE)
initialLoadSize = INITIAL_LOAD_SIZE
)
return getMessages(
conversationId,
pagingConfig = pagingConfig
pagingConfig = pagingConfig,
startingOffset = max(0, lastReadIndex - PREFETCH_DISTANCE)
).map { pagingData ->
pagingData.flatMap { messageItem ->
observeMemberDetailsByIds(messageMapper.memberIdList(listOf(messageItem)))
Expand Down
Loading

0 comments on commit faf8286

Please sign in to comment.