mirror of
https://github.com/vitorpamplona/amethyst.git
synced 2025-07-13 22:04:33 +02:00
Creates links to njump when events can't be found on Amethyst
This commit is contained in:
@ -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()
|
||||||
|
@ -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,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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)
|
||||||
|
Reference in New Issue
Block a user