Creates links to njump when events can't be found on Amethyst

This commit is contained in:
Vitor Pamplona
2024-08-21 17:05:14 -04:00
parent a8a3c94115
commit 83f1e523ea
6 changed files with 88 additions and 43 deletions

View File

@ -25,6 +25,7 @@ import android.util.LruCache
import androidx.compose.runtime.Stable import androidx.compose.runtime.Stable
import androidx.lifecycle.LiveData import androidx.lifecycle.LiveData
import com.vitorpamplona.amethyst.service.checkNotInMainThread import com.vitorpamplona.amethyst.service.checkNotInMainThread
import com.vitorpamplona.amethyst.ui.note.njumpLink
import com.vitorpamplona.ammolite.relays.BundledUpdate import com.vitorpamplona.ammolite.relays.BundledUpdate
import com.vitorpamplona.ammolite.relays.Relay import com.vitorpamplona.ammolite.relays.Relay
import com.vitorpamplona.ammolite.relays.RelayStats import com.vitorpamplona.ammolite.relays.RelayStats
@ -82,7 +83,7 @@ class AntiSpamFilter {
logOffender(hash, event) logOffender(hash, event)
if (relay != null) { if (relay != null) {
RelayStats.newSpam(relay.url, "https://njump.me/${Nip19Bech32.createNEvent(event.id, event.pubKey, event.kind, relay.url)}") RelayStats.newSpam(relay.url, njumpLink(Nip19Bech32.createNEvent(event.id, event.pubKey, event.kind, relay.url)))
} }
liveSpam.invalidateData() liveSpam.invalidateData()

View File

@ -41,6 +41,7 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.takeOrElse import androidx.compose.ui.graphics.takeOrElse
import androidx.compose.ui.input.pointer.pointerInput import androidx.compose.ui.input.pointer.pointerInput
import androidx.compose.ui.platform.LocalUriHandler
import androidx.compose.ui.text.AnnotatedString import androidx.compose.ui.text.AnnotatedString
import androidx.compose.ui.text.Placeholder import androidx.compose.ui.text.Placeholder
import androidx.compose.ui.text.PlaceholderVerticalAlign import androidx.compose.ui.text.PlaceholderVerticalAlign
@ -59,7 +60,7 @@ import coil.compose.AsyncImage
import com.vitorpamplona.amethyst.model.Note import com.vitorpamplona.amethyst.model.Note
import com.vitorpamplona.amethyst.model.User import com.vitorpamplona.amethyst.model.User
import com.vitorpamplona.amethyst.ui.note.LoadChannel import com.vitorpamplona.amethyst.ui.note.LoadChannel
import com.vitorpamplona.amethyst.ui.note.toShortenHex import com.vitorpamplona.amethyst.ui.note.njumpLink
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
import com.vitorpamplona.quartz.encoders.HexKey import com.vitorpamplona.quartz.encoders.HexKey
import com.vitorpamplona.quartz.encoders.Nip19Bech32 import com.vitorpamplona.quartz.encoders.Nip19Bech32
@ -80,12 +81,12 @@ fun ClickableRoute(
nav: (String) -> Unit, nav: (String) -> Unit,
) { ) {
when (val entity = nip19.entity) { when (val entity = nip19.entity) {
is Nip19Bech32.NPub -> DisplayUser(entity.hex, nip19.additionalChars, accountViewModel, nav) is Nip19Bech32.NPub -> DisplayUser(entity.hex, nip19.nip19raw, nip19.additionalChars, accountViewModel, nav)
is Nip19Bech32.NProfile -> DisplayUser(entity.hex, nip19.additionalChars, accountViewModel, nav) is Nip19Bech32.NProfile -> DisplayUser(entity.hex, nip19.nip19raw, nip19.additionalChars, accountViewModel, nav)
is Nip19Bech32.Note -> DisplayEvent(entity.hex, null, nip19.additionalChars, accountViewModel, nav) is Nip19Bech32.Note -> DisplayEvent(entity.hex, null, nip19.nip19raw, nip19.additionalChars, accountViewModel, nav)
is Nip19Bech32.NEvent -> DisplayEvent(entity.hex, entity.kind, nip19.additionalChars, accountViewModel, nav) is Nip19Bech32.NEvent -> DisplayEvent(entity.hex, entity.kind, nip19.nip19raw, nip19.additionalChars, accountViewModel, nav)
is Nip19Bech32.NEmbed -> LoadAndDisplayEvent(entity.event, nip19.additionalChars, accountViewModel, nav) is Nip19Bech32.NEmbed -> LoadAndDisplayEvent(entity.event, nip19.additionalChars, accountViewModel, nav)
is Nip19Bech32.NAddress -> DisplayAddress(entity, nip19.additionalChars, accountViewModel, nav) is Nip19Bech32.NAddress -> DisplayAddress(entity, nip19.nip19raw, nip19.additionalChars, accountViewModel, nav)
is Nip19Bech32.NRelay -> { is Nip19Bech32.NRelay -> {
Text(word) Text(word)
} }
@ -127,11 +128,16 @@ private fun LoadAndDisplayEvent(
if (it != null) { if (it != null) {
DisplayNoteLink(it, event.id, event.kind, additionalChars, accountViewModel, nav) DisplayNoteLink(it, event.id, event.kind, additionalChars, accountViewModel, nav)
} else { } else {
val externalLink = event.toNIP19()
val uri = LocalUriHandler.current
CreateClickableText( CreateClickableText(
clickablePart = remember(event.id) { "@${event.toNIP19()}" }, clickablePart = "@$externalLink",
suffix = additionalChars, suffix = additionalChars,
route = remember(event.id) { "Event/${event.id}" }, maxLines = 1,
nav = nav, onClick = {
runCatching { uri.openUri(njumpLink(externalLink)) }
},
) )
} }
} }
@ -141,6 +147,7 @@ private fun LoadAndDisplayEvent(
fun DisplayEvent( fun DisplayEvent(
hex: HexKey, hex: HexKey,
kind: Int?, kind: Int?,
nip19: String,
additionalChars: String?, additionalChars: String?,
accountViewModel: AccountViewModel, accountViewModel: AccountViewModel,
nav: (String) -> Unit, nav: (String) -> Unit,
@ -149,11 +156,16 @@ fun DisplayEvent(
if (it != null) { if (it != null) {
DisplayNoteLink(it, hex, kind, additionalChars, accountViewModel, nav) DisplayNoteLink(it, hex, kind, additionalChars, accountViewModel, nav)
} else { } else {
val externalLink = njumpLink(nip19)
val uri = LocalUriHandler.current
CreateClickableText( CreateClickableText(
clickablePart = remember(hex) { "@${hex.toShortenHex()}" }, clickablePart = remember(nip19) { "@$nip19" },
suffix = additionalChars, suffix = additionalChars,
route = remember(hex) { "Event/$hex" }, maxLines = 1,
nav = nav, onClick = {
runCatching { uri.openUri(externalLink) }
},
) )
} }
} }
@ -218,6 +230,7 @@ private fun DisplayNoteLink(
@Composable @Composable
private fun DisplayAddress( private fun DisplayAddress(
nip19: Nip19Bech32.NAddress, nip19: Nip19Bech32.NAddress,
originalNip19: String,
additionalChars: String?, additionalChars: String?,
accountViewModel: AccountViewModel, accountViewModel: AccountViewModel,
nav: (String) -> Unit, nav: (String) -> Unit,
@ -245,21 +258,23 @@ private fun DisplayAddress(
} }
if (noteBase == null) { if (noteBase == null) {
if (additionalChars != null) { val uri = LocalUriHandler.current
Text(
remember { "@${nip19.atag}$additionalChars" }, CreateClickableText(
) clickablePart = "@$originalNip19",
} else { suffix = additionalChars,
Text( maxLines = 1,
remember { "@${nip19.atag}" }, onClick = {
) runCatching { uri.openUri(njumpLink(originalNip19)) }
} },
)
} }
} }
@Composable @Composable
public fun DisplayUser( public fun DisplayUser(
userHex: HexKey, userHex: HexKey,
originalNip19: String,
additionalChars: String?, additionalChars: String?,
accountViewModel: AccountViewModel, accountViewModel: AccountViewModel,
nav: (String) -> Unit, nav: (String) -> Unit,
@ -280,15 +295,16 @@ public fun DisplayUser(
userBase?.let { RenderUserAsClickableText(it, additionalChars, nav) } userBase?.let { RenderUserAsClickableText(it, additionalChars, nav) }
if (userBase == null) { if (userBase == null) {
if (additionalChars != null) { val uri = LocalUriHandler.current
Text(
remember { "@${userHex}$additionalChars" }, CreateClickableText(
) clickablePart = "@$originalNip19",
} else { suffix = additionalChars,
Text( maxLines = 1,
remember { "@$userHex" }, onClick = {
) runCatching { uri.openUri(njumpLink(originalNip19)) }
} },
)
} }
} }
@ -320,6 +336,26 @@ fun CreateClickableText(
fontSize: TextUnit = TextUnit.Unspecified, fontSize: TextUnit = TextUnit.Unspecified,
route: String, route: String,
nav: (String) -> Unit, nav: (String) -> Unit,
) {
CreateClickableText(
clickablePart,
suffix,
maxLines,
overrideColor,
fontWeight,
fontSize,
) { nav(route) }
}
@Composable
fun CreateClickableText(
clickablePart: String,
suffix: String?,
maxLines: Int = Int.MAX_VALUE,
overrideColor: Color? = null,
fontWeight: FontWeight? = null,
fontSize: TextUnit = TextUnit.Unspecified,
onClick: (Int) -> Unit,
) { ) {
val primaryColor = MaterialTheme.colorScheme.primary val primaryColor = MaterialTheme.colorScheme.primary
val onBackgroundColor = MaterialTheme.colorScheme.onBackground val onBackgroundColor = MaterialTheme.colorScheme.onBackground
@ -351,7 +387,8 @@ fun CreateClickableText(
ClickableText( ClickableText(
text = text, text = text,
maxLines = maxLines, maxLines = maxLines,
onClick = { nav(route) }, overflow = TextOverflow.Ellipsis,
onClick = onClick,
) )
} }

View File

@ -162,8 +162,8 @@ class MarkdownMediaRenderer(
} }
} else if (loadedLink?.nip19 != null) { } else if (loadedLink?.nip19 != null) {
when (val entity = loadedLink.nip19.entity) { when (val entity = loadedLink.nip19.entity) {
is Nip19Bech32.NPub -> renderObservableUser(entity.hex, richTextStringBuilder) is Nip19Bech32.NPub -> renderObservableUser(entity.hex, loadedLink.nip19.nip19raw, richTextStringBuilder)
is Nip19Bech32.NProfile -> renderObservableUser(entity.hex, richTextStringBuilder) is Nip19Bech32.NProfile -> renderObservableUser(entity.hex, loadedLink.nip19.nip19raw, richTextStringBuilder)
is Nip19Bech32.Note -> renderObservableShortNoteUri(loadedLink, uri, richTextStringBuilder) is Nip19Bech32.Note -> renderObservableShortNoteUri(loadedLink, uri, richTextStringBuilder)
is Nip19Bech32.NEvent -> renderObservableShortNoteUri(loadedLink, uri, richTextStringBuilder) is Nip19Bech32.NEvent -> renderObservableShortNoteUri(loadedLink, uri, richTextStringBuilder)
is Nip19Bech32.NEmbed -> renderObservableShortNoteUri(loadedLink, uri, richTextStringBuilder) is Nip19Bech32.NEmbed -> renderObservableShortNoteUri(loadedLink, uri, richTextStringBuilder)
@ -201,10 +201,11 @@ class MarkdownMediaRenderer(
fun renderObservableUser( fun renderObservableUser(
userHex: String, userHex: String,
nip19: String,
richTextStringBuilder: RichTextString.Builder, richTextStringBuilder: RichTextString.Builder,
) { ) {
renderInline(richTextStringBuilder) { renderInline(richTextStringBuilder) {
DisplayUser(userHex, null, accountViewModel, nav) DisplayUser(userHex, nip19, null, accountViewModel, nav)
} }
} }

View File

@ -111,7 +111,11 @@ private fun lightenColor(
} }
val externalLinkForUser = { user: User -> val externalLinkForUser = { user: User ->
"https://njump.me/${user.toNProfile()}" njumpLink(user.toNProfile())
}
val njumpLink = { nip19BechAddress: String ->
"https://njump.me/$nip19BechAddress"
} }
val externalLinkForNote = { note: Note -> val externalLinkForNote = { note: Note ->
@ -119,17 +123,17 @@ val externalLinkForNote = { note: Note ->
if (note.event?.getReward() != null) { if (note.event?.getReward() != null) {
"https://nostrbounties.com/b/${note.address().toNAddr()}" "https://nostrbounties.com/b/${note.address().toNAddr()}"
} else if (note.event is PeopleListEvent) { } else if (note.event is PeopleListEvent) {
"https://listr.lol/a/${note.address()?.toNAddr()}" "https://listr.lol/a/${note.address().toNAddr()}"
} else if (note.event is AudioTrackEvent) { } else if (note.event is AudioTrackEvent) {
"https://zapstr.live/?track=${note.address()?.toNAddr()}" "https://zapstr.live/?track=${note.address().toNAddr()}"
} else { } else {
"https://njump.me/${note.address()?.toNAddr()}" njumpLink(note.address().toNAddr())
} }
} else { } else {
if (note.event is FileHeaderEvent) { if (note.event is FileHeaderEvent) {
"https://filestr.vercel.app/e/${note.toNEvent()}" "https://filestr.vercel.app/e/${note.toNEvent()}"
} else { } else {
"https://njump.me/${note.toNEvent()}" njumpLink(note.toNEvent())
} }
} }
} }

View File

@ -257,7 +257,7 @@ fun DisplayEntryForNote(
style = LocalTextStyle.current.copy(color = MaterialTheme.colorScheme.primary), style = LocalTextStyle.current.copy(color = MaterialTheme.colorScheme.primary),
) )
} else { } else {
DisplayEvent(noteEvent.id, noteEvent.kind, "", accountViewModel, nav) DisplayEvent(noteEvent.id, noteEvent.kind, noteEvent.toNIP19(), null, accountViewModel, nav)
} }
} }

View File

@ -57,6 +57,7 @@ object Nip19Bech32 {
@Immutable @Immutable
data class ParseReturn( data class ParseReturn(
val entity: Entity, val entity: Entity,
val nip19raw: String,
val additionalChars: String? = null, val additionalChars: String? = null,
) )
@ -138,7 +139,8 @@ object Nip19Bech32 {
additionalChars: String?, additionalChars: String?,
): ParseReturn? = ): ParseReturn? =
try { try {
val bytes = (type + key).bechToBytes() val nip19 = (type + key)
val bytes = nip19.bechToBytes()
when (type.lowercase()) { when (type.lowercase()) {
"nsec1" -> nsec(bytes) "nsec1" -> nsec(bytes)
@ -151,7 +153,7 @@ object Nip19Bech32 {
"nembed1" -> nembed(bytes) "nembed1" -> nembed(bytes)
else -> null else -> null
}?.let { }?.let {
ParseReturn(it, additionalChars) ParseReturn(it, nip19, additionalChars)
} }
} catch (e: Throwable) { } catch (e: Throwable) {
Log.w("NIP19 Parser", "Issue trying to Decode NIP19 $key: ${e.message}", e) Log.w("NIP19 Parser", "Issue trying to Decode NIP19 $key: ${e.message}", e)