diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/components/ClickableEmail.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/components/ClickableEmail.kt index 2ad3d1f646..857f1b39a2 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/components/ClickableEmail.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/components/ClickableEmail.kt @@ -23,13 +23,8 @@ package com.vitorpamplona.amethyst.ui.components import android.content.ActivityNotFoundException import android.content.Context import android.content.Intent -import androidx.compose.foundation.text.ClickableText -import androidx.compose.material3.LocalTextStyle -import androidx.compose.material3.MaterialTheme import androidx.compose.runtime.Composable -import androidx.compose.runtime.remember import androidx.compose.ui.platform.LocalContext -import androidx.compose.ui.text.AnnotatedString import kotlinx.coroutines.CancellationException @Composable @@ -37,10 +32,9 @@ fun ClickableEmail(email: String) { val stripped = email.replaceFirst("mailto:", "") val context = LocalContext.current - ClickableText( - text = remember { AnnotatedString(stripped) }, + ClickableTextPrimary( + text = stripped, onClick = { runCatching { context.sendMail(stripped) } }, - style = LocalTextStyle.current.copy(color = MaterialTheme.colorScheme.primary), ) } diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/components/ClickableNoteTag.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/components/ClickableNoteTag.kt deleted file mode 100644 index d99e1ab512..0000000000 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/components/ClickableNoteTag.kt +++ /dev/null @@ -1,45 +0,0 @@ -/** - * Copyright (c) 2024 Vitor Pamplona - * - * Permission is hereby granted, free of charge, to any person obtaining a copy of - * this software and associated documentation files (the "Software"), to deal in - * the Software without restriction, including without limitation the rights to use, - * copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the - * Software, and to permit persons to whom the Software is furnished to do so, - * subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS - * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR - * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN - * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ -package com.vitorpamplona.amethyst.ui.components - -import androidx.compose.foundation.text.ClickableText -import androidx.compose.material3.LocalTextStyle -import androidx.compose.material3.MaterialTheme -import androidx.compose.runtime.Composable -import androidx.compose.ui.text.AnnotatedString -import com.vitorpamplona.amethyst.model.Note -import com.vitorpamplona.amethyst.ui.navigation.INav -import com.vitorpamplona.amethyst.ui.navigation.routeFor -import com.vitorpamplona.amethyst.ui.note.toShortenHex -import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel - -@Composable -fun ClickableNoteTag( - baseNote: Note, - accountViewModel: AccountViewModel, - nav: INav, -) { - ClickableText( - text = AnnotatedString("@${baseNote.idNote().toShortenHex()}"), - onClick = { routeFor(baseNote, accountViewModel.userProfile())?.let { nav.nav(it) } }, - style = LocalTextStyle.current.copy(color = MaterialTheme.colorScheme.primary), - ) -} diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/components/ClickablePhone.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/components/ClickablePhone.kt index 990ee9137d..48f6064b59 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/components/ClickablePhone.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/components/ClickablePhone.kt @@ -23,22 +23,16 @@ package com.vitorpamplona.amethyst.ui.components import android.content.Context import android.content.Intent import android.net.Uri -import androidx.compose.foundation.text.ClickableText -import androidx.compose.material3.LocalTextStyle -import androidx.compose.material3.MaterialTheme import androidx.compose.runtime.Composable -import androidx.compose.runtime.remember import androidx.compose.ui.platform.LocalContext -import androidx.compose.ui.text.AnnotatedString @Composable fun ClickablePhone(phone: String) { val context = LocalContext.current - ClickableText( - text = remember { AnnotatedString(phone) }, - onClick = { runCatching { context.dial(phone) } }, - style = LocalTextStyle.current.copy(color = MaterialTheme.colorScheme.primary), + ClickableTextPrimary( + text = phone, + onClick = { context.dial(phone) }, ) } diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/components/ClickableRoute.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/components/ClickableRoute.kt index 0db680d7cf..8214257d4d 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/components/ClickableRoute.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/components/ClickableRoute.kt @@ -20,10 +20,8 @@ */ package com.vitorpamplona.amethyst.ui.components -import androidx.compose.foundation.gestures.detectTapGestures import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.text.ClickableText import androidx.compose.foundation.text.InlineTextContent import androidx.compose.foundation.text.appendInlineContent import androidx.compose.material3.LocalContentColor @@ -41,18 +39,18 @@ import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.takeOrElse -import androidx.compose.ui.input.pointer.pointerInput import androidx.compose.ui.platform.LocalUriHandler -import androidx.compose.ui.text.AnnotatedString +import androidx.compose.ui.text.LinkAnnotation import androidx.compose.ui.text.Placeholder import androidx.compose.ui.text.PlaceholderVerticalAlign import androidx.compose.ui.text.SpanStyle -import androidx.compose.ui.text.TextLayoutResult +import androidx.compose.ui.text.TextLinkStyles import androidx.compose.ui.text.TextStyle import androidx.compose.ui.text.buildAnnotatedString import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.text.style.TextOverflow +import androidx.compose.ui.text.withLink import androidx.compose.ui.text.withStyle import androidx.compose.ui.unit.TextUnit import androidx.compose.ui.unit.dp @@ -367,7 +365,7 @@ fun CreateClickableText( overrideColor: Color? = null, fontWeight: FontWeight? = null, fontSize: TextUnit = TextUnit.Unspecified, - onClick: (Int) -> Unit, + onClick: () -> Unit, ) { val primaryColor = MaterialTheme.colorScheme.primary val onBackgroundColor = MaterialTheme.colorScheme.onBackground @@ -381,61 +379,34 @@ fun CreateClickableText( fontWeight = fontWeight, ) - val nonClickablePartStyle = - SpanStyle( - fontSize = fontSize, - color = overrideColor ?: onBackgroundColor, - fontWeight = fontWeight, - ) - buildAnnotatedString { - withStyle(clickablePartStyle) { append(clickablePart) } - if (!suffix.isNullOrBlank()) { - withStyle(nonClickablePartStyle) { append(suffix) } + withLink( + LinkAnnotation.Clickable( + tag = "clickable", + styles = TextLinkStyles(clickablePartStyle), + ) { + onClick() + }, + ) { + append(clickablePart) } - } - } + if (!suffix.isNullOrBlank()) { + val nonClickablePartStyle = + SpanStyle( + fontSize = fontSize, + color = overrideColor ?: onBackgroundColor, + fontWeight = fontWeight, + ) - ClickableText( - text = text, - maxLines = maxLines, - overflow = TextOverflow.Ellipsis, - onClick = onClick, - ) -} - -@Composable -fun ClickableText( - text: AnnotatedString, - modifier: Modifier = Modifier, - style: TextStyle = LocalTextStyle.current, - softWrap: Boolean = true, - overflow: TextOverflow = TextOverflow.Clip, - maxLines: Int = Int.MAX_VALUE, - onTextLayout: (TextLayoutResult) -> Unit = {}, - onClick: (Int) -> Unit, -) { - val layoutResult = remember { mutableStateOf(null) } - val pressIndicator = - Modifier.pointerInput(onClick) { - detectTapGestures { pos -> - layoutResult.value?.let { layoutResult -> - onClick(layoutResult.getOffsetForPosition(pos)) + withStyle(nonClickablePartStyle) { append(suffix) } } } } Text( text = text, - modifier = modifier.then(pressIndicator), - style = style, - softWrap = softWrap, - overflow = overflow, maxLines = maxLines, - onTextLayout = { - layoutResult.value = it - onTextLayout(it) - }, + overflow = TextOverflow.Ellipsis, ) } @@ -611,21 +582,29 @@ fun CreateClickableTextWithEmoji( maxLines: Int = Int.MAX_VALUE, tags: ImmutableListOfLists?, style: TextStyle, - onClick: (Int) -> Unit, + onClick: () -> Unit, ) { CustomEmojiChecker( text = clickablePart, tags = tags, onRegularText = { - ClickableText( - text = AnnotatedString(clickablePart), + Text( + text = + buildAnnotatedString { + withLink( + LinkAnnotation.Clickable("me") { + onClick() + }, + ) { + append(clickablePart) + } + }, style = style, maxLines = maxLines, - onClick = onClick, ) }, onEmojiText = { - ClickableInLineIconRenderer(it, maxLines, style.toSpanStyle()) { onClick(it) } + ClickableInLineIconRenderer(it, maxLines, style.toSpanStyle(), onClick = onClick) }, ) } @@ -683,15 +662,11 @@ fun ClickableInLineIconRenderer( style: SpanStyle, suffix: String? = null, nonClickableStype: SpanStyle? = null, - onClick: (Int) -> Unit, + onClick: () -> Unit, ) { val placeholderSize = remember(style) { - if (style.fontSize == TextUnit.Unspecified) { - 22.sp - } else { - style.fontSize.times(1.1f) - } + if (style.fontSize == TextUnit.Unspecified) 22.sp else style.fontSize.times(1.1f) } val inlineContent = @@ -725,8 +700,10 @@ fun ClickableInLineIconRenderer( val annotatedText = buildAnnotatedString { wordsInOrder.forEachIndexed { idx, value -> - withStyle( - style, + withLink( + LinkAnnotation.Clickable("link", TextLinkStyles(style)) { + onClick() + }, ) { if (value is CustomEmoji.TextType) { append(value.text) @@ -743,20 +720,10 @@ fun ClickableInLineIconRenderer( } } - val layoutResult = remember { mutableStateOf(null) } - val pressIndicator = - Modifier.pointerInput(onClick) { - detectTapGestures { pos -> - layoutResult.value?.let { layoutResult -> onClick(layoutResult.getOffsetForPosition(pos)) } - } - } - Text( text = annotatedText, - modifier = pressIndicator, inlineContent = inlineContent, maxLines = maxLines, - onTextLayout = { layoutResult.value = it }, ) } @@ -771,11 +738,7 @@ fun InLineIconRenderer( ) { val placeholderSize = remember(fontSize) { - if (fontSize == TextUnit.Unspecified) { - 22.sp - } else { - fontSize.times(1.1f) - } + if (fontSize == TextUnit.Unspecified) 22.sp else fontSize.times(1.1f) } val inlineContent = diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/components/ClickableTexts.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/components/ClickableTexts.kt new file mode 100644 index 0000000000..aabb06fe8d --- /dev/null +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/components/ClickableTexts.kt @@ -0,0 +1,151 @@ +/** + * Copyright (c) 2024 Vitor Pamplona + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the + * Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +package com.vitorpamplona.amethyst.ui.components + +import androidx.compose.material3.LocalTextStyle +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.remember +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.text.AnnotatedString +import androidx.compose.ui.text.AnnotatedString.Builder +import androidx.compose.ui.text.LinkAnnotation +import androidx.compose.ui.text.SpanStyle +import androidx.compose.ui.text.TextLinkStyles +import androidx.compose.ui.text.TextStyle +import androidx.compose.ui.text.buildAnnotatedString +import androidx.compose.ui.text.style.TextOverflow +import androidx.compose.ui.text.withLink + +@Composable +fun ClickableTextPrimary( + text: String, + modifier: Modifier = Modifier, + style: TextStyle = LocalTextStyle.current, + softWrap: Boolean = true, + overflow: TextOverflow = TextOverflow.Ellipsis, + maxLines: Int = Int.MAX_VALUE, + onClick: () -> Unit, +) { + ClickableTextColor( + text, + modifier, + style, + softWrap, + overflow, + maxLines, + MaterialTheme.colorScheme.primary, + onClick, + ) +} + +@Composable +fun ClickableTextColor( + text: String, + modifier: Modifier = Modifier, + style: TextStyle = LocalTextStyle.current, + softWrap: Boolean = true, + overflow: TextOverflow = TextOverflow.Ellipsis, + maxLines: Int = Int.MAX_VALUE, + linkColor: Color = MaterialTheme.colorScheme.primary, + onClick: () -> Unit, +) { + Text( + text = + remember(text) { + buildAnnotatedString { + appendLink(text, linkColor, onClick) + } + }, + modifier = modifier, + style = style, + softWrap = softWrap, + overflow = overflow, + maxLines = maxLines, + ) +} + +@Composable +fun ClickableTextNormal( + text: String, + modifier: Modifier = Modifier, + style: TextStyle = LocalTextStyle.current, + softWrap: Boolean = true, + overflow: TextOverflow = TextOverflow.Ellipsis, + maxLines: Int = Int.MAX_VALUE, + onClick: () -> Unit, +) { + Text( + text = + remember(text) { + buildAnnotatedString { + appendLink(text, onClick) + } + }, + modifier = modifier, + style = style, + softWrap = softWrap, + overflow = overflow, + maxLines = maxLines, + ) +} + +inline fun Builder.appendLink( + text: String, + color: Color, + crossinline onClick: () -> Unit, +) = withLink( + LinkAnnotation.Clickable( + "clickable", + TextLinkStyles(SpanStyle(color)), + ) { + onClick() + }, +) { + append(text) +} + +inline fun Builder.appendLink( + text: String, + crossinline onClick: () -> Unit, +) = withLink( + LinkAnnotation.Clickable("clickable") { + onClick() + }, +) { + append(text) +} + +inline fun buildLinkString( + text: String, + crossinline onClick: () -> Unit, +): AnnotatedString = + buildAnnotatedString { + withLink( + LinkAnnotation.Clickable("link") { + onClick() + }, + ) { + append(text) + } + } diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/components/ClickableUrl.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/components/ClickableUrl.kt index a33039cdfc..e55a0057e3 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/components/ClickableUrl.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/components/ClickableUrl.kt @@ -20,13 +20,10 @@ */ package com.vitorpamplona.amethyst.ui.components -import androidx.compose.foundation.text.ClickableText -import androidx.compose.material3.LocalTextStyle -import androidx.compose.material3.MaterialTheme +import android.R.attr.maxLines +import android.R.attr.onClick import androidx.compose.runtime.Composable -import androidx.compose.runtime.remember import androidx.compose.ui.platform.LocalUriHandler -import androidx.compose.ui.text.AnnotatedString import androidx.compose.ui.text.style.TextOverflow @Composable @@ -36,10 +33,8 @@ fun ClickableUrl( ) { val uri = LocalUriHandler.current - val text = remember(urlText) { AnnotatedString(urlText) } - - ClickableText( - text = text, + ClickableTextPrimary( + text = urlText, maxLines = 1, overflow = TextOverflow.Ellipsis, onClick = { @@ -48,6 +43,5 @@ fun ClickableUrl( uri.openUri(doubleCheckedUrl) } }, - style = LocalTextStyle.current.copy(color = MaterialTheme.colorScheme.primary), ) } diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/components/ClickableUserTag.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/components/ClickableUserTag.kt deleted file mode 100644 index afe421bcfd..0000000000 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/components/ClickableUserTag.kt +++ /dev/null @@ -1,52 +0,0 @@ -/** - * Copyright (c) 2024 Vitor Pamplona - * - * Permission is hereby granted, free of charge, to any person obtaining a copy of - * this software and associated documentation files (the "Software"), to deal in - * the Software without restriction, including without limitation the rights to use, - * copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the - * Software, and to permit persons to whom the Software is furnished to do so, - * subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS - * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR - * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN - * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ -package com.vitorpamplona.amethyst.ui.components - -import androidx.compose.foundation.text.ClickableText -import androidx.compose.material3.LocalTextStyle -import androidx.compose.material3.MaterialTheme -import androidx.compose.runtime.Composable -import androidx.compose.runtime.getValue -import androidx.compose.runtime.livedata.observeAsState -import androidx.compose.runtime.remember -import androidx.compose.ui.text.AnnotatedString -import com.vitorpamplona.amethyst.model.User -import com.vitorpamplona.amethyst.ui.navigation.INav -import com.vitorpamplona.amethyst.ui.navigation.routeFor - -@Composable -fun ClickableUserTag( - user: User, - nav: INav, -) { - val route = remember(user) { routeFor(user) } - - val innerUserState by user.live().metadata.observeAsState() - - val userName = - remember(innerUserState) { AnnotatedString("@${innerUserState?.user?.toBestDisplayName()}") } - - ClickableText( - text = userName, - onClick = { nav.nav(route) }, - style = LocalTextStyle.current.copy(color = MaterialTheme.colorScheme.primary), - ) -} diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/components/ClickableWithdrawal.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/components/ClickableWithdrawal.kt index 947f1d5ac9..faecd0ba79 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/components/ClickableWithdrawal.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/components/ClickableWithdrawal.kt @@ -20,9 +20,7 @@ */ package com.vitorpamplona.amethyst.ui.components -import androidx.compose.foundation.text.ClickableText import androidx.compose.material3.LocalTextStyle -import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect @@ -31,7 +29,6 @@ import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import androidx.compose.ui.platform.LocalContext -import androidx.compose.ui.text.AnnotatedString import androidx.compose.ui.text.style.TextDirection import com.vitorpamplona.amethyst.R import com.vitorpamplona.amethyst.ui.actions.CrossfadeIfEnabled @@ -70,8 +67,6 @@ fun MayBeWithdrawal( fun ClickableWithdrawal(withdrawalString: String) { val context = LocalContext.current - val withdraw = remember(withdrawalString) { AnnotatedString("$withdrawalString ") } - var showErrorMessageDialog by remember { mutableStateOf(null) } if (showErrorMessageDialog != null) { @@ -82,9 +77,8 @@ fun ClickableWithdrawal(withdrawalString: String) { ) } - ClickableText( - text = withdraw, + ClickableTextPrimary( + text = "$withdrawalString ", onClick = { payViaIntent(withdrawalString, context, { }) { showErrorMessageDialog = it } }, - style = LocalTextStyle.current.copy(color = MaterialTheme.colorScheme.primary), ) } diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/components/RichTextViewer.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/components/RichTextViewer.kt index 770d837e0d..b87492a5c4 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/components/RichTextViewer.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/components/RichTextViewer.kt @@ -762,7 +762,10 @@ private fun DisplayNoteFromTag( nav = nav, ) } else { - ClickableNoteTag(baseNote, accountViewModel, nav) + ClickableTextPrimary( + text = "@${baseNote.idNote().toShortenHex()}", + onClick = { routeFor(baseNote, accountViewModel.userProfile())?.let { nav.nav(it) } }, + ) } addedChars?.ifBlank { null }?.let { Text(text = it) } diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/navigation/DrawerContent.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/navigation/DrawerContent.kt index 242b75d07d..e28ff88407 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/navigation/DrawerContent.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/navigation/DrawerContent.kt @@ -20,6 +20,7 @@ */ package com.vitorpamplona.amethyst.ui.navigation +import android.R.attr.maxLines import android.R.attr.onClick import androidx.compose.foundation.Image import androidx.compose.foundation.border @@ -73,13 +74,15 @@ import androidx.compose.ui.layout.ContentScale import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalFocusManager import androidx.compose.ui.res.painterResource +import androidx.compose.ui.text.LinkAnnotation import androidx.compose.ui.text.SpanStyle +import androidx.compose.ui.text.TextLinkStyles import androidx.compose.ui.text.buildAnnotatedString import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.input.ImeAction import androidx.compose.ui.text.input.KeyboardCapitalization import androidx.compose.ui.text.style.TextOverflow -import androidx.compose.ui.text.withStyle +import androidx.compose.ui.text.withLink import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import androidx.lifecycle.compose.collectAsStateWithLifecycle @@ -90,7 +93,6 @@ import com.vitorpamplona.amethyst.model.Account import com.vitorpamplona.amethyst.model.FeatureSetType import com.vitorpamplona.amethyst.model.User import com.vitorpamplona.amethyst.ui.actions.mediaServers.MediaServersListView -import com.vitorpamplona.amethyst.ui.components.ClickableText import com.vitorpamplona.amethyst.ui.components.CreateTextWithEmoji import com.vitorpamplona.amethyst.ui.components.RobohashFallbackAsyncImage import com.vitorpamplona.amethyst.ui.note.LoadStatuses @@ -740,23 +742,33 @@ fun BottomContent( .padding(horizontal = 15.dp), verticalAlignment = Alignment.CenterVertically, ) { - ClickableText( - text = + val string = + remember { buildAnnotatedString { - withStyle( - SpanStyle( - fontSize = 12.sp, - fontWeight = FontWeight.Bold, - ), + withLink( + LinkAnnotation.Clickable( + "clickable", + TextLinkStyles( + SpanStyle( + fontSize = 12.sp, + fontWeight = FontWeight.Bold, + ), + ), + ) { + nav.nav(Route.Note(BuildConfig.RELEASE_NOTES_ID)) + nav.closeDrawer() + }, ) { - append("v" + BuildConfig.VERSION_NAME + "-" + BuildConfig.FLAVOR.uppercase()) + append("v12" + BuildConfig.VERSION_NAME + "-" + BuildConfig.FLAVOR.uppercase()) } - }, - onClick = { - nav.nav(Route.Note(BuildConfig.RELEASE_NOTES_ID)) - nav.closeDrawer() - }, + } + } + + Text( + text = string, modifier = Modifier.padding(start = 16.dp), + overflow = TextOverflow.Ellipsis, + maxLines = 1, ) Box(modifier = Modifier.weight(1F)) IconButton( diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/NIP05VerificationDisplay.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/NIP05VerificationDisplay.kt index f0a4770156..ed7ac1dc8c 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/NIP05VerificationDisplay.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/NIP05VerificationDisplay.kt @@ -24,7 +24,6 @@ import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.text.ClickableText import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.OpenInNew import androidx.compose.material3.Icon @@ -390,8 +389,8 @@ fun DisplayNIP05( NIP05VerifiedSymbol(nip05Verified, NIP05IconSize, accountViewModel) - ClickableText( - text = remember(nip05) { AnnotatedString(domain) }, + ClickableTextPrimary( + text = domain, onClick = { runCatching { uri.openUri("https://$domain") } }, style = LocalTextStyle.current.copy(color = MaterialTheme.colorScheme.nip05, fontSize = Font14SP), @@ -441,7 +440,7 @@ fun DisplayNip05ProfileStatus( if (user != "_") { Text( - text = remember { AnnotatedString(user + "@") }, + text = "$user@", color = MaterialTheme.colorScheme.primary, modifier = Modifier.padding(top = 1.dp, bottom = 1.dp, start = 5.dp), maxLines = 1, @@ -450,10 +449,9 @@ fun DisplayNip05ProfileStatus( domainPadStart = 0.dp } - ClickableText( - text = AnnotatedString(domain), + ClickableTextPrimary( + text = domain, onClick = { nip05.let { runCatching { uri.openUri("https://${it.split("@")[1]}") } } }, - style = LocalTextStyle.current.copy(color = MaterialTheme.colorScheme.primary), modifier = Modifier.padding(top = 1.dp, bottom = 1.dp, start = domainPadStart), maxLines = 1, overflow = TextOverflow.Ellipsis, diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/creators/zapsplits/DisplayZapSplits.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/creators/zapsplits/DisplayZapSplits.kt index 3fe5af56c6..5afd85d801 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/creators/zapsplits/DisplayZapSplits.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/creators/zapsplits/DisplayZapSplits.kt @@ -24,13 +24,10 @@ import androidx.compose.foundation.layout.ExperimentalLayoutApi import androidx.compose.foundation.layout.FlowRow import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer -import androidx.compose.foundation.text.ClickableText -import androidx.compose.material3.LocalTextStyle -import androidx.compose.material3.MaterialTheme import androidx.compose.runtime.Composable import androidx.compose.runtime.remember import androidx.compose.ui.Alignment -import androidx.compose.ui.text.AnnotatedString +import com.vitorpamplona.amethyst.ui.components.ClickableTextPrimary import com.vitorpamplona.amethyst.ui.navigation.INav import com.vitorpamplona.amethyst.ui.note.UserPicture import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel @@ -76,11 +73,7 @@ fun DisplayZapSplits( list.forEach { when (it) { is ZapSplitSetupLnAddress -> - ClickableText( - text = AnnotatedString(it.lnAddress), - onClick = {}, - style = LocalTextStyle.current.copy(color = MaterialTheme.colorScheme.primary), - ) + ClickableTextPrimary(it.lnAddress) { } is ZapSplitSetup -> UserPicture( userHex = it.pubKeyHex, diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/elements/DisplayCommunity.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/elements/DisplayCommunity.kt index c3058c4a94..a686b891c6 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/elements/DisplayCommunity.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/elements/DisplayCommunity.kt @@ -20,16 +20,17 @@ */ package com.vitorpamplona.amethyst.ui.note.elements +import android.R.attr.maxLines import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row -import androidx.compose.foundation.text.ClickableText import androidx.compose.material3.LocalTextStyle import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.remember import androidx.compose.ui.Alignment -import androidx.compose.ui.text.AnnotatedString import com.vitorpamplona.amethyst.model.Note +import com.vitorpamplona.amethyst.ui.components.buildLinkString import com.vitorpamplona.amethyst.ui.navigation.INav import com.vitorpamplona.amethyst.ui.navigation.Route import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel @@ -57,12 +58,13 @@ private fun DisplayCommunity( val communityTag = remember(note) { note.event?.getTagOfAddressableKind(CommunityDefinitionEvent.KIND) } ?: return - val displayTag = remember(note) { AnnotatedString(getCommunityShortName(communityTag)) } - val route = remember(note) { Route.Community(communityTag.toTag()) } + val displayTag = + remember(note) { + buildLinkString(getCommunityShortName(communityTag)) { nav.nav(Route.Community(communityTag.toTag())) } + } - ClickableText( + Text( text = displayTag, - onClick = { nav.nav(route) }, style = LocalTextStyle.current.copy( color = diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/elements/DisplayHashtags.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/elements/DisplayHashtags.kt index 47e8270954..3ed6aba7ae 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/elements/DisplayHashtags.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/elements/DisplayHashtags.kt @@ -20,12 +20,13 @@ */ package com.vitorpamplona.amethyst.ui.note.elements +import android.R.attr.maxLines import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row -import androidx.compose.foundation.text.ClickableText import androidx.compose.material3.LocalTextStyle import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.getValue @@ -33,9 +34,10 @@ import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment -import androidx.compose.ui.text.AnnotatedString +import androidx.compose.ui.text.style.TextOverflow import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.vitorpamplona.amethyst.model.Note +import com.vitorpamplona.amethyst.ui.components.buildLinkString import com.vitorpamplona.amethyst.ui.navigation.INav import com.vitorpamplona.amethyst.ui.navigation.Route import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel @@ -70,19 +72,15 @@ private fun DisplayTagList( firstTag: String, nav: INav, ) { - val displayTag = remember(firstTag) { AnnotatedString(" #$firstTag") } - val route = remember(firstTag) { Route.Hashtag(firstTag) } - - ClickableText( - text = displayTag, - onClick = { nav.nav(route) }, - style = - LocalTextStyle.current.copy( - color = - MaterialTheme.colorScheme.primary.copy( - alpha = 0.52f, - ), - ), + Text( + text = + remember(firstTag) { + buildLinkString(" #$firstTag") { + nav.nav(Route.Hashtag(firstTag)) + } + }, + style = LocalTextStyle.current.copy(MaterialTheme.colorScheme.primary.copy(alpha = 0.52f)), + overflow = TextOverflow.Ellipsis, maxLines = 1, ) } diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/elements/DisplayLocation.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/elements/DisplayLocation.kt index 4b335b9230..8e23cc3954 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/elements/DisplayLocation.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/elements/DisplayLocation.kt @@ -20,12 +20,14 @@ */ package com.vitorpamplona.amethyst.ui.note.elements -import androidx.compose.foundation.text.ClickableText import androidx.compose.material3.LocalTextStyle import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Text import androidx.compose.runtime.Composable -import androidx.compose.ui.text.AnnotatedString +import androidx.compose.ui.text.LinkAnnotation +import androidx.compose.ui.text.buildAnnotatedString import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.text.withLink import com.vitorpamplona.amethyst.ui.navigation.INav import com.vitorpamplona.amethyst.ui.navigation.Route import com.vitorpamplona.amethyst.ui.note.creators.location.LoadCityName @@ -37,9 +39,15 @@ fun DisplayLocation( nav: INav, ) { LoadCityName(geohashStr) { cityName -> - ClickableText( - text = AnnotatedString(cityName), - onClick = { nav.nav(Route.Geohash(geohashStr)) }, + Text( + text = + buildAnnotatedString { + withLink( + LinkAnnotation.Clickable("cityname") { nav.nav(Route.Geohash(geohashStr)) }, + ) { + append(cityName) + } + }, style = LocalTextStyle.current.copy( color = diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/elements/DisplayOts.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/elements/DisplayOts.kt index 573fe81ca3..6ad96aa160 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/elements/DisplayOts.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/elements/DisplayOts.kt @@ -20,7 +20,6 @@ */ package com.vitorpamplona.amethyst.ui.note.elements -import androidx.compose.foundation.text.ClickableText import androidx.compose.material3.LocalTextStyle import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text @@ -29,10 +28,10 @@ import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.ui.platform.LocalContext -import androidx.compose.ui.text.buildAnnotatedString import androidx.compose.ui.text.font.FontWeight import com.vitorpamplona.amethyst.R import com.vitorpamplona.amethyst.model.Note +import com.vitorpamplona.amethyst.ui.components.buildLinkString import com.vitorpamplona.amethyst.ui.note.LoadOts import com.vitorpamplona.amethyst.ui.note.timeAgoNoDot import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel @@ -61,26 +60,15 @@ fun DisplayOts( ) } - ClickableText( + Text( text = - buildAnnotatedString { - append( - stringRes( - id = R.string.existed_since, - timeStr, - ), + buildLinkString(stringRes(R.string.existed_since, timeStr)) { + accountViewModel.toastManager.toast( + R.string.ots_info_title, + R.string.ots_info_description, + SimpleDateFormat.getDateTimeInstance().format(Date(unixtimestamp * 1000)), ) }, - onClick = { - val fullDateTime = - SimpleDateFormat.getDateTimeInstance().format(Date(unixtimestamp * 1000)) - - accountViewModel.toastManager.toast( - R.string.ots_info_title, - R.string.ots_info_description, - fullDateTime, - ) - }, style = LocalTextStyle.current.copy( color = MaterialTheme.colorScheme.lessImportantLink, diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/elements/DisplayReward.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/elements/DisplayReward.kt index b460ca9794..8f44e07eb9 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/elements/DisplayReward.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/elements/DisplayReward.kt @@ -30,9 +30,7 @@ import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.width -import androidx.compose.foundation.text.ClickableText import androidx.compose.foundation.text.KeyboardOptions -import androidx.compose.material3.LocalTextStyle import androidx.compose.material3.MaterialTheme import androidx.compose.material3.OutlinedTextField import androidx.compose.material3.Surface @@ -48,7 +46,6 @@ import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier -import androidx.compose.ui.text.AnnotatedString import androidx.compose.ui.text.input.KeyboardCapitalization import androidx.compose.ui.text.input.KeyboardType import androidx.compose.ui.text.input.TextFieldValue @@ -61,6 +58,7 @@ import androidx.lifecycle.viewmodel.compose.viewModel import com.vitorpamplona.amethyst.R import com.vitorpamplona.amethyst.model.Account import com.vitorpamplona.amethyst.model.Note +import com.vitorpamplona.amethyst.ui.components.ClickableTextColor import com.vitorpamplona.amethyst.ui.navigation.INav import com.vitorpamplona.amethyst.ui.navigation.Route import com.vitorpamplona.amethyst.ui.note.ZapIcon @@ -95,17 +93,12 @@ fun DisplayReward( verticalAlignment = Alignment.CenterVertically, modifier = Modifier.clickable { popupExpanded = true }, ) { - ClickableText( - text = AnnotatedString("#bounty"), - onClick = { nav.nav(Route.Hashtag("bounty")) }, - style = - LocalTextStyle.current.copy( - color = - MaterialTheme.colorScheme.primary.copy( - alpha = 0.52f, - ), - ), - ) + ClickableTextColor( + "#bounty", + linkColor = MaterialTheme.colorScheme.primary.copy(alpha = 0.52f), + ) { + nav.nav(Route.Hashtag("bounty")) + } RenderPledgeAmount(baseNote, baseReward, accountViewModel) } diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/elements/DisplayUncitedHashtags.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/elements/DisplayUncitedHashtags.kt index 2e888a4060..2a59432966 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/elements/DisplayUncitedHashtags.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/elements/DisplayUncitedHashtags.kt @@ -22,16 +22,13 @@ package com.vitorpamplona.amethyst.ui.note.elements import androidx.compose.foundation.layout.ExperimentalLayoutApi import androidx.compose.foundation.layout.FlowRow -import androidx.compose.foundation.text.ClickableText -import androidx.compose.material3.LocalTextStyle import androidx.compose.material3.MaterialTheme import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.runtime.produceState -import androidx.compose.runtime.remember -import androidx.compose.ui.text.AnnotatedString import com.vitorpamplona.amethyst.commons.richtext.HashTagSegment import com.vitorpamplona.amethyst.service.CachedRichTextParser +import com.vitorpamplona.amethyst.ui.components.ClickableTextColor import com.vitorpamplona.amethyst.ui.navigation.INav import com.vitorpamplona.amethyst.ui.navigation.Route import com.vitorpamplona.amethyst.ui.theme.HalfTopPadding @@ -95,19 +92,14 @@ fun DisplayUncitedHashtags( } if (unusedHashtags.isNotEmpty()) { - val style = - LocalTextStyle.current.copy( - color = MaterialTheme.colorScheme.lessImportantLink, - ) - FlowRow( modifier = HalfTopPadding, ) { unusedHashtags.forEach { hashtag -> - ClickableText( - text = remember { AnnotatedString("#$hashtag ") }, + ClickableTextColor( + text = "#$hashtag ", onClick = { nav.nav(Route.Hashtag(hashtag)) }, - style = style, + linkColor = MaterialTheme.colorScheme.lessImportantLink, ) } } diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/elements/ForkInfo.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/elements/ForkInfo.kt index 5d734079ea..63a895ad81 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/elements/ForkInfo.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/elements/ForkInfo.kt @@ -22,7 +22,6 @@ package com.vitorpamplona.amethyst.ui.note.elements import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer -import androidx.compose.foundation.text.ClickableText import androidx.compose.material3.LocalTextStyle import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text @@ -37,6 +36,7 @@ import com.vitorpamplona.amethyst.R import com.vitorpamplona.amethyst.model.Note import com.vitorpamplona.amethyst.ui.components.CreateClickableTextWithEmoji import com.vitorpamplona.amethyst.ui.components.LoadNote +import com.vitorpamplona.amethyst.ui.components.appendLink import com.vitorpamplona.amethyst.ui.navigation.INav import com.vitorpamplona.amethyst.ui.navigation.routeFor import com.vitorpamplona.amethyst.ui.note.LoadAddressableNote @@ -87,13 +87,13 @@ fun ForkInformationRowLightColor( if (route != null) { Row(modifier) { - ClickableText( + Text( text = buildAnnotatedString { - append(stringRes(id = R.string.forked_from)) - append(" ") + appendLink(stringRes(id = R.string.forked_from) + " ") { + nav.nav(route) + } }, - onClick = { nav.nav(route) }, style = LocalTextStyle.current.copy( color = MaterialTheme.colorScheme.nip05, diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/elements/ZapTheDevsCard.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/elements/ZapTheDevsCard.kt index 8e3f0a6935..958434b978 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/elements/ZapTheDevsCard.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/elements/ZapTheDevsCard.kt @@ -20,6 +20,7 @@ */ package com.vitorpamplona.amethyst.ui.note.elements +import android.R.attr.onClick import android.content.Context import androidx.compose.animation.core.animateFloatAsState import androidx.compose.foundation.layout.Arrangement @@ -49,11 +50,11 @@ import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.platform.LocalContext -import androidx.compose.ui.text.SpanStyle +import androidx.compose.ui.text.LinkAnnotation import androidx.compose.ui.text.TextStyle import androidx.compose.ui.text.buildAnnotatedString import androidx.compose.ui.text.font.FontWeight -import androidx.compose.ui.text.withStyle +import androidx.compose.ui.text.withLink import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp @@ -65,8 +66,8 @@ import com.vitorpamplona.amethyst.model.Note import com.vitorpamplona.amethyst.model.User import com.vitorpamplona.amethyst.service.ZapPaymentHandler import com.vitorpamplona.amethyst.ui.actions.CrossfadeIfEnabled -import com.vitorpamplona.amethyst.ui.components.ClickableText import com.vitorpamplona.amethyst.ui.components.LoadNote +import com.vitorpamplona.amethyst.ui.components.appendLink import com.vitorpamplona.amethyst.ui.navigation.EmptyNav import com.vitorpamplona.amethyst.ui.navigation.INav import com.vitorpamplona.amethyst.ui.navigation.Route @@ -222,16 +223,12 @@ fun ZapTheDevsCard( Spacer(modifier = StdVertSpacer) - ClickableText( - text = - buildAnnotatedString { - append(stringRes(id = R.string.zap_the_devs_description, BuildConfig.VERSION_NAME)) - append(" ") - withStyle(SpanStyle(color = MaterialTheme.colorScheme.primary)) { - append("#value4value") - } - }, - onClick = { nav.nav(Route.Hashtag("value4value")) }, + Text( + buildAnnotatedString { + append(stringRes(id = R.string.zap_the_devs_description, BuildConfig.VERSION_NAME)) + append(" ") + appendLink("#value4value", MaterialTheme.colorScheme.primary) { nav.nav(Route.Hashtag("value4value")) } + }, ) Spacer(modifier = StdVertSpacer) @@ -244,15 +241,16 @@ fun ZapTheDevsCard( } if (route != null) { - ClickableText( + Text( text = buildAnnotatedString { - withStyle(SpanStyle(color = MaterialTheme.colorScheme.primary)) { + withLink( + LinkAnnotation.Clickable("clickable") { nav.nav(route) }, + ) { append(stringRes(id = R.string.version_name, BuildConfig.VERSION_NAME.substringBefore("-"))) } append(" " + stringRes(id = R.string.brought_to_you_by)) }, - onClick = { nav.nav(route) }, ) } else { Text(stringRes(id = R.string.this_version_brought_to_you_by)) diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/types/AppDefinition.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/types/AppDefinition.kt index 3ee743da6a..10bbeb5370 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/types/AppDefinition.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/types/AppDefinition.kt @@ -36,8 +36,6 @@ import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.foundation.shape.CircleShape -import androidx.compose.foundation.text.ClickableText -import androidx.compose.material3.LocalTextStyle import androidx.compose.material3.MaterialTheme import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect @@ -60,6 +58,7 @@ import coil3.compose.AsyncImage import com.vitorpamplona.amethyst.R import com.vitorpamplona.amethyst.commons.richtext.RichTextParser import com.vitorpamplona.amethyst.model.Note +import com.vitorpamplona.amethyst.ui.components.ClickableTextPrimary import com.vitorpamplona.amethyst.ui.components.CreateTextWithEmoji import com.vitorpamplona.amethyst.ui.components.TranslatableRichTextViewer import com.vitorpamplona.amethyst.ui.components.ZoomableImageDialog @@ -210,10 +209,9 @@ fun RenderAppDefinition( Row(verticalAlignment = Alignment.CenterVertically) { LinkIcon(Size16Modifier, MaterialTheme.colorScheme.placeholderText) - ClickableText( - text = AnnotatedString(website.removePrefix("https://")), + ClickableTextPrimary( + text = website.removePrefix("https://"), onClick = { website.let { runCatching { uri.openUri(it) } } }, - style = LocalTextStyle.current.copy(color = MaterialTheme.colorScheme.primary), modifier = Modifier.padding(top = 1.dp, bottom = 1.dp, start = 5.dp), ) } diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/types/Highlight.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/types/Highlight.kt index ab7fb0d11b..d246ea254a 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/types/Highlight.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/types/Highlight.kt @@ -26,9 +26,7 @@ import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.ExperimentalLayoutApi import androidx.compose.foundation.layout.FlowRow import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.text.ClickableText import androidx.compose.material3.LocalTextStyle -import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect @@ -40,10 +38,10 @@ import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color -import androidx.compose.ui.text.AnnotatedString import com.vitorpamplona.amethyst.model.AddressableNote import com.vitorpamplona.amethyst.model.Note import com.vitorpamplona.amethyst.model.User +import com.vitorpamplona.amethyst.ui.components.ClickableTextPrimary import com.vitorpamplona.amethyst.ui.components.ClickableUrl import com.vitorpamplona.amethyst.ui.components.CreateClickableTextWithEmoji import com.vitorpamplona.amethyst.ui.components.DisplayEvent @@ -253,10 +251,9 @@ fun DisplayEntryForNote( Text("-", maxLines = 1) if (description != null) { - ClickableText( - text = AnnotatedString(description), + ClickableTextPrimary( + text = description, onClick = { routeFor(note, accountViewModel.userProfile())?.let { nav.nav(it) } }, - style = LocalTextStyle.current.copy(color = MaterialTheme.colorScheme.primary), ) } else { DisplayEvent(noteEvent.id, noteEvent.kind, note.toNostrUri(), null, accountViewModel, nav) diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/profile/header/DisplayLNAddress.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/profile/header/DisplayLNAddress.kt index 550034c09b..75699dea8b 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/profile/header/DisplayLNAddress.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/profile/header/DisplayLNAddress.kt @@ -22,8 +22,6 @@ package com.vitorpamplona.amethyst.ui.screen.loggedIn.profile.header import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.padding -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 @@ -33,11 +31,10 @@ import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalContext -import androidx.compose.ui.text.AnnotatedString import androidx.compose.ui.unit.dp import com.vitorpamplona.amethyst.R import com.vitorpamplona.amethyst.ui.actions.InformationDialog -import com.vitorpamplona.amethyst.ui.components.ClickableText +import com.vitorpamplona.amethyst.ui.components.ClickableTextPrimary import com.vitorpamplona.amethyst.ui.navigation.INav import com.vitorpamplona.amethyst.ui.navigation.routeToMessage import com.vitorpamplona.amethyst.ui.note.ErrorMessageDialog @@ -91,10 +88,9 @@ fun DisplayLNAddress( Row(verticalAlignment = Alignment.CenterVertically) { LightningAddressIcon(modifier = Size16Modifier, tint = BitcoinOrange) - ClickableText( - text = AnnotatedString(lud16), + ClickableTextPrimary( + text = lud16, onClick = { zapExpanded = !zapExpanded }, - style = LocalTextStyle.current.copy(color = MaterialTheme.colorScheme.primary), modifier = Modifier .padding(top = 1.dp, bottom = 1.dp, start = 5.dp) diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/profile/header/DrawAdditionalInfo.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/profile/header/DrawAdditionalInfo.kt index 6454778b57..3ae5407be5 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/profile/header/DrawAdditionalInfo.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/profile/header/DrawAdditionalInfo.kt @@ -29,7 +29,6 @@ import androidx.compose.material.icons.filled.ContentCopy import androidx.compose.material.icons.filled.Link import androidx.compose.material3.Icon import androidx.compose.material3.IconButton -import androidx.compose.material3.LocalTextStyle import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text import androidx.compose.runtime.Composable @@ -50,7 +49,7 @@ import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import com.vitorpamplona.amethyst.R import com.vitorpamplona.amethyst.model.User -import com.vitorpamplona.amethyst.ui.components.ClickableText +import com.vitorpamplona.amethyst.ui.components.ClickableTextPrimary import com.vitorpamplona.amethyst.ui.components.CreateTextWithEmoji import com.vitorpamplona.amethyst.ui.components.DisplayNip05ProfileStatus import com.vitorpamplona.amethyst.ui.components.TranslatableRichTextViewer @@ -171,8 +170,8 @@ fun DrawAdditionalInfo( modifier = Modifier.size(16.dp), ) - ClickableText( - text = AnnotatedString(website.removePrefix("https://")), + ClickableTextPrimary( + text = website.removePrefix("https://"), onClick = { website.let { runCatching { @@ -184,7 +183,6 @@ fun DrawAdditionalInfo( } } }, - style = LocalTextStyle.current.copy(color = MaterialTheme.colorScheme.primary), modifier = Modifier.padding(top = 1.dp, bottom = 1.dp, start = 5.dp), ) } @@ -205,10 +203,9 @@ fun DrawAdditionalInfo( modifier = Modifier.size(16.dp), ) - ClickableText( - text = AnnotatedString(identity.identity), + ClickableTextPrimary( + text = identity.identity, onClick = { runCatching { uri.openUri(identity.toProofUrl()) } }, - style = LocalTextStyle.current.copy(color = MaterialTheme.colorScheme.primary), modifier = Modifier .padding(top = 1.dp, bottom = 1.dp, start = 5.dp) diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedOff/AcceptTerms.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedOff/AcceptTerms.kt index 6e67a0672a..2ac1f49978 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedOff/AcceptTerms.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedOff/AcceptTerms.kt @@ -23,14 +23,13 @@ package com.vitorpamplona.amethyst.ui.screen.loggedOff import androidx.compose.foundation.layout.Row import androidx.compose.material3.Checkbox import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.platform.LocalUriHandler -import androidx.compose.ui.text.SpanStyle import androidx.compose.ui.text.buildAnnotatedString -import androidx.compose.ui.text.withStyle import com.vitorpamplona.amethyst.R -import com.vitorpamplona.amethyst.ui.components.ClickableText +import com.vitorpamplona.amethyst.ui.components.appendLink import com.vitorpamplona.amethyst.ui.stringRes @Composable @@ -44,32 +43,18 @@ fun AcceptTerms( onCheckedChange = onCheckedChange, ) - val regularText = SpanStyle(color = MaterialTheme.colorScheme.onBackground) - val clickableTextStyle = SpanStyle(color = MaterialTheme.colorScheme.primary) - - val annotatedTermsString = - buildAnnotatedString { - withStyle(regularText) { append(stringRes(R.string.i_accept_the)) } - - withStyle(clickableTextStyle) { - pushStringAnnotation("openTerms", "") - append(stringRes(R.string.terms_of_use)) - pop() - } - } - val uri = LocalUriHandler.current + val primary = MaterialTheme.colorScheme.primary - ClickableText( - annotatedTermsString, - ) { spanOffset -> - annotatedTermsString.getStringAnnotations(spanOffset, spanOffset).firstOrNull()?.also { span -> - if (span.tag == "openTerms") { + Text( + buildAnnotatedString { + append(stringRes(R.string.i_accept_the)) + appendLink(stringRes(R.string.terms_of_use), primary) { runCatching { uri.openUri("https://github.com/vitorpamplona/amethyst/blob/main/PRIVACY.md") } } - } - } + }, + ) } } diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedOff/TorSettingsSetup.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedOff/TorSettingsSetup.kt index 295a8d45a4..1757e2daa1 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedOff/TorSettingsSetup.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedOff/TorSettingsSetup.kt @@ -22,18 +22,17 @@ package com.vitorpamplona.amethyst.ui.screen.loggedOff import androidx.compose.foundation.layout.padding import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier -import androidx.compose.ui.text.SpanStyle import androidx.compose.ui.text.buildAnnotatedString -import androidx.compose.ui.text.withStyle import androidx.compose.ui.unit.dp import com.vitorpamplona.amethyst.R -import com.vitorpamplona.amethyst.ui.components.ClickableText +import com.vitorpamplona.amethyst.ui.components.appendLink import com.vitorpamplona.amethyst.ui.stringRes import com.vitorpamplona.amethyst.ui.tor.ConnectTorDialog import com.vitorpamplona.amethyst.ui.tor.TorSettings @@ -48,20 +47,16 @@ fun TorSettingsSetup( var connectOrbotDialogOpen by remember { mutableStateOf(false) } var activeTor by remember { mutableStateOf(false) } - val text = - buildAnnotatedString { - append(stringRes(R.string.connect_via_tor1) + " ") - withStyle(SpanStyle(color = MaterialTheme.colorScheme.primary)) { - append(stringRes(R.string.connect_via_tor2)) - } - } + val primary = MaterialTheme.colorScheme.primary - ClickableText( - text = text, + Text( + text = + buildAnnotatedString { + append(stringRes(R.string.connect_via_tor1) + " ") + appendLink(stringRes(R.string.connect_via_tor2), primary) { connectOrbotDialogOpen = true } + }, modifier = Modifier.padding(vertical = 10.dp), - ) { - connectOrbotDialogOpen = true - } + ) if (connectOrbotDialogOpen) { ConnectTorDialog( diff --git a/amethyst/src/play/java/com/vitorpamplona/amethyst/ui/components/TranslatableRichTextViewer.kt b/amethyst/src/play/java/com/vitorpamplona/amethyst/ui/components/TranslatableRichTextViewer.kt index 1c2e86911f..4dc9b42f62 100644 --- a/amethyst/src/play/java/com/vitorpamplona/amethyst/ui/components/TranslatableRichTextViewer.kt +++ b/amethyst/src/play/java/com/vitorpamplona/amethyst/ui/components/TranslatableRichTextViewer.kt @@ -27,7 +27,6 @@ import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size -import androidx.compose.foundation.text.ClickableText import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.Check import androidx.compose.material3.DropdownMenu @@ -49,10 +48,8 @@ import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color -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 androidx.compose.ui.unit.dp import androidx.core.os.ConfigurationCompat import com.vitorpamplona.amethyst.R @@ -178,54 +175,25 @@ private fun TranslationMessage( Row( modifier = Modifier.fillMaxWidth().padding(top = 5.dp), ) { - val clickableTextStyle = SpanStyle(color = MaterialTheme.colorScheme.lessImportantLink) + val textColor = MaterialTheme.colorScheme.lessImportantLink - val annotatedTranslationString = - buildAnnotatedString { - withStyle(clickableTextStyle) { - pushStringAnnotation("langSettings", true.toString()) - append(stringRes(R.string.translations_auto)) - pop() - } - - append("-${stringRes(R.string.translations_translated_from)} ") - - withStyle(clickableTextStyle) { - pushStringAnnotation("showOriginal", true.toString()) - append(Locale(source).displayName) - pop() - } - - append(" ${stringRes(R.string.translations_to)} ") - - withStyle(clickableTextStyle) { - pushStringAnnotation("showOriginal", false.toString()) - append(Locale(target).displayName) - pop() - } - } - - ClickableText( - text = annotatedTranslationString, + Text( + text = + buildAnnotatedString { + appendLink(stringRes(R.string.translations_auto), textColor) { langSettingsPopupExpanded = !langSettingsPopupExpanded } + append("-${stringRes(R.string.translations_translated_from)} ") + appendLink(Locale(source).displayName, textColor) { onChangeWhatToShow(true) } + append(" ${stringRes(R.string.translations_to)} ") + appendLink(Locale(target).displayName, textColor) { onChangeWhatToShow(false) } + }, style = LocalTextStyle.current.copy( - color = - MaterialTheme.colorScheme.onSurface.copy( - alpha = 0.32f, - ), + color = MaterialTheme.colorScheme.onSurface.copy(alpha = 0.32f), fontSize = Font14SP, ), overflow = TextOverflow.Visible, maxLines = 3, - ) { spanOffset -> - annotatedTranslationString.getStringAnnotations(spanOffset, spanOffset).firstOrNull()?.also { span -> - if (span.tag == "showOriginal") { - onChangeWhatToShow(span.item.toBoolean()) - } else { - langSettingsPopupExpanded = !langSettingsPopupExpanded - } - } - } + ) DropdownMenu( expanded = langSettingsPopupExpanded,