Skip to content

Commit

Permalink
feat: Search inside conversation (WPB-4915) (#2358)
Browse files Browse the repository at this point in the history
  • Loading branch information
alexandreferris committed Oct 23, 2023
1 parent e69dfee commit 046f846
Show file tree
Hide file tree
Showing 25 changed files with 1,385 additions and 83 deletions.
Expand Up @@ -28,6 +28,7 @@ import com.wire.kalium.logic.feature.asset.GetMessageAssetUseCase
import com.wire.kalium.logic.feature.asset.ScheduleNewAssetMessageUseCase
import com.wire.kalium.logic.feature.asset.UpdateAssetMessageDownloadStatusUseCase
import com.wire.kalium.logic.feature.message.DeleteMessageUseCase
import com.wire.kalium.logic.feature.message.GetConversationMessagesFromSearchQueryUseCase
import com.wire.kalium.logic.feature.message.GetMessageByIdUseCase
import com.wire.kalium.logic.feature.message.GetNotificationsUseCase
import com.wire.kalium.logic.feature.message.GetPaginatedFlowOfMessagesByConversationUseCase
Expand Down Expand Up @@ -147,4 +148,9 @@ class MessageModule {
@Provides
fun provideGetPaginatedMessagesUseCase(messageScope: MessageScope): GetPaginatedFlowOfMessagesByConversationUseCase =
messageScope.getPaginatedFlowOfMessagesByConversation

@ViewModelScoped
@Provides
fun provideGetConversationMessagesFromSearchQueryUseCase(messageScope: MessageScope): GetConversationMessagesFromSearchQueryUseCase =
messageScope.getConversationMessagesFromSearchQuery
}
25 changes: 20 additions & 5 deletions app/src/main/kotlin/com/wire/android/ui/common/SearchBar.kt
Expand Up @@ -31,11 +31,13 @@ import androidx.compose.material3.IconButton
import androidx.compose.material3.LocalTextStyle
import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.ExperimentalComposeUiApi
import androidx.compose.ui.Modifier
import androidx.compose.ui.focus.FocusRequester
import androidx.compose.ui.focus.focusRequester
import androidx.compose.ui.platform.LocalSoftwareKeyboardController
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.TextStyle
Expand Down Expand Up @@ -73,6 +75,7 @@ fun SearchBar(
)
}

