* 'main' of https://github.com/vitorpamplona/amethyst: (35 commits)
New Crowdin translations by GitHub Action
fix chess tests
Replace subsequent checks with '!isNullOrEmpty()' call
use forEach: no intermediate list created.
Use filterIsInstance to avoid dual iteration
Lambda argument should be moved out of parentheses
replace null checks with Elvis
merge call chain with flatMap (slight performance improvement)
New Crowdin translations by GitHub Action
New Crowdin translations by GitHub Action
update cz, pt, de, sv
was this meant to be withContext?
add names to boolean params
remove unused imports
Explicit type arguments can be inferred
remove redundant modifiers
correct package name
If-Null return/break/... foldable to '?:'
Declaration has type inferred from a platform call, which can lead to unchecked nullability issues. Specify type explicitly as nullable or non-nullable.
Declaration has type inferred from a platform call, which can lead to unchecked nullability issues. Specify type explicitly as nullable or non-nullable.
...
# Conflicts:
# amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/settings/ReactionsSettingsScreen.kt
Add NamecoinLookupException sealed class to distinguish:
- NameNotFound: name queried successfully but doesn't exist on blockchain
- NameExpired: name exists but expired (>36000 blocks since last update)
- ServersUnreachable: all ElectrumX servers failed with connection errors
Previously, all three cases returned null from nameShowWithFallback,
making it impossible for the UI to show meaningful feedback.
Changes:
- ElectrumxClient.connectAndQuery: throws NameNotFound/NameExpired
instead of returning null for definitive blockchain answers
- ElectrumxClient.nameShow: lets NamecoinLookupException propagate
(only catches connection/IO errors as null)
- ElectrumxClient.nameShowWithFallback: short-circuits on NameNotFound/
NameExpired (no point trying other servers for definitive answers),
throws ServersUnreachable when all servers fail with connection errors
- SearchBarViewModel: new namecoinSearchState flow with typed states
(Idle/Loading/Resolved/NotFound/Expired/ServersUnreachable) for UI
to show loading spinners, resolved profiles, and specific error
messages. Derived namecoinResolvedUser from this flow.
Discovered while porting to notedeck (damus-io/notedeck#1314).
Fixes discovered while porting Namecoin NIP-05 to Primal Android
(PrimalHQ/primal-android-app#934):
1. Race condition: stale intermediate lookups overwrite final result
- Changed map to mapLatest in SearchBarViewModel so that previous
in-flight ElectrumX lookups are cancelled when the query changes
- Without this, typing 'd/testls' character by character causes
stale results from 'd/test', 'd/testl' etc. to arrive after
the correct 'd/testls' result and overwrite it with null
2. CancellationException swallowed in catch block
- mapLatest cancels previous coroutines via CancellationException
- The generic catch was catching it and emitting null, wiping the
valid result. Now re-throws CancellationException
3. Root lookup fails when names object has no underscore key
- extractFromDomainValue tried names[localPart] then names[underscore]
which is the same thing for root lookups
- Now falls back to first available entry for root lookups when
no underscore key exists
- Non-root lookups still fail correctly (no false matches)
4. Global Mutex serializes all lookups
- Single Mutex in ElectrumxClient blocked unrelated lookups to
different servers. A 25s timeout on one server stalled everything
- Now uses per-server mutexes via ConcurrentHashMap
5. customServers set but never read
- NamecoinNameService.setCustomServers() stored the list but the
resolver was constructed with the default serverListProvider
- Now wires customServers through to the resolver
6. resolveLive uses orphaned CoroutineScope
- Now accepts an optional external scope parameter so callers can
tie resolution to their own lifecycle
Adds 2 new tests: root fallback to first entry, non-root no-fallback.
- CalendarDateSlotEvent (31922): Add title, summary, image, geohash,
hashtags, participants, references, multiple locations accessors and
build() method with DSL builder pattern
- CalendarTimeSlotEvent (31923): Fix timezone bug (startTzId/endTzId
were parsed as Long instead of String), add all accessors like
DateSlot, rename startTmz/endTmz to startTzId/endTzId, add build()
- CalendarRSVPEvent (31925): Remove incorrect location/start/end
accessors (not in spec), add status/freebusy/calendarEventAddress/
calendarEventId/calendarEventAuthor accessors, update from outdated
L/l label format to current status/fb tag format, add build()
- CalendarEvent (31924): Add title and calendarEventAddresses accessors,
add build() method
- Create NIP-52 tag classes: LocationTag, RSVPStatusTag (with enum),
FreeBusyTag (with enum)
- Create TagArrayBuilderExt.kt with DSL builder extensions for all
four calendar event types
https://claude.ai/code/session_01UMXgE3tNftmqNZqcYwF2kL
Reorganize nip64Chess event classes into individual packages with
dedicated tag classes, TagArrayBuilderExt, and TagArrayExt files,
following the same structure used by nip88Polls.
New package structure:
- challenge/ - LiveChessGameChallengeEvent with PlayerColorTag, TimeControlTag
- accept/ - LiveChessGameAcceptEvent with ChallengeEventTag
- move/ - LiveChessMoveEvent with GameIdTag, MoveNumberTag, SanTag, FenTag
- end/ - LiveChessGameEndEvent with ResultTag, TerminationTag, WinnerTag
- draw/ - LiveChessDrawOfferEvent
- game/ - ChessGameEvent (Kind 64)
- jester/ - JesterEvent, JesterProtocol, JesterContent, JesterGameEvents
Each tag class follows the companion object pattern with isTag(),
parse(), and assemble() methods. Each event package includes
TagArrayBuilderExt for building and TagArrayExt for parsing.
https://claude.ai/code/session_01Qtzhka3p5N3Hbns4xrRbsv
- Add ExternalIdentitiesEvent (kind 10011) to quartz/nip39ExtIdentities
with createNew/updateFromPast factory methods and identityClaims()
- Generalize TagArrayBuilderExt to work with any Event type (not just
MetadataEvent) so claims/twitterClaim etc. work with both kinds
- Extract replaceClaims logic to List<IdentityClaimTag>.replaceClaims()
shared extension used by both MetadataEvent and ExternalIdentitiesEvent
- Register ExternalIdentitiesEvent in EventFactory (kind 10011)
- Add consume(ExternalIdentitiesEvent) to LocalCache and dispatch in
justConsumeInnerInner
- Add ExternalIdentitiesEvent.KIND to UserMetadataForKeyKinds relay filter
so kind 10011 is fetched alongside kind 0 when loading user profiles
- Add UserExternalIdentitiesViewModel that observes the kind 10011
AddressableNote for a user and exposes List<IdentityClaimTag> as a Flow
- Thread UserExternalIdentitiesViewModel through ProfileScreen →
RenderScreen → ProfileHeader → DrawAdditionalInfo
- DrawAdditionalInfo now shows kind 10011 identities, falling back to
kind 0 for backwards compatibility
- UserMetadataState: add sendNewUserIdentities() for kind 10011 and
remove identity claim params from sendNewUserMetadata() (kind 0)
- NewUserMetadataViewModel: load identities from kind 10011 first
(fallback to kind 0), save identities to kind 10011 separately
https://claude.ai/code/session_017iU6ZdRu5qRXPCeDztVeeV
Adds full protocol support for NIP-C0 code snippet events following the
same structure used for NIP-88 polls.
Quartz (quartz/nipC0CodeSnippets/):
- CodeSnippetEvent (kind:1337) with accessors for all optional metadata
- TagArrayExt: parse language, extension, name, description, runtime,
license, deps (repeatable), and repo from tag arrays
- TagArrayBuilderExt: typed builder functions for CodeSnippetEvent
- Tag classes: LanguageTag (l), ExtensionTag, SnippetNameTag (name),
SnippetDescriptionTag (description), RuntimeTag, LicenseTag, DepTag,
RepoTag
EventFactory: register kind 1337 → CodeSnippetEvent
LocalCache: add consume(CodeSnippetEvent) and dispatch in when block
https://claude.ai/code/session_013ykLNfJNdwWpXh8ZhZaSXY
Query current block height via blockchain.headers.subscribe and reject
names that have expired (>= 36000 blocks since last update). Populate
expiresIn field in NameShowResult for downstream use.
Unconfirmed transactions (height <= 0) are treated as active.
- ElectrumxClient accepts injected SocketFactory (lambda) so connections
respect the user's Tor/proxy settings instead of leaking their IP
through raw sockets
- Add ProxiedSocketFactory for SOCKS5 proxy routing
- Add .onion ElectrumX server as primary when Tor is enabled, with
electrumx.testls.space as clearnet fallback
- NamecoinNameResolver accepts serverListProvider lambda for dynamic
server selection based on current Tor settings
- RoleBasedHttpClientBuilder.socketFactoryForNip05() bridges Amethyst's
Tor settings to the socket factory
- NamecoinNameService now requires explicit init with proxy-aware client
- Remove dead code: Nip05NamecoinAdapter (never referenced),
NamecoinVerificationDisplay (never called)
- Update design documentation
Add censorship-resistant NIP-05 verification using the Namecoin blockchain.
Users can set their nip05 field to a .bit domain (e.g. alice@example.bit)
or direct Namecoin name (d/example, id/alice) and Amethyst will resolve
the pubkey mapping via ElectrumX instead of HTTP.
Resolution uses the standard Electrum protocol (scripthash-based lookups):
- Build canonical name index script matching ElectrumX-NMC indexing
- Query blockchain.scripthash.get_history for the name's tx history
- Parse NAME_UPDATE script from the latest transaction output
- Extract Nostr pubkey from the name's JSON value
Supports both d/ (domain) and id/ (identity) Namecoin namespaces,
simple and extended NIP-05-like value formats with relay hints,
LRU caching with 1h TTL, and self-signed TLS certificates.
New files:
- quartz: ElectrumxClient, NamecoinNameResolver, NamecoinLookupCache
- amethyst: NamecoinNameService, Nip05NamecoinAdapter, NamecoinVerificationDisplay
- docs: namecoin-nip05-design.md
- tests: NamecoinNameResolverTest
Modified:
- Nip05Client: optional namecoinResolver routes .bit to blockchain
- AppModules: wire up resolver
See docs/namecoin-nip05-design.md for full architecture and protocol details.