Converts old ClickableTexts into new Compose Texts with clickable spans
This commit is contained in:
@@ -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),
|
||||
)
|
||||
}
|
||||
+2
-8
@@ -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(
|
||||
|
||||
+5
-7
@@ -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,
|
||||
|
||||
+2
-9
@@ -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,
|
||||
|
||||
+8
-6
@@ -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 =
|
||||
|
||||
+13
-15
@@ -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,
|
||||
)
|
||||
}
|
||||
|
||||
+13
-5
@@ -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,
|
||||
|
||||
+7
-14
@@ -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)
|
||||
}
|
||||
|
||||
+4
-12
@@ -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,
|
||||
|
||||
+14
-16
@@ -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)
|
||||
|
||||
+3
-7
@@ -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)
|
||||
|
||||
+5
-8
@@ -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)
|
||||
|
||||
+9
-24
@@ -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")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
+10
-15
@@ -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(
|
||||
|
||||
+12
-44
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user