Adds the Namecoin diagnostics card that Android renders below the
per-server test results to the desktop settings panel, so support
requests carry the same information on both platforms.
- last test timestamp + pass/fail tally
- host OS (name/version/arch) — desktop equivalent of Android's
Build.MANUFACTURER/MODEL row
- JVM name + version — desktop equivalent of Android's API level row
- distinct TLS versions observed during the test run
Also adds a 'Testing next server...' inline progress row matching the
Android section's behaviour when the test loop is mid-run.
Pure UI addition; no model, preference, or callback changes.
Addresses review feedback on PR #3068: keep the entire NamecoinCoreRpcClient
bootstrap (config push + pinned-cert load) inside the single applicationIOScope
launch instead of doing setConfig synchronously in the by-lazy block. Matches
the ElectrumXClient init shape above.
The local-cache suggestion dropdown can momentarily show a stale match
when the user types a .bit identifier. For example, after resolving
"m@testls.bit" earlier in the session, typing the bare host
"testls.bit" causes findUsersStartingWith() to return the cached
m@... profile first; the correct _@testls.bit profile only appears a
few seconds later when ElectrumX resolution finishes. There is no
visual hint that a Namecoin lookup is in flight, and on hard failures
(timeout, malformed record, no nostr field, etc.) the user gets no
feedback at all.
This adds a dedicated NamecoinResolutionRow composable mounted between
the recipient field and the local-cache dropdown:
- "Resolving <name> on Namecoin…" spinner while the on-chain
lookup is in flight (after a 300 ms debounce that matches the
dropdown's own debounce).
- On success, a tappable row showing the resolved profile with a
distinct "Namecoin" badge (MaterialSymbols.Link), so the user
can pick the on-chain-verified profile unambiguously.
- On failure, a single explanatory error line covering all
NamecoinResolveOutcome variants the resolver already produces:
NameNotFound, NoNostrField, MalformedRecord (with the underlying
parser error verbatim), ServersUnreachable, InvalidIdentifier and
Timeout.
State is held in the shared NamecoinResolveState sealed class already
used by NamecoinNameService and the desktop SearchScreen — no new
state model is introduced. A small mapOutcomeToResolveState() helper
mirrors the same wording desktop ships, so all Namecoin surfaces
produce the same diagnostic string for the same outcome.
The row owns its own LaunchedEffect keyed on the typed query, so it
cancels in-flight lookups whenever the user keeps typing, and it
unmounts cleanly once a recipient is selected. It uses the existing
Amethyst.instance.namecoinResolver instance, so no DI plumbing or new
network calls beyond what was already wired up for .bit resolution.
The dropdown is left untouched: local-cache results are still valid
hits (just not necessarily the *intended* on-chain identity), so they
remain available below the new row.
Pure helpers (looksLikeNamecoinIdentifier + the outcome-to-state
mapper) are covered by unit tests in NamecoinResolutionRowTest.
Currently the only profile fields with an explicit copy affordance are
npub and nprofile (small ContentCopy IconButtons next to the keys).
The other identifier-shaped fields rendered by DrawAdditionalInfo and
DisplayLNAddress (NIP-05 Nostr Address, Website URL, lud16 Lightning
Address, NIP-39 external identities, NIP-A3 payment targets) all have
a tap action that *uses* the value (opens the URL, expands the zap
sheet, etc.) but no way to actually copy the value itself short of
selecting the underlying "View source" or opening the link and
copying from the browser. Closes that gap with the platform-native
long-press gesture.
Adds a small reusable LongPressCopyText composable in
ui/components/util/ that:
- renders plain Text styled in primary colour (visual match for the
previous ClickableTextPrimary),
- wires onClick through Modifier.combinedClickable (so existing tap
behaviour is preserved),
- adds onLongClick to copy the supplied raw value to the system
Clipboard and surface a Toast.makeText "Copied to clipboard"
confirmation,
- announces the long-press action to TalkBack via onLongClickLabel.
The composable deliberately uses a plain Text + outer
combinedClickable rather than an AnnotatedString with an inline
LinkAnnotation.Clickable: an annotation-level click can't see a
parent combinedClickable's long-press, because the parent's tap area
sits above the annotation's hit-test region and would consume the
tap before the annotation could fire it. Plain Text + outer modifier
keeps both gestures behaving correctly.
Wired into:
- DisplayNip05ProfileStatus (NIP-05 Nostr Address) — tap opens the
domain in the browser, long-press copies the full
user@domain value.
- The website row in DrawAdditionalInfo — tap opens the URL,
long-press copies the *raw* website value as stored in the
profile (the displayed form is stripped of the scheme + trailing
slash for readability; the copy preserves the original).
- The external-identity rows (Twitter, Mastodon, Telegram, GitHub)
— tap opens the proof URL, long-press copies the identity handle.
- DisplayLNAddress lud16 — tap toggles the zap sheet (unchanged),
long-press copies the lud16 address.
- PaymentTargetRow authority — tap opens the payto: URI, long-press
copies the authority.
The existing inline ContentCopy IconButtons on npub and nprofile are
left in place; the long-press gesture is purely additive for the
other rows.
Two notable side-effects from the ClickableTextPrimary -> Text +
combinedClickable rewrite:
- Touch ripple feedback now appears on tap (combinedClickable's
default indication). Previously the inline LinkAnnotation rendered
no ripple. This is consistent with how other clickable rows in the
app behave and provides better tap feedback.
- The NIP-05 row no longer renders the click region as a styled
inline span; the entire text behaves as one tap+long-press target.
Visually identical for short addresses; for any address long
enough to wrap, the click region now matches the visible bounds
rather than the per-glyph bounds.
Adds a new copied_to_clipboard string. Picks up the existing
copy_to_clipboard string for the TalkBack long-press hint.
The Send-onchain-zap Recipient field already supported typing a NIP-05
or bare .bit name and tapping the resolved suggestion. If a user typed
a fully-qualified name (e.g. "m@testls.bit" or "testls.bit") and hit
Send without first tapping the suggestion, the eager resolver only ran
decodePublicKeyAsHexOrNull(), which returns null for non-npub inputs,
so the button stayed disabled with no inline feedback.
Mirror the dropdown's existing NIP-05 / .bit resolution into the dialog
state via UserSuggestionState.nip05ResolutionFlow, and use the resolved
User's pubkey as one more fallback in resolvedRecipient. The flow is
already debounced (300ms) and shared with the suggestion list, so this
adds zero extra network traffic and zero new code paths -- just enables
Send 300ms after a valid name resolves.
Behaviour:
- Typing "m@testls.bit" + waiting 300ms -> Send enables.
- Typing "testls.bit" + waiting 300ms -> Send enables (same path the
dropdown uses for bare .bit names; resolves as _@testls.bit).
- Typing an npub or hex pubkey -> unchanged (already worked).
- Typing an unrecognised string -> Send stays disabled, unchanged.
- Tapping the suggestion before Send -> unchanged (selectedUser wins).
No behaviour change anywhere outside the onchain zap dialog.
Adds CommunityRulesEvent and CommunityRulesValidator under
quartz/.../nip72ModCommunities/rules/, mirroring the existing definition/
and approval/ subpackages.
NIP-9A complements NIP-72: the freeform rules tag on kind:34550 keeps
the human-readable text, this new addressable kind:34551 event carries
the machine-readable companion. Rules cover per-kind allow-lists with
optional max-bytes and per-author-per-day quotas, per-pubkey allow/deny
overrides, an optional web-of-trust gate, a global event size cap, and
an anti-rollback ratchet for stolen-key recovery.
CommunityRulesValidator is pure logic (no I/O); WoT lookups are deferred
to the caller via a WotResolver functional interface so consumers can
plug in NIP-02 follow lists, NIP-85 trusted assertions, or any local
source.
14 commonTest cases covering each violation path and the explicit-allow
override over WoT gates.
Spec: https://github.com/nostr-protocol/nips/pull/2331
This commit only adds the protocol layer. UI integration (composer
validation, rules editor, feed filter) will land as separate PRs once
the NIP draft has had review feedback and a kind number is finalised.
The NamecoinNameService.init() singleton was the only consumer of
the companion object removed in the previous commit. The
namecoinNameService val itself was never referenced — namecoinResolver
is what's wired into Nip05Client.