@OptIn(ExperimentalComposeUiApi::class)
@Composable
fun SearchBarInput(
placeholderText: String,
Expand All @@ -82,10 +85,15 @@ fun SearchBarInput(
placeholderTextStyle: TextStyle = LocalTextStyle.current,
interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
textStyle: TextStyle = LocalTextStyle.current,
modifier: Modifier = Modifier
modifier: Modifier = Modifier,
shouldRequestFocus: Boolean = false
) {
val focusRequester = remember { FocusRequester() }
val keyboardController = LocalSoftwareKeyboardController.current

WireTextField(
modifier = modifier,
modifier = modifier
.focusRequester(focusRequester),
value = text,
onValueChange = onTextTyped,
leadingIcon = {
Expand Down Expand Up @@ -116,6 +124,13 @@ fun SearchBarInput(
maxLines = 1,
singleLine = true,
)

if (shouldRequestFocus) {
LaunchedEffect(Unit) {
focusRequester.requestFocus()
keyboardController?.show()
}
}
}

@Preview(showBackground = true)
Expand Down
Expand Up @@ -59,16 +59,18 @@ import com.wire.android.ui.theme.wireDimensions
@OptIn(ExperimentalAnimationApi::class)
@Composable
fun SearchTopBar(
modifier: Modifier = Modifier,
isSearchActive: Boolean,
searchBarHint: String,
searchQuery: TextFieldValue = TextFieldValue(""),
onSearchQueryChanged: (TextFieldValue) -> Unit,
onInputClicked: () -> Unit,
onCloseSearchClicked: () -> Unit,
shouldRequestFocus: Boolean = false,
bottomContent: @Composable ColumnScope.() -> Unit = {}
) {
Column(
modifier = Modifier
modifier = modifier
.wrapContentHeight()
.fillMaxWidth()
.background(MaterialTheme.wireColorScheme.background)
Expand Down Expand Up @@ -117,7 +119,8 @@ fun SearchTopBar(
interactionSource = interactionSource,
modifier = Modifier
.padding(dimensions().spacing8x)
.focusRequester(focusRequester)
.focusRequester(focusRequester),
shouldRequestFocus = shouldRequestFocus
)
// We added an invisible clickable box only present when the search is not active.
// That way we can still make the whole top bar clickable and intercept and discard the long press gestures.
Expand Down
Expand Up @@ -47,6 +47,7 @@ import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp
import androidx.compose.foundation.layout.FlowRow
import androidx.compose.ui.graphics.Color
import com.wire.android.R
import com.wire.android.media.audiomessage.AudioState
import com.wire.android.model.Clickable
Expand Down Expand Up @@ -106,7 +107,10 @@ fun MessageItem(
onSelfDeletingMessageRead: (UIMessage) -> Unit,
onFailedMessageRetryClicked: (String) -> Unit = {},
onFailedMessageCancelClicked: (String) -> Unit = {},
onLinkClick: (String) -> Unit = {}
onLinkClick: (String) -> Unit = {},
defaultBackgroundColor: Color = Color.Transparent,
shouldDisplayMessageStatus: Boolean = true,
shouldDisplayFooter: Boolean = true
) {
with(message) {
val selfDeletionTimerState = rememberSelfDeletionTimer(header.messageStatus.expirationStatus)
Expand All @@ -133,7 +137,7 @@ fun MessageItem(

Modifier.background(color)
} else {
Modifier
Modifier.background(defaultBackgroundColor)
}

Box(backgroundColorModifier) {
Expand Down Expand Up @@ -236,7 +240,7 @@ fun MessageItem(
onLinkClick = onLinkClick
)
}
if (isMyMessage) {
if (isMyMessage && shouldDisplayMessageStatus) {
MessageStatusIndicator(
status = message.header.messageStatus.flowStatus,
isGroupConversation = conversationDetailsData is ConversationDetailsData.Group,
Expand All @@ -249,10 +253,12 @@ fun MessageItem(
HorizontalSpace.x24()
}
}
MessageFooter(
messageFooter,
onReactionClicked
)
if (shouldDisplayFooter) {
MessageFooter(
messageFooter = messageFooter,
onReactionClicked = onReactionClicked
)
}
} else {
MessageDecryptionFailure(
messageHeader = header,
Expand Down
Expand Up @@ -21,9 +21,11 @@
package com.wire.android.ui.home.conversations.details

import androidx.annotation.StringRes
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.fadeIn
import androidx.compose.animation.fadeOut
import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.LocalOverscrollConfiguration
import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyListState
Expand All @@ -32,6 +34,7 @@ import androidx.compose.foundation.pager.HorizontalPager
import androidx.compose.foundation.pager.rememberPagerState
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface
import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.LaunchedEffect
Expand Down Expand Up @@ -60,6 +63,7 @@ import com.wire.android.appLogger
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.MoreOptionIcon
import com.wire.android.ui.common.TabItem
import com.wire.android.ui.common.WireTabRow
Expand All @@ -69,7 +73,6 @@ import com.wire.android.ui.common.bottomsheet.conversation.rememberConversationS
import com.wire.android.ui.common.bottomsheet.rememberWireModalSheetState
import com.wire.android.ui.common.calculateCurrentTab
import com.wire.android.ui.common.dialogs.ArchiveConversationDialog
import com.wire.android.ui.common.scaffold.WireScaffold
import com.wire.android.ui.common.snackbar.LocalSnackbarHostState
import com.wire.android.ui.common.topBarElevation
import com.wire.android.ui.common.topappbar.NavigationIconType
Expand All @@ -94,7 +97,9 @@ import com.wire.android.ui.home.conversations.details.participants.GroupConversa
import com.wire.android.ui.home.conversations.details.participants.model.UIParticipant
import com.wire.android.ui.home.conversationslist.model.DialogState
import com.wire.android.ui.home.conversationslist.model.GroupDialogState
import com.wire.android.ui.destinations.SearchConversationMessagesScreenDestination
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.util.ui.UIText
import kotlinx.coroutines.launch
Expand All @@ -116,6 +121,16 @@ fun GroupConversationDetailsScreen(
val snackbarHostState = LocalSnackbarHostState.current
val showSnackbarMessage: (UIText) -> Unit = remember { { scope.launch { snackbarHostState.showSnackbar(it.asString(resources)) } } }

val onSearchConversationMessagesClick: () -> Unit = {
navigator.navigate(
NavigationCommand(
SearchConversationMessagesScreenDestination(
conversationId = viewModel.conversationId
)
)
)
}

GroupConversationDetailsContent(
conversationSheetContent = viewModel.conversationSheetContent,
bottomSheetEventsHandler = viewModel,
Expand Down Expand Up @@ -195,7 +210,8 @@ fun GroupConversationDetailsScreen(
onEditGroupName = {
navigator.navigate(NavigationCommand(EditConversationNameScreenDestination(viewModel.conversationId)))
},
isLoading = viewModel.requestInProgress
isLoading = viewModel.requestInProgress,
onSearchConversationMessagesClick = onSearchConversationMessagesClick
)

val tryAgainSnackBarMessage = stringResource(id = R.string.error_unknown_message)
Expand Down Expand Up @@ -235,7 +251,8 @@ private fun GroupConversationDetailsContent(
onLeaveGroup: (GroupDialogState) -> Unit,
onDeleteGroup: (GroupDialogState) -> Unit,
groupParticipantsState: GroupConversationParticipantsState,
isLoading: Boolean
isLoading: Boolean,
onSearchConversationMessagesClick: () -> Unit
) {
val scope = rememberCoroutineScope()
val resources = LocalContext.current.resources
Expand Down Expand Up @@ -281,64 +298,87 @@ private fun GroupConversationDetailsContent(
clearConversationDialogState.dismiss()
archiveConversationDialogState.dismiss()
}
WireScaffold(
topBar = {

CollapsingTopBarScaffold(
topBarHeader = {
WireCenterAlignedTopAppBar(
elevation = elevationState,
title = stringResource(R.string.conversation_details_title),
navigationIconType = NavigationIconType.Close,
onNavigationPressed = onBackPressed,
actions = { MoreOptionIcon(onButtonClicked = openBottomSheet) }
) {
WireTabRow(
tabs = GroupConversationDetailsTabItem.entries,
selectedTabIndex = currentTabState,
onTabChange = { scope.launch { pagerState.animateScrollToPage(it) } },
modifier = Modifier.padding(top = MaterialTheme.wireDimensions.spacing16x),
divider = {} // no divider
)
},
topBarCollapsing = {
conversationSheetState.conversationSheetContent?.let {
GroupConversationDetailsTopBarCollapsing(
title = it.title,
conversationId = it.conversationId,
totalParticipants = groupParticipantsState.data.allCount,
isLoading = isLoading,
onSearchConversationMessagesClick = onSearchConversationMessagesClick
)
}
},
modifier = Modifier.fillMaxHeight(),
) { internalPadding ->
var focusedTabIndex: Int by remember { mutableStateOf(initialPageIndex) }
val keyboardController = LocalSoftwareKeyboardController.current
val focusManager = LocalFocusManager.current

CompositionLocalProvider(LocalOverscrollConfiguration provides null) {
HorizontalPager(
state = pagerState,
modifier = Modifier
.fillMaxWidth()
.padding(internalPadding)
) { pageIndex ->
when (GroupConversationDetailsTabItem.entries[pageIndex]) {
GroupConversationDetailsTabItem.OPTIONS -> GroupConversationOptions(
lazyListState = lazyListStates[pageIndex],
onEditGuestAccess = onEditGuestAccess,
onEditSelfDeletingMessages = onEditSelfDeletingMessages,
onEditGroupName = onEditGroupName
)

GroupConversationDetailsTabItem.PARTICIPANTS -> GroupConversationParticipants(
groupParticipantsState = groupParticipantsState,
openFullListPressed = openFullListPressed,
onAddParticipantsPressed = onAddParticipantsPressed,
onProfilePressed = onProfilePressed,
lazyListState = lazyListStates[pageIndex]
topBarFooter = {
AnimatedVisibility(
visible = conversationSheetState.conversationSheetContent != null,
enter = fadeIn(),
exit = fadeOut(),
) {
Surface(
shadowElevation = elevationState,
color = MaterialTheme.wireColorScheme.background
) {
WireTabRow(
tabs = GroupConversationDetailsTabItem.entries,
selectedTabIndex = currentTabState,
onTabChange = { scope.launch { pagerState.animateScrollToPage(it) } },
modifier = Modifier.padding(top = MaterialTheme.wireDimensions.spacing16x),
divider = {} // no divider
)
}
}
},
content = {
var focusedTabIndex: Int by remember { mutableStateOf(initialPageIndex) }
val keyboardController = LocalSoftwareKeyboardController.current
val focusManager = LocalFocusManager.current

CompositionLocalProvider(LocalOverscrollConfiguration provides null) {
HorizontalPager(
state = pagerState,
modifier = Modifier
.fillMaxWidth()
) { pageIndex ->
when (GroupConversationDetailsTabItem.entries[pageIndex]) {
GroupConversationDetailsTabItem.OPTIONS -> GroupConversationOptions(
lazyListState = lazyListStates[pageIndex],
onEditGuestAccess = onEditGuestAccess,
onEditSelfDeletingMessages = onEditSelfDeletingMessages,
onEditGroupName = onEditGroupName
)

LaunchedEffect(pagerState.isScrollInProgress, focusedTabIndex, pagerState.currentPage) {
if (!pagerState.isScrollInProgress && focusedTabIndex != pagerState.currentPage) {
keyboardController?.hide()
focusManager.clearFocus()
focusedTabIndex = pagerState.currentPage
GroupConversationDetailsTabItem.PARTICIPANTS -> GroupConversationParticipants(
groupParticipantsState = groupParticipantsState,
openFullListPressed = openFullListPressed,
onAddParticipantsPressed = onAddParticipantsPressed,
onProfilePressed = onProfilePressed,
lazyListState = lazyListStates[pageIndex]
)
}
}

LaunchedEffect(pagerState.isScrollInProgress, focusedTabIndex, pagerState.currentPage) {
if (!pagerState.isScrollInProgress && focusedTabIndex != pagerState.currentPage) {
keyboardController?.hide()
focusManager.clearFocus()
focusedTabIndex = pagerState.currentPage
}
}
}
}
}
)

WireModalSheetLayout(
sheetState = sheetState,
Expand Down Expand Up @@ -428,7 +468,8 @@ fun PreviewGroupConversationDetails() {
isLoading = false,
onEditGroupName = {},
onEditSelfDeletingMessages = {},
onEditGuestAccess = {}
onEditGuestAccess = {},
onSearchConversationMessagesClick = {}
)
}
}

0 comments on commit 046f846

Please sign in to comment.