Converts old ClickableTexts into new Compose Texts with clickable spans

This commit is contained in:
Vitor Pamplona
2025-04-02 20:37:14 -04:00
parent 4ac046fe39
commit 7c4911f2dc
27 changed files with 358 additions and 444 deletions
@@ -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),
)
}
@@ -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),
)
}
@@ -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) },
)
}
@@ -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<TextLayoutResult?>(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<String>?,
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<TextLayoutResult?>(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 =
@@ -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)
}
}
@@ -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),
)
}
@@ -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),
)
}
@@ -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<String?>(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),
)
}
@@ -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) }
@@ -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(
@@ -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,
@@ -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,
@@ -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 =
@@ -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,
)
}
@@ -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 =
@@ -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,
@@ -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)
}
@@ -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,
)
}
}
@@ -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,
@@ -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))
@@ -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),
)
}
@@ -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)
@@ -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)
@@ -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)
@@ -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")
}
}
}
}
},
)
}
}
@@ -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(
@@ -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,