diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/components/ClickableRelayUrl.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/components/ClickableRelayUrl.kt new file mode 100644 index 0000000000..d3d7f424e5 --- /dev/null +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/components/ClickableRelayUrl.kt @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2025 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 android.R.attr.maxLines +import android.R.attr.onClick +import androidx.compose.foundation.combinedClickable +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.platform.LocalClipboardManager +import androidx.compose.ui.text.AnnotatedString +import androidx.compose.ui.text.style.TextOverflow +import com.vitorpamplona.amethyst.ui.components.appendLink +import com.vitorpamplona.amethyst.ui.navigation.navs.INav +import com.vitorpamplona.amethyst.ui.navigation.routes.Route + +@Composable +fun ClickableRelayUrl( + relayUrl: String, + nav: INav, +) { + val clipboardManager = LocalClipboardManager.current + val clickableModifier = + remember(relayUrl) { + Modifier + .combinedClickable( + onLongClick = { clipboardManager.setText(AnnotatedString(relayUrl)) }, + onClick = { nav.nav(Route.RelayInfo(relayUrl)) }, + ) + } + + Text( + text = relayUrl, + modifier = clickableModifier, + color = MaterialTheme.colorScheme.primary, + overflow = TextOverflow.MiddleEllipsis, + maxLines = 1, + ) +} diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/components/RichTextViewer.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/components/RichTextViewer.kt index 2deba85af2..45cdf10a16 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/components/RichTextViewer.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/components/RichTextViewer.kt @@ -79,6 +79,7 @@ import com.vitorpamplona.amethyst.commons.richtext.LinkSegment import com.vitorpamplona.amethyst.commons.richtext.ParagraphState import com.vitorpamplona.amethyst.commons.richtext.PhoneSegment import com.vitorpamplona.amethyst.commons.richtext.RegularTextSegment +import com.vitorpamplona.amethyst.commons.richtext.RelayUrlSegment import com.vitorpamplona.amethyst.commons.richtext.RichTextViewerState import com.vitorpamplona.amethyst.commons.richtext.SchemelessUrlSegment import com.vitorpamplona.amethyst.commons.richtext.SecretEmoji @@ -148,7 +149,7 @@ fun RichTextViewer( fun RenderStrangeNamePreview() { Column(modifier = Modifier.padding(10.dp)) { RenderRegular( - "If you want to FreeFrom Official \uD80C\uDD66 stream or download the music from nostr:npub1sctag667a7np6p6ety2up94pnwwxhd2ep8n8afr2gtr47cwd4ewsvdmmjm can you here", + "If you want to FreeFrom Official \uD80C\uDD66 stream or download the music from nostr:npub1sctag667a7np6p6ety2up94pnwwxhd2ep8n8afr2gtr47cwd4ewsvdmmjm at wss://relay.damus.io can you here", EmptyTagList, ) { paragraph, state, spaceWidth, modifier -> RenderTextParagraph(paragraph, spaceWidth, modifier) { word -> @@ -163,6 +164,10 @@ fun RenderStrangeNamePreview() { is RegularTextSegment -> { Text(word.segmentText) } + + is RelayUrlSegment -> { + ClickableRelayUrl(word.segmentText, EmptyNav()) + } } } } @@ -254,6 +259,8 @@ fun RenderRegularPreview2() { is SchemelessUrlSegment -> NoProtocolUrlRenderer(word) is RegularTextSegment -> Text(word.segmentText) + + is RelayUrlSegment -> ClickableRelayUrl(word.segmentText, EmptyNav()) } } } @@ -302,6 +309,8 @@ fun RenderRegularPreview3() { is SchemelessUrlSegment -> NoProtocolUrlRenderer(word) is RegularTextSegment -> Text(word.segmentText) + + is RelayUrlSegment -> ClickableRelayUrl(word.segmentText, EmptyNav()) } } } @@ -478,6 +487,8 @@ private fun RenderWordWithoutPreview( is SchemelessUrlSegment -> NoProtocolUrlRenderer(word) is RegularTextSegment -> Text(word.segmentText) + + is RelayUrlSegment -> ClickableRelayUrl(word.segmentText, nav) } } @@ -509,6 +520,7 @@ private fun RenderWordWithPreview( is SchemelessUrlSegment -> NoProtocolUrlRenderer(word) is RegularTextSegment -> Text(word.segmentText) is Base64Segment -> ZoomableContentView(word.segmentText, state, accountViewModel) + is RelayUrlSegment -> ClickableRelayUrl(word.segmentText, nav) } } diff --git a/commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/richtext/RichTextParser.kt b/commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/richtext/RichTextParser.kt index 9d9df3b991..f1ea9fc299 100644 --- a/commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/richtext/RichTextParser.kt +++ b/commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/richtext/RichTextParser.kt @@ -245,7 +245,7 @@ class RichTextParser { if (videos.contains(word)) return VideoSegment(word) - if (word.startsWith("ws://", ignoreCase = true) || word.startsWith("wss://", ignoreCase = true)) return RelayUrlSegment(word) + if (word.startsWith("ws://") || word.startsWith("wss://")) return RelayUrlSegment(word) if (urls.contains(word)) return LinkSegment(word)