mirror of
https://github.com/vitorpamplona/amethyst.git
synced 2026-10-06 03:38:23 +00:00
feat(compose): wire NIP-05 popover mentions to nostr:nprofile1…
When the author types a NIP-05 mention (full `m@testls.bit` form or bare `testls.bit` domain), the existing user-suggestion popover already resolves it asynchronously on Dispatchers.IO via Nip05Client (Namecoin for .bit, regular .well-known/nostr.json for everything else). On pick, however, the inserted token was `@npub1…`, losing the relay hints already harvested during resolution. Insert `nostr:${user.toNProfile()} ` for NIP-05-shaped picks so: * the send-time NewMessageTagger parses the bech32 inline through its existing `nprofile1` branch — no extra main-thread I/O, no per-word network round-trips that could compound into 30s+ stalls on dead servers; * relay hints harvested by nip05ResolutionFlow ride along in the nprofile; * the email-vs-mention call is the author's: typing `vitor@vitorpamplona.com` surfaces the suggestion but only commits as a mention when the author taps the row. Just typing the address sends as plain text. Non-NIP-05 picks (search by name, typed npub/nprofile, hex pubkey) keep the existing `@npub1…` insertion form. Also collapses the open-coded `Nip05Id("_", prefix.lowercase())` bare-domain synthesis onto Nip05Id.parseLenient so there's one place doing that conversion. Addresses review feedback on #3165: avoids the extra parser pass and the inline send-time resolve loop in NewMessageTagger by routing all NIP-05 mentions through the popover the author already uses.
This commit is contained in:
+42
-6
@@ -110,12 +110,16 @@ class UserSuggestionState(
|
||||
.map(::userSearchTermOrNull)
|
||||
.map { prefix ->
|
||||
if (prefix != null) {
|
||||
// NIP-05 resolution: user@domain or bare .bit domain
|
||||
// NIP-05 resolution: full `name@domain` form, or bare
|
||||
// `.bit` domain synthesised as the wildcard `_@domain`.
|
||||
// Bare DNS domains aren't accepted here on purpose: a
|
||||
// `.com`/`.io`/etc. that happens to host nostr.json is
|
||||
// ambiguous with a regular URL the user might be typing.
|
||||
val nip05 =
|
||||
if (prefix.contains('@')) {
|
||||
if (prefix.endsWith(".bit", ignoreCase = true) && !prefix.contains('@')) {
|
||||
Nip05Id.parseLenient(prefix)
|
||||
} else if (prefix.contains('@')) {
|
||||
Nip05Id.parse(prefix)
|
||||
} else if (prefix.endsWith(".bit", ignoreCase = true)) {
|
||||
Nip05Id("_", prefix.lowercase())
|
||||
} else {
|
||||
null
|
||||
}
|
||||
@@ -231,7 +235,7 @@ class UserSuggestionState(
|
||||
item: User,
|
||||
): TextFieldValue {
|
||||
val lastWordStart = message.selection.end - word.length
|
||||
val wordToInsert = "@${item.pubkeyNpub()} "
|
||||
val wordToInsert = mentionInsertion(word, item)
|
||||
|
||||
return TextFieldValue(
|
||||
message.text.replaceRange(lastWordStart, message.selection.end, wordToInsert),
|
||||
@@ -244,11 +248,43 @@ class UserSuggestionState(
|
||||
word: String,
|
||||
item: User,
|
||||
) {
|
||||
val wordToInsert = "@${item.pubkeyNpub()} "
|
||||
val wordToInsert = mentionInsertion(word, item)
|
||||
state.edit {
|
||||
val lastWordStart = selection.end - word.length
|
||||
replace(lastWordStart, selection.end, wordToInsert)
|
||||
selection = TextRange(lastWordStart + wordToInsert.length, lastWordStart + wordToInsert.length)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The token to insert into the message text when the author picks [item]
|
||||
* from the suggestion popover. When the author was typing a NIP-05
|
||||
* mention (full `m@testls.bit` or bare-domain `.bit` form), we insert
|
||||
* `nostr:nprofile1…` directly so the send-time tagger doesn't need to
|
||||
* re-resolve anything — it parses the bech32 inline via its existing
|
||||
* `nprofile1` branch, with no main-thread I/O. For every other path
|
||||
* (search by name, typed npub/nprofile, hex) we keep the existing
|
||||
* `@npub1…` form to preserve current behaviour.
|
||||
*
|
||||
* Pre-resolved NIP-05 hits already have their relay hints pushed into
|
||||
* the account cache by [nip05ResolutionFlow] before this runs, so
|
||||
* [User.toNProfile] picks them up automatically.
|
||||
*/
|
||||
private fun mentionInsertion(
|
||||
word: String,
|
||||
item: User,
|
||||
): String {
|
||||
val typed = userSearchTermOrNull(word)
|
||||
val wasNip05Mention =
|
||||
typed != null &&
|
||||
(
|
||||
(typed.endsWith(".bit", ignoreCase = true) && !typed.contains('@')) ||
|
||||
(typed.contains('@') && Nip05Id.parse(typed) != null)
|
||||
)
|
||||
return if (wasNip05Mention) {
|
||||
"nostr:${item.toNProfile()} "
|
||||
} else {
|
||||
"@${item.pubkeyNpub()} "
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user