diff --git a/app/src/main/java/com/vitorpamplona/amethyst/ui/actions/NewPostView.kt b/app/src/main/java/com/vitorpamplona/amethyst/ui/actions/NewPostView.kt index 100b3fabe..8ef31d698 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/ui/actions/NewPostView.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/ui/actions/NewPostView.kt @@ -167,7 +167,7 @@ fun NewPostView(onClose: () -> Unit, baseReplyTo: Note? = null, quote: Note? = n if (isValidURL(myUrlPreview)) { val removedParamsFromUrl = myUrlPreview.split("?")[0].lowercase() - if (imageExtensions.any { removedParamsFromUrl.endsWith(it, true) }) { + if (imageExtensions.any { removedParamsFromUrl.endsWith(it) }) { AsyncImage( model = myUrlPreview, contentDescription = myUrlPreview, @@ -182,7 +182,7 @@ fun NewPostView(onClose: () -> Unit, baseReplyTo: Note? = null, quote: Note? = n RoundedCornerShape(15.dp) ) ) - } else if (videoExtensions.any { removedParamsFromUrl.endsWith(it, true) }) { + } else if (videoExtensions.any { removedParamsFromUrl.endsWith(it) }) { VideoView(myUrlPreview) } else { UrlPreview(myUrlPreview, myUrlPreview) diff --git a/app/src/main/java/com/vitorpamplona/amethyst/ui/components/RichTextViewer.kt b/app/src/main/java/com/vitorpamplona/amethyst/ui/components/RichTextViewer.kt index 02ff82e40..1dc0ce154 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/ui/components/RichTextViewer.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/ui/components/RichTextViewer.kt @@ -15,7 +15,6 @@ import androidx.compose.material.LocalTextStyle import androidx.compose.material.MaterialTheme import androidx.compose.material.Text import androidx.compose.runtime.Composable -import androidx.compose.runtime.livedata.observeAsState import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip import androidx.compose.ui.graphics.Color @@ -138,10 +137,10 @@ fun RichTextViewer( // sequence of images will render in a slideview if (isValidURL(word)) { val removedParamsFromUrl = word.split("?")[0].lowercase() - if (imageExtensions.any { word.endsWith(it, true) }) { + if (imageExtensions.any { removedParamsFromUrl.endsWith(it) }) { imagesForPager.add(word) } - if (videoExtensions.any { word.endsWith(it, true) }) { + if (videoExtensions.any { removedParamsFromUrl.endsWith(it) }) { imagesForPager.add(word) } } @@ -155,28 +154,59 @@ fun RichTextViewer( s.forEach { word: String -> if (canPreview) { // Explicit URL - val lnInvoice = LnInvoiceUtil.findInvoice(word) - val lnWithdrawal = LnWithdrawalUtil.findWithdrawal(word) - if (isValidURL(word)) { val removedParamsFromUrl = word.split("?")[0].lowercase() - if (imageExtensions.any { word.endsWith(it, true) }) { + if (imageExtensions.any { removedParamsFromUrl.endsWith(it) }) { ZoomableImageView(word, imagesForPager) - } else if (videoExtensions.any { word.endsWith(it, true) }) { + } else if (videoExtensions.any { removedParamsFromUrl.endsWith(it) }) { ZoomableImageView(word, imagesForPager) } else { UrlPreview(word, "$word ") } - } else if (lnInvoice != null) { - InvoicePreview(lnInvoice) - } else if (lnWithdrawal != null) { - ClickableWithdrawal(withdrawalString = lnWithdrawal) + } else if (word.startsWith("lnbc", true)) { + val lnInvoice = LnInvoiceUtil.findInvoice(word) + if (lnInvoice != null) { + InvoicePreview(lnInvoice) + } else { + Text( + text = "$word ", + style = LocalTextStyle.current.copy(textDirection = TextDirection.Content) + ) + } + } else if (word.startsWith("lnurl", true)) { + val lnWithdrawal = LnWithdrawalUtil.findWithdrawal(word) + if (lnWithdrawal != null) { + ClickableWithdrawal(withdrawalString = lnWithdrawal) + } else { + Text( + text = "$word ", + style = LocalTextStyle.current.copy(textDirection = TextDirection.Content) + ) + } } else if (Patterns.EMAIL_ADDRESS.matcher(word).matches()) { ClickableEmail(word) - } else if (Patterns.PHONE.matcher(word).matches() && word.length > 6) { + } else if (word.length > 6 && Patterns.PHONE.matcher(word).matches()) { ClickablePhone(word) } else if (isBechLink(word)) { BechLink(word, navController) + } else if (word.startsWith("#")) { + if (tagIndex.matcher(word).matches() && tags != null) { + TagLink( + word, + tags, + canPreview, + backgroundColor, + accountViewModel, + navController + ) + } else if (hashTagsPattern.matcher(word).matches()) { + HashTag(word, accountViewModel, navController) + } else { + Text( + text = "$word ", + style = LocalTextStyle.current.copy(textDirection = TextDirection.Content) + ) + } } else if (noProtocolUrlValidator.matcher(word).matches()) { val matcher = noProtocolUrlValidator.matcher(word) matcher.find() @@ -185,10 +215,6 @@ fun RichTextViewer( ClickableUrl(url, "https://$url") Text("$additionalChars ") - } else if (tagIndex.matcher(word).matches() && tags != null) { - TagLink(word, tags, canPreview, backgroundColor, accountViewModel, navController) - } else if (hashTagsPattern.matcher(word).matches()) { - HashTag(word, accountViewModel, navController) } else { Text( text = "$word ", @@ -198,12 +224,40 @@ fun RichTextViewer( } else { if (isValidURL(word)) { ClickableUrl("$word ", word) + } else if (word.startsWith("lnurl", true)) { + val lnWithdrawal = LnWithdrawalUtil.findWithdrawal(word) + if (lnWithdrawal != null) { + ClickableWithdrawal(withdrawalString = lnWithdrawal) + } else { + Text( + text = "$word ", + style = LocalTextStyle.current.copy(textDirection = TextDirection.Content) + ) + } } else if (Patterns.EMAIL_ADDRESS.matcher(word).matches()) { ClickableEmail(word) } else if (Patterns.PHONE.matcher(word).matches() && word.length > 6) { ClickablePhone(word) } else if (isBechLink(word)) { BechLink(word, navController) + } else if (word.startsWith("#")) { + if (tagIndex.matcher(word).matches() && tags != null) { + TagLink( + word, + tags, + canPreview, + backgroundColor, + accountViewModel, + navController + ) + } else if (hashTagsPattern.matcher(word).matches()) { + HashTag(word, accountViewModel, navController) + } else { + Text( + text = "$word ", + style = LocalTextStyle.current.copy(textDirection = TextDirection.Content) + ) + } } else if (noProtocolUrlValidator.matcher(word).matches()) { val matcher = noProtocolUrlValidator.matcher(word) matcher.find() @@ -212,10 +266,6 @@ fun RichTextViewer( ClickableUrl(url, "https://$url") Text("$additionalChars ") - } else if (tagIndex.matcher(word).matches() && tags != null) { - TagLink(word, tags, canPreview, backgroundColor, accountViewModel, navController) - } else if (hashTagsPattern.matcher(word).matches()) { - HashTag(word, accountViewModel, navController) } else { Text( text = "$word ", @@ -235,9 +285,9 @@ private fun isArabic(text: String): Boolean { } fun isBechLink(word: String): Boolean { - val cleaned = word.removePrefix("@").removePrefix("nostr:").removePrefix("@") + val cleaned = word.removePrefix("@").removePrefix("nostr:").removePrefix("@").take(7).lowercase() - return listOf("npub1", "naddr1", "note1", "nprofile1", "nevent1").any { cleaned.startsWith(it, true) } + return listOf("npub1", "naddr1", "note1", "nprofile1", "nevent1").any { cleaned.startsWith(it) } } @Composable @@ -340,14 +390,8 @@ fun TagLink(word: String, tags: List>, canPreview: Boolean, backgro if (tags[index][0] == "p") { val baseUser = LocalCache.checkGetOrCreateUser(tags[index][1]) if (baseUser != null) { - val userState = baseUser.live().metadata.observeAsState() - val user = userState.value?.user - if (user != null) { - ClickableUserTag(user, navController) - Text(text = "$extraCharacters ") - } else { - Text(text = "$word ") - } + ClickableUserTag(baseUser, navController) + Text(text = "$extraCharacters ") } else { // if here the tag is not a valid Nostr Hex Text(text = "$word ") diff --git a/app/src/main/java/com/vitorpamplona/amethyst/ui/components/ZoomableImageView.kt b/app/src/main/java/com/vitorpamplona/amethyst/ui/components/ZoomableImageView.kt index 02f5cafe8..19b3b2a24 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/ui/components/ZoomableImageView.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/ui/components/ZoomableImageView.kt @@ -62,7 +62,8 @@ fun ZoomableImageView(word: String, images: List = listOf(word)) { mutableStateOf(null) } - if (imageExtensions.any { word.endsWith(it, true) }) { + val removedParamsFromUrl = word.split("?")[0].lowercase() + if (imageExtensions.any { removedParamsFromUrl.endsWith(it) }) { AsyncImage( model = word, contentDescription = word, @@ -171,7 +172,8 @@ fun ZoomableImageDialog(imageUrl: String, allImages: List = listOf(image @Composable private fun RenderImageOrVideo(imageUrl: String) { - if (imageExtensions.any { imageUrl.endsWith(it, true) }) { + val removedParamsFromUrl = imageUrl.split("?")[0].lowercase() + if (imageExtensions.any { removedParamsFromUrl.endsWith(it) }) { AsyncImage( model = imageUrl, contentDescription = stringResource(id = R.string.profile_image),