Links in message bubbles
This commit is contained in:
@@ -6,7 +6,10 @@ import androidx.lifecycle.viewModelScope
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.SharingStarted
|
||||
import kotlinx.coroutines.flow.cache
|
||||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.flow.shareIn
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import net.buzzert.kordophone.backend.model.Conversation
|
||||
@@ -29,6 +32,7 @@ class ConversationListViewModel @Inject constructor(
|
||||
) : ViewModel() {
|
||||
val conversations: Flow<List<Conversation>>
|
||||
get() = chatRepository.conversationChanges
|
||||
.shareIn(viewModelScope, started = SharingStarted.WhileSubscribed())
|
||||
.map {
|
||||
it.sortedBy { it.date }
|
||||
.reversed()
|
||||
|
||||
@@ -19,6 +19,7 @@ import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.LazyListState
|
||||
import androidx.compose.foundation.lazy.rememberLazyListState
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.foundation.text.ClickableText
|
||||
import androidx.compose.material.CircularProgressIndicator
|
||||
import androidx.compose.material.MaterialTheme
|
||||
import androidx.compose.material.Scaffold
|
||||
@@ -37,6 +38,7 @@ import androidx.compose.ui.draw.alpha
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.platform.LocalUriHandler
|
||||
import androidx.compose.ui.text.input.TextFieldValue
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
@@ -58,6 +60,8 @@ import net.buzzert.kordophonedroid.ui.Destination
|
||||
import net.buzzert.kordophonedroid.ui.LocalNavController
|
||||
import net.buzzert.kordophonedroid.ui.attachments.AttachmentFetchData
|
||||
import net.buzzert.kordophonedroid.ui.attachments.AttachmentViewModel
|
||||
import net.buzzert.kordophonedroid.ui.shared.LINK_ANNOTATION_TAG
|
||||
import net.buzzert.kordophonedroid.ui.shared.linkify
|
||||
import net.buzzert.kordophonedroid.ui.theme.KordophoneTopAppBar
|
||||
import okhttp3.HttpUrl
|
||||
import okhttp3.HttpUrl.Companion.toHttpUrl
|
||||
@@ -317,16 +321,27 @@ fun MessageBubble(
|
||||
) {
|
||||
val backgroundBubbleColor = if (mine) MaterialTheme.colors.primary else MaterialTheme.colors.secondary
|
||||
|
||||
// Linkify text
|
||||
val annotatedString = text.linkify()
|
||||
val urlHandler = LocalUriHandler.current
|
||||
|
||||
BubbleScaffold(mine = mine, modifier = modifier) {
|
||||
Surface(
|
||||
color = backgroundBubbleColor,
|
||||
shape = if (mine) OutgoingChatBubbleShape else IncomingChatBubbleShape,
|
||||
) {
|
||||
Text(
|
||||
text = text,
|
||||
ClickableText(
|
||||
text = annotatedString,
|
||||
style = MaterialTheme.typography.body2,
|
||||
modifier = Modifier
|
||||
.padding(12.dp)
|
||||
.padding(12.dp),
|
||||
onClick = { index ->
|
||||
annotatedString
|
||||
.getStringAnnotations(LINK_ANNOTATION_TAG, index, index)
|
||||
.firstOrNull()?.let {
|
||||
urlHandler.openUri(it.item)
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
package net.buzzert.kordophonedroid.ui.shared
|
||||
|
||||
import androidx.compose.ui.text.AnnotatedString
|
||||
import androidx.compose.ui.text.SpanStyle
|
||||
import androidx.compose.ui.text.buildAnnotatedString
|
||||
import androidx.compose.ui.text.style.TextDecoration
|
||||
|
||||
const val LINK_ANNOTATION_TAG = "link"
|
||||
private val LINK_REGEX = "(https?)://[-a-zA-Z0-9+&@#/%?=~_|!:,.;]*[-a-zA-Z0-9+&@#/%=~_|]".toRegex()
|
||||
|
||||
fun String.linkify(): AnnotatedString {
|
||||
val text = this
|
||||
val matches = LINK_REGEX.findAll(this)
|
||||
return buildAnnotatedString {
|
||||
append(text)
|
||||
|
||||
for (match in matches) {
|
||||
val range = match.range.also {
|
||||
// Make inclusive.
|
||||
IntRange(it.first, it.last + 1)
|
||||
}
|
||||
|
||||
// Annotate link
|
||||
addStringAnnotation(LINK_ANNOTATION_TAG, match.value, range.first, range.last)
|
||||
|
||||
// Add style
|
||||
addStyle(
|
||||
style = SpanStyle(textDecoration = TextDecoration.Underline),
|
||||
start = range.first, end = range.last
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user