Adds 124 missing translations across cs-rCZ, pt-rBR, sv-rSE, and de-rDE
covering the new Nests (NIP-53 audio-rooms) UI, AI-suggested alt-text
hints, tracked broadcasts setting, and meeting/live-stream tags.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Display names weren't showing because metadata was never requested for
account pubkeys. Now:
1. LaunchedEffect triggers loadMetadataForPubkeys() for all accounts
in the switcher once relays connect
2. metadataVersion collector persists display names for ALL accounts
(not just active one) when metadata arrives from relays
3. Names available immediately on next dropdown open
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
saveCurrentAccount() previously returned failure for read-only and
bunker accounts, preventing them from being added to multi-account
storage. Now:
- Read-only (npub): skips private key save, still saves to storage
- Bunker: saves to storage (private key already saved during login)
- Internal (nsec): saves private key + storage (unchanged)
This was the root cause of npub-only accounts not appearing in the
switcher and accounts being lost when adding via AddAccountDialog.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
DesktopAccountStorage now caches AccountMetadata in memory after first
read. Subsequent loadAccounts/saveAccount/setCurrentAccount serve from
cache, only hitting disk on writes. Also caches AES encryption key to
avoid repeated OS keychain lookups.
Before: 3 decrypts + 1 encrypt + 4 keychain calls per login/switch
After: 1 decrypt + 1 keychain call on first access, then memory only
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Root cause of accounts not persisting:
ensureCurrentAccountInStorage() was fire-and-forget (scope.launch),
so loginWithKey() ran before the old account was saved. The old account
was lost. Now all steps run sequentially in one coroutine.
UI hanging fix:
metadataVersion LaunchedEffect fired on every metadata event, doing
encrypted file I/O each time. Now debounced with 2s delay so it only
writes once after a batch of metadata settles.
Also:
- loadInternalAccount falls back to read-only (test updated)
- bunker/nostrconnect paths also await ensureCurrentAccountInStorage
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Three fixes:
1. Display names now stored in AccountInfo/AccountInfoDto and persisted
in encrypted storage. Populated when metadata arrives from relays via
metadataVersion LaunchedEffect. Available immediately on next open.
2. loadInternalAccount falls back to loadReadOnlyAccount when no private
key found in SecureKeyStorage — fixes npub-only accounts that were
saved as SignerType.Internal from earlier sessions.
3. Dropdown uses persisted displayName first, cache lookup as fallback.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Root cause of npub-only switch failure:
loginWithKey() with pubkey created AccountState with signerType=Internal
(default). switchAccount then called loadInternalAccount which requires
a private key from SecureKeyStorage → failed. Now sets ViewOnly.
Display names not showing:
- resolveDisplayName() ran at composition time but metadata hadn't
loaded from relays yet. Dropdown never recomposed when it arrived.
- Added metadataVersion counter to DesktopLocalCache, incremented on
each consumeMetadata(). Dropdown collects it to trigger recomposition
when user names become available.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Add loadReadOnlyAccount() for ViewOnly signer type switching
(was mapping to loadInternalAccount which requires private key)
- Remove remember() from display name resolution — now re-evaluates
each recomposition so names appear once metadata loads from relays
- Only show subtitle (npub row) when display name is available;
if no display name, show npub as title only
- Signer type badge shown as subtitle when no display name
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Account switcher dropdown improvements:
- Two-row display: Display Name on top, npub (middle-truncated) below
e.g. 'Alice' / 'npub1abc...wxyz · Bunker'
- Middle-truncation for npub: shows first 10 + last 6 chars
- Resolves display names from DesktopLocalCache user metadata
- Confirmation dialog also shows display name
- npub-only (view-only) accounts now persist to encrypted storage
(ensureCurrentAccountInStorage called in onLoginSuccess)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
`subscribeSpeaker_survives_session_swap` and
`unsubscribe_before_session_swap_releases_handle` had two race
conditions exposed only on macOS scheduling:
1. ScriptedListener.subscribeCount is incremented inside
opener(listener) — BEFORE the wrapper's pump reaches
`handle.objects.collect { frames.emit(it) }`. Waiting on
subscribeCount alone doesn't prove the pump is collecting
from first.frames. An emit upstream before the pump's collect
registers is silently dropped.
2. The wrapper's outer `frames` SharedFlow is replay=0. The
`scope.async { take(2).toList() }` consumer might not have
subscribed by the time the test thread races into the first
emit, dropping the value.
Both are fixed by waiting for actual subscription registration:
- first/second.frames.subscriptionCount.first { it > 0 } — flips
to 1 only after the pump's collect lands, which is strictly
after liveHandleRef.set(handle).
- onSubscription + CompletableDeferred — fires after the
consumer's collector is registered against the SharedFlow.
The triple benchmark probed libschnorr256k1 with a try/catch that only
handled UnsatisfiedLinkError. On Windows the loader raises
IllegalStateException when the JNI .dll for the platform isn't packaged,
which propagated out as a test failure instead of skipping the C row.
Mirror the broader catch already used in Secp256k1CrossValidationTest so
the benchmark prints a SKIP-style note and continues with the ACINQ +
Kotlin rows on platforms where the C lib isn't available.
Activity used the system default soft-input behavior (pan), so opening
the keyboard slid the whole screen up, partially hiding the composer
and putting the top of the screen out of reach. Set adjustResize on
NestActivity and add consumeWindowInsets + imePadding to the Scaffold
body so the column reflows above the IME and the textfield stays in
view.
Bring the audio-room lobby filter closer to the NostrNests reference:
- Reject service/endpoint URLs that aren't HTTPS, dropping legacy
`wss+livekit://` rooms a moq-lite client can't reach.
- Drop PLANNED rooms whose `starts` is more than 1 h in the past or
more than 30 d in the future.
- Add a single lobby-wide kind-10312 REQ (10 min, limit 500) per
active relay and use it to hide OPEN/PRIVATE rooms whose host
crashed without flipping status to CLOSED. Brand-new rooms keep
showing for the freshness window so they don't pop in late.
- Expand follow-style top filters to include rooms whose p-tagged
speakers are followed, not just the host.
The bottom action bar in NestFullScreen was rendering under Android's
gesture navigation area in edge-to-edge mode, leaving the leave / talk
/ react buttons partially obscured. Apply BottomAppBarDefaults
windowInsets to the surface so it pads above the system navigation.
The Android emulator (and a number of dual-stack networks where the
IPv6 path is broken) advertises working v6 connectivity but silently
drops outbound packets. With the JDK / RFC 6724 default of AAAA-first,
any host that publishes a stale or unreachable AAAA record — e.g.
moq.nostrnests.com, whose Linode v6 currently doesn't accept
connections — causes every HTTPS call and every QUIC handshake to
fast-fail with ConnectException, parking the Nests room screen on
"Reconnecting" indefinitely.
Plug a small Dns wrapper into both OkHttp factories that puts
Inet4Address entries ahead of v6 in the resolved list (v6 stays as
fallback for genuinely v6-only hosts), and switch UdpSocket.connect
from getByName to getAllByName + IPv4-first selection so the QUIC
audio path makes the same choice as the HTTPS auth POST.
Pulls in the per-platform JNI shards (linux-x86_64, linux-aarch64,
darwin-x86_64, darwin-aarch64) that the upstream library now publishes as
runtime dependencies of schnorr256k1-kmp-jvm. The 1.0.3 release also
moved the groupId to com.vitorpamplona.schnorr256k1.
Net effect: Secp256k1CrossValidationTest's customCImplementationMatchesAcinqAndKotlin
no longer prints SKIP, and the Custom C(JNI) column in
Secp256k1TripleBenchmark now reports numbers (verifySchnorr ~25.6k ops/s,
signSchnorr cached pk ~57.4k ops/s) instead of (N/A). 188/188 tests pass
in the secp256k1 suite on a stock JVM with no LD_LIBRARY_PATH or
-Djava.library.path needed.
https://claude.ai/code/session_01XEfLBStDum7h8Brwo7qFXt
The public nostrnests deployment serves moq-auth + moq-relay on
:4443; :443 is not the live endpoint. With the old defaults a
freshly-created room could never mint a JWT, so the room screen
parked on "Reconnecting" forever and the room never showed up in
the nostrnests UI.
Move the LIVE chip and listener count out of the header strip and into
the right side of the Stage card's title row (listeners first, then
chip). The remaining summary now hides by default and toggles when the
user taps the room title in the top bar, reclaiming vertical space for
the stage and chat.
Lets a clustered burst of mint calls (typical interop test scenario,
also any moq-auth deployment with stricter rate limits) outlast the
60 s rate-limit window instead of cascading into a hard failure.
- Retry up to 7 times on HTTP 429. Worst-case total wait at 1 s
initial + 16 s cap is 1+2+4+8+16+16+16 = 63 s — just past the
reference moq-auth 60 s/IP window.
- Respect the `Retry-After` header (delta-seconds OR HTTP-date)
when present; fall back to capped exponential backoff otherwise.
- Re-sign the NIP-98 auth event on every attempt. The reference
validator accepts a 60 s validity window; without re-signing,
any retry that waits past that window fails 401 "Event too old".
- Suspending `delay` so coroutine cancellation tears the loop down
cleanly.
`./gradlew :nestsClient:jvmTest -DnestsInterop=true -DnestsInteropExternal=true`
now goes green in a single invocation: 157 tests across 33 classes,
0 failures. Previously the same command cascaded 11 failures once
the moq-auth 20/min/IP bucket filled.
Unit tests: 7 new cases covering Retry-After parsing (seconds,
HTTP-date, garbage, missing), exponential cap, the 429-then-200
retry loop, max-retries exhaustion, and that non-429 4xx is NOT
retried. Backed by a tiny ServerSocket-based HTTP fake so no new
test deps.