Skip to content

Commit

Permalink
feat: search message highlight [WPB-5163] (#2426)
Browse files Browse the repository at this point in the history
  • Loading branch information
Garzas authored Nov 15, 2023
1 parent 4a25e69 commit 18795b3
Show file tree
Hide file tree
Showing 12 changed files with 163 additions and 78 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@ import com.wire.kalium.logic.data.user.UserId
fun MessageItem(
message: UIMessage.Regular,
conversationDetailsData: ConversationDetailsData,
searchQuery: String = "",
showAuthor: Boolean = true,
audioMessagesState: Map<String, AudioState>,
onLongClicked: (UIMessage.Regular) -> Unit,
Expand Down Expand Up @@ -236,6 +237,7 @@ fun MessageItem(
MessageContent(
message = message,
messageContent = messageContent,
searchQuery = searchQuery,
audioMessagesState = audioMessagesState,
onAudioClick = onAudioClick,
onChangeAudioPosition = onChangeAudioPosition,
Expand Down Expand Up @@ -463,6 +465,7 @@ private fun Username(username: String, modifier: Modifier = Modifier) {
private fun MessageContent(
message: UIMessage.Regular,
messageContent: UIMessageContent.Regular?,
searchQuery: String,
audioMessagesState: Map<String, AudioState>,
onAssetClick: Clickable,
onImageClick: Clickable,
Expand Down Expand Up @@ -498,6 +501,7 @@ private fun MessageContent(
}
MessageBody(
messageBody = messageContent.messageBody,
searchQuery = searchQuery,
isAvailable = !message.isPending && message.isAvailable,
onLongClick = onLongClick,
onOpenProfile = onOpenProfile,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ internal fun MessageBody(
messageId: String,
messageBody: MessageBody?,
isAvailable: Boolean,
searchQuery: String = "",
onLongClick: (() -> Unit)? = null,
onOpenProfile: (String) -> Unit,
buttonList: List<MessageButton>?,
Expand All @@ -94,6 +95,7 @@ internal fun MessageBody(
style = MaterialTheme.wireTypography.body01,
colorScheme = MaterialTheme.wireColorScheme,
typography = MaterialTheme.wireTypography,
searchQuery = searchQuery,
mentions = displayMentions,
onLongClick = onLongClick,
onOpenProfile = onOpenProfile,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,6 @@ package com.wire.android.ui.home.conversations.search
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.SideEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.SpanStyle
Expand All @@ -39,31 +33,22 @@ import com.wire.android.R
import com.wire.android.ui.theme.wireColorScheme
import com.wire.android.ui.theme.wireTypography
import com.wire.android.util.EMPTY
import com.wire.android.util.MatchQueryResult
import com.wire.android.util.QueryMatchExtractor
import kotlinx.coroutines.launch

@Composable
fun HighlightName(
name: String,
searchQuery: String,
modifier: Modifier = Modifier
) {
val scope = rememberCoroutineScope()
var highlightIndexes by remember {
mutableStateOf(emptyList<MatchQueryResult>())
}

val queryWithoutSuffix = searchQuery.removeQueryPrefix()

SideEffect {
scope.launch {
highlightIndexes = QueryMatchExtractor.extractQueryMatchIndexes(
matchText = queryWithoutSuffix,
text = name
)
}
}
val highlightIndexes = QueryMatchExtractor.extractQueryMatchIndexes(
matchText = queryWithoutSuffix,
text = name
)

if (queryWithoutSuffix != String.EMPTY && highlightIndexes.isNotEmpty()) {
Text(
buildAnnotatedString {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,44 +23,27 @@ package com.wire.android.ui.home.conversations.search
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.SideEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.setValue
import androidx.compose.ui.text.SpanStyle
import androidx.compose.ui.text.buildAnnotatedString
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.text.withStyle
import com.wire.android.ui.theme.wireColorScheme
import com.wire.android.ui.theme.wireTypography
import com.wire.android.util.EMPTY
import com.wire.android.util.MatchQueryResult
import com.wire.android.util.QueryMatchExtractor
import kotlinx.coroutines.launch

@Composable
fun HighlightSubtitle(
subTitle: String,
searchQuery: String = "",
suffix: String = "@"
) {
val scope = rememberCoroutineScope()
var highlightIndexes by remember {
mutableStateOf(emptyList<MatchQueryResult>())
}

val queryWithoutSuffix = searchQuery.removeQueryPrefix()

SideEffect {
scope.launch {
highlightIndexes = QueryMatchExtractor.extractQueryMatchIndexes(
matchText = queryWithoutSuffix,
text = subTitle
)
}
}
val highlightIndexes = QueryMatchExtractor.extractQueryMatchIndexes(
matchText = queryWithoutSuffix,
text = subTitle
)

if (queryWithoutSuffix != String.EMPTY && highlightIndexes.isNotEmpty()) {
Text(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ import com.wire.android.util.ui.PreviewMultipleThemes
@Composable
fun SearchConversationMessagesResultsScreen(
searchResult: List<UIMessage>,
searchQuery: String = "",
onMessageClick: (messageId: String) -> Unit
) {
LazyColumn {
Expand All @@ -40,6 +41,7 @@ fun SearchConversationMessagesResultsScreen(
MessageItem(
message = message,
conversationDetailsData = ConversationDetailsData.None,
searchQuery = searchQuery,
audioMessagesState = mapOf(),
onLongClicked = { },
onAssetMessageClicked = { },
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,7 @@ fun SearchConversationMessagesResultContent(
} else {
SearchConversationMessagesResultsScreen(
searchResult = searchResult,
searchQuery = searchQuery,
onMessageClick = onMessageClick
)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -89,9 +89,9 @@ class SearchConversationMessagesViewModel @Inject constructor(
val textQueryChanged = searchConversationMessagesState.searchQuery.text != searchQuery.text
// we set the state with a searchQuery, immediately to update the UI first
searchConversationMessagesState = searchConversationMessagesState.copy(searchQuery = searchQuery)
if (textQueryChanged) {
if (textQueryChanged && searchQuery.text.isNotBlank()) {
viewModelScope.launch {
mutableSearchQueryFlow.emit(searchQuery.text)
mutableSearchQueryFlow.emit(searchQuery.text.trim())
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@ import androidx.compose.ui.text.style.TextDecoration
import com.wire.android.ui.common.LinkSpannableString
import com.wire.android.ui.markdown.MarkdownConstants.MENTION_MARK
import com.wire.android.ui.markdown.MarkdownConstants.TAG_URL
import com.wire.android.util.MatchQueryResult
import com.wire.android.util.QueryMatchExtractor
import com.wire.kalium.logic.data.message.mention.MessageMention
import org.commonmark.ext.gfm.strikethrough.Strikethrough
import org.commonmark.ext.gfm.tables.TableBlock
Expand Down Expand Up @@ -203,6 +205,7 @@ fun appendLinksAndMentions(

val stringBuilder = StringBuilder(string)
val updatedMentions = nodeData.mentions.toMutableList()
var highlightIndexes = emptyList<MatchQueryResult>()

// get mentions from text, remove mention marks and update position of mentions
val mentionList: List<MessageMention> = if (stringBuilder.contains(MENTION_MARK) && updatedMentions.isNotEmpty()) {
Expand All @@ -229,6 +232,13 @@ fun appendLinksAndMentions(
listOf()
}

if (nodeData.searchQuery.isNotBlank()) {
highlightIndexes = QueryMatchExtractor.extractQueryMatchIndexes(
matchText = nodeData.searchQuery,
text = stringBuilder.toString()
)
}

val linkInfos = LinkSpannableString.getLinkInfos(stringBuilder.toString(), Linkify.WEB_URLS or Linkify.EMAIL_ADDRESSES)

val append = buildAnnotatedString {
Expand Down Expand Up @@ -257,27 +267,42 @@ fun appendLinksAndMentions(
}

if (mentionList.isNotEmpty()) {
mentionList.forEach {
if (it.length <= 0 || it.start >= length || it.start + it.length > length) {
mentionList.forEach { mention ->
if (mention.length <= 0 || mention.start >= length || mention.start + mention.length > length) {
return@forEach
}
addStyle(
style = SpanStyle(
fontWeight = nodeData.typography.body02.fontWeight,
color = onPrimaryVariant,
background = if (it.isSelfMention) primaryVariant else Color.Unspecified
background = if (mention.isSelfMention) primaryVariant else Color.Unspecified
),
start = it.start,
end = it.start + it.length
start = mention.start,
end = mention.start + mention.length
)
addStringAnnotation(
tag = MarkdownConstants.TAG_MENTION,
annotation = it.userId.toString(),
start = it.start,
end = it.start + it.length
annotation = mention.userId.toString(),
start = mention.start,
end = mention.start + mention.length
)
}
}

highlightIndexes
.forEach { highLightIndex ->
if (highLightIndex.endIndex <= length) {
addStyle(
style = SpanStyle(
background = nodeData.colorScheme.highLight.copy(alpha = 0.5f),
fontFamily = nodeData.typography.body02.fontFamily,
fontWeight = FontWeight.Bold
),
start = highLightIndex.startIndex,
end = highLightIndex.endIndex
)
}
}
}
}
annotatedString.append(append)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ data class NodeData(
val colorScheme: WireColorScheme,
val typography: WireTypography,
val mentions: List<DisplayMention>,
val searchQuery: String,
val onLongClick: (() -> Unit)? = null,
val onOpenProfile: (String) -> Unit,
val onLinkClick: (String) -> Unit
Expand Down
48 changes: 22 additions & 26 deletions app/src/main/kotlin/com/wire/android/util/QueryMatchExtractor.kt
Original file line number Diff line number Diff line change
Expand Up @@ -20,45 +20,41 @@

package com.wire.android.util

import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext

object QueryMatchExtractor {
/**
* extractHighLightIndexes is a recursive function returning a list of the start index and end index
* of the [matchText] that is a text we want to search within the String provided as [text].
* [resultMatches] contains a list of QueryResult with startIndex and endIndex if the match is found.
* [startIndex] is a index from which we start the search
*/
suspend fun extractQueryMatchIndexes(
fun extractQueryMatchIndexes(
resultMatches: List<MatchQueryResult> = emptyList(),
startIndex: Int = 0,
matchText: String,
text: String
): List<MatchQueryResult> =
withContext(Dispatchers.Default) {
if (matchText.isEmpty()) {
return@withContext listOf()
}
val index = text.indexOf(matchText, startIndex = startIndex, ignoreCase = true)
): List<MatchQueryResult> {
if (matchText.isEmpty()) {
return listOf()
}
val index = text.indexOf(matchText, startIndex = startIndex, ignoreCase = true)

if (isIndexFound(index)) {
extractQueryMatchIndexes(
resultMatches = resultMatches + MatchQueryResult(
startIndex = index,
endIndex = index + matchText.length
),
// we are incrementing the startIndex by 1 for the next recursion
// to start looking for the match from the next index that we ended up
// finding the match for the matchText
startIndex = index + 1,
matchText = matchText,
text = text
)
} else {
resultMatches
}
return if (isIndexFound(index)) {
extractQueryMatchIndexes(
resultMatches = resultMatches + MatchQueryResult(
startIndex = index,
endIndex = index + matchText.length
),
// we are incrementing the startIndex by 1 for the next recursion
// to start looking for the match from the next index that we ended up
// finding the match for the matchText
startIndex = index + 1,
matchText = matchText,
text = text
)
} else {
resultMatches
}
}

private fun isIndexFound(index: Int): Boolean {
return index != -1
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ object FeatureVisibilityFlags {
const val ConversationSearchIcon = false
const val UserProfileEditIcon = false
const val MessageEditIcon = true
const val SearchConversationMessages = false
const val SearchConversationMessages = true
}

val LocalFeatureVisibilityFlags = staticCompositionLocalOf { FeatureVisibilityFlags }
Loading

0 comments on commit 18795b3

Please sign in to comment.