Commit Graph
14185 Commits
Author SHA1 Message Date
m b5b70fe693 feat(desktop): persist TOFU-pinned Namecoin ElectrumX certs
Mirrors Android's NamecoinSharedPreferences pinned-cert API on Desktop so
user-accepted TLS pins survive process restart. Same JSON-list shape, same
distinct-append semantics, same wipe-on-reset behaviour.

What's new
- DesktopNamecoinPreferences gains addPinnedCert / loadPinnedCerts /
  clearPinnedCerts (sync rather than suspend, since java.util.prefs is
  synchronous). reset() now clears pinned certs too, matching Android.
- DesktopNamecoinNameService accepts a pinnedCertsProvider and pushes the
  loaded list into ElectrumXClient.setDynamicCerts at init, mirroring
  Android's AppModules.kt wiring. Exposes the underlying client so the
  Settings UI can call testServer() and re-apply pins live.
- Desktop NamecoinSettingsSection grows an optional Test Connection + TOFU
  pin sub-section: runs ElectrumXClient.testServer per active server,
  collects PEM + SHA-256 fingerprint from successful TLS handshakes, and
  prompts the user to pin each new cert via AlertDialog. UI hidden when
  no service is wired (so existing call sites stay valid).
- Main.kt wires both halves together and updates the freshly-pinned cert
  list into the live client without waiting for restart.

Persistence is plain java.util.prefs (same backing store as the rest of
DesktopNamecoinPreferences) — explicitly NOT EncryptedSharedPreferences.
Pinned cert PEMs are public material; no secrets stored.

Tests
- DesktopNamecoinPreferencesTest: +6 cases covering empty default,
  persistence + reload, dedup, blank input ignored, reset wipes, and
  independence from settings copies.

Verification
- ./gradlew :desktopApp:compileKotlin — BUILD SUCCESSFUL
- ./gradlew :desktopApp:test — BUILD SUCCESSFUL (16 tests, 0 failures)
- ./gradlew :amethyst:compilePlayDebugKotlin — BUILD SUCCESSFUL
- ./gradlew :amethyst:spotlessCheck :commons:spotlessCheck :desktopApp:spotlessCheck — BUILD SUCCESSFUL

Stack note
Stacked behind #3072 (merged 2026-05-27). Next: PR-C for the full
Namecoin Core RPC backend + composite fallback persistence + UI.
2026-05-28 05:41:22 +10:00
Vitor PamplonaandGitHub 80991c6c8b Merge pull request #3074 from vitorpamplona/claude/gallant-darwin-VfniN
Fix route hint re-firing on activity recreation
2026-05-27 15:33:28 -04:00
Vitor PamplonaandGitHub e937b460e1 Merge pull request #3072 from mstrofnone/feat/commons-namecoin-settings-extension
refactor(namecoin): consolidate NamecoinSettings into commons
2026-05-27 15:31:19 -04:00
Claude 187cb407c9 fix(new-user): stop Import Follow List from reappearing after dismiss
The route hint set by newKey() (Route.ImportFollowsSelectUser) was stored
on AccountState.LoggedIn and never cleared. LoggedInPage re-assigned
accountViewModel.firstRoute = route on every recomposition, so any
Activity recreation (rotation, dark-mode toggle, returning from
background) made the screen pop up again even after the user dismissed
it via popBack.

Consume the route once via remember(state) in AccountScreen, and gate the
firstRoute assignment behind LaunchedEffect(Unit) so it only fires once
per composition lifetime.
2026-05-27 19:26:28 +00:00
Vitor PamplonaandGitHub 58f1ee9606 Merge pull request #3069 from vitorpamplona/l10n_crowdin_translations
New Crowdin Translations
2026-05-27 15:07:14 -04:00
Vitor PamplonaandGitHub 63b91012fb Merge pull request #3071 from vitorpamplona/claude/trusting-mccarthy-SA1aI
Add Software Apps feed with follow list filtering
2026-05-27 15:07:05 -04:00
m 67edb32fa7 refactor(namecoin): consolidate NamecoinSettings into commons
Two NamecoinSettings classes had drifted:

- commons (used by Desktop): only enabled + customServers
- amethyst service.namecoin (used by Android): full schema with backend,
  namecoinCoreRpc, fallbackToCustomElectrumx, fallbackToDefaultElectrumx

This left Desktop unable to persist any of the Namecoin Core RPC or
fallback-policy state introduced in the Android settings UI. Promote
the rich Android version into commons as the single source of truth
and delete the Android duplicate.

- Move the rich schema (backend, namecoinCoreRpc, fallback toggles,
  hasUsableCoreRpc, toFallbackPolicy) into the commons NamecoinSettings.
- Delete amethyst/service/namecoin/NamecoinSettings.kt and its test.
- Repoint the two Android imports (NamecoinSharedPreferences,
  NamecoinSettingsSection) at the commons class. No behaviour change on
  Android.
- Fold the Android-only backend/RPC/fallback test cases into the commons
  NamecoinSettingsTest so the shared schema stays covered.

Desktop persistence (DesktopNamecoinPreferences) still only reads/writes
enabled + customServers; the extra commons fields fall back to defaults
on the existing Desktop store. Wiring those new fields into Desktop is
the next change.
2026-05-28 05:06:44 +10:00
Vitor PamplonaandGitHub 7692bee0a5 Merge pull request #3070 from mstrofnone/fix/namecoin-search-id-d-prefixes
fix(search): route d/ and id/ Namecoin namespaces through the resolution row
2026-05-27 15:06:10 -04:00
m 216176eeb7 fix(search): route d/ and id/ Namecoin namespaces through the resolution row
The inline Namecoin resolution row in the global search bar (and the
on-chain zap recipient field) is gated by looksLikeNamecoinIdentifier,
which previously only matched the '.bit' shapes. Direct namespace
references like 'id/mstrofnone' or 'd/mstrofnone' — both accepted by
NamecoinNameResolver.isNamecoinIdentifier — fell through the gate and
the resolver was never called, so no on-chain feedback appeared in the
search bar.

Bring the UI gate in line with the resolver:

  - accept 'd/<name>' (domain namespace, direct reference)
  - accept 'id/<name>' (identity namespace)
  - lower the length floor so single-character labels (valid, if
    expensive, on chain) still trigger; '.bit' inputs keep their
    5-char floor

Doc comment for the row's behaviour and the NamecoinResolutionRow
KDoc are updated to reflect the broader accepted set. New unit tests
cover both new prefixes (with case-insensitivity and leading '@'),
short single-label names, bare-prefix rejection, and explicit
non-routing for other Namecoin namespaces ('a/', 'u/').
2026-05-28 04:46:55 +10:00
Claude 506ed94bfd fix(software-apps): add top-nav filter and route NIP-82 events through LocalCache
The Apps screen was missing the top-nav follow-list spinner that other
single-feed screens (Articles, Longs, Pictures) use, and nothing was
loading because the relay subscription only queried the user's own
outbox and `SoftwareApplicationEvent` (kind 32267) was never consumed
by `LocalCache.justConsumeInnerInner` — it fell through to the
"Event Not Supported" else branch and got dropped. `ReleaseArtifactSetEvent`
(kind 30063) and `SoftwareAssetEvent` (kind 3063) had the same gap.

- Register `SoftwareApplicationEvent`, `SoftwareAssetEvent`, and
  `ReleaseArtifactSetEvent` in the `LocalCache` consume dispatch.
- Add `defaultSoftwareAppsFollowList` setting (Account/Settings/Prefs)
  and `liveSoftwareAppsFollowLists{,PerRelay}` flows.
- Reshape `SoftwareAppsSubAssembler` to extend
  `PerUserAndFollowListEoseManager<_, TopFilter>` and assemble per-list
  relay filters via `makeSoftwareAppsFilter`, mirroring Pictures/Longs.
- Add a `SoftwareAppsTopBar` with the standard `FeedFilterSpinner` and
  a `WatchAccountForSoftwareAppsScreen` to invalidate the DAL when the
  selected list changes.
- DAL now applies `FilterByListParams` so the rendered feed honors the
  active top-nav list.
2026-05-27 18:25:39 +00:00
Crowdin Bot 2b6798e78a New Crowdin translations by GitHub Action 2026-05-27 18:25:22 +00:00
Vitor PamplonaandGitHub d5cca1e3e4 Merge pull request #3068 from mstrofnone/feat/namecoin-core-rpc-tofu
feat(namecoin): TOFU pin for Namecoin Core RPC TLS path
2026-05-27 14:23:25 -04:00
mstrofnone a1071b5189 fix(namecoin): move setConfig into the bootstrap launch
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.
2026-05-28 04:18:15 +10:00
m cfb3f1b9fa feat(namecoin): TOFU pin for Namecoin Core RPC TLS path
When the user picks the Namecoin Core RPC backend and points at a
self-hosted node behind a self-signed cert (StartOS / Start9, umbrel,
LAN reverse proxy, …) the previous flow only worked if the cert's CA
was already in the device trust store. There was no in-app way to
inspect or pin the certificate, so users had to install the StartOS
root CA at the OS level — or settle for an unencrypted onion path.

This change brings the Namecoin Core RPC path up to parity with the
existing ElectrumX path:

  - NamecoinCoreRpcClient.probe() now opens a short-lived, no-auth TLS
    socket alongside the JSON-RPC call to capture the server's leaf
    certificate (PEM + SHA-256 fingerprint). Capture is best-effort
    and only runs for https:// URLs. Credentials are never sent over
    the inspection socket.

  - RpcProbeResult exposes serverCertPem, certFingerprint, and
    tlsHandshakeFailed so the Settings UI can react. New fields are
    nullable / default false so existing callers compile unchanged.

  - NamecoinCoreRpcClient maintains its own dynamic-cert keystore and
    a lazy pinned SSLSocketFactory (same shape as ElectrumXClient's,
    minus the hardcoded list — Core RPC has no public defaults). When
    cfg.usePinnedTrustStore is true and the URL is https, callRpc()
    routes through the pinned factory with a permissive hostname
    verifier (LAN/onion certs commonly carry IP-only SANs).

  - NamecoinSettingsSection's Namecoin Core RPC card now shows a
    'Trust Server Certificate?' AlertDialog after Test RPC when the
    probe captured a cert and the user hasn't pinned yet, reusing the
    existing namecoin_pin_cert_* strings. Accept persists the PEM
    AND flips usePinnedTrustStore=true on the config. The result
    card also displays the captured fingerprint and a '(pinned)'
    marker so the user can see the current trust state at a glance.

  - The pinned PEM list is stored in the existing
    KEY_PINNED_CERTS DataStore entry, so a single TOFU confirmation
    covers both backends. AppModules' namecoinCoreRpcClient init now
    bootstraps the pinned list on app start, matching ElectrumX.

  - Tests cover the new probe fields' defaults and the addPinnedCert
    / setDynamicCerts surface.

Local verification: builds clean (assembleFdroidDebug), :quartz:jvmTest
NamecoinCoreRpcClientTest all green, :amethyst:testFdroidDebugUnitTest
namecoin suites all green.
2026-05-28 03:32:28 +10:00
Vitor PamplonaandGitHub 1cd57bd497 Merge pull request #3066 from vitorpamplona/claude/sweet-maxwell-UMahG
fix(playback): hide video controls while the error overlay is shown
2026-05-27 11:27:18 -04:00
Claude b2c76b7428 fix(playback): hide video controls while the error overlay is shown
Tapping the player while the codec-not-supported overlay is up would still
toggle the gradients/buttons in. Gate the controls block on a clear error
state so the overlay stays the only thing on screen.
2026-05-27 15:13:25 +00:00
Vitor PamplonaandGitHub bbc7f9740e Merge pull request #3065 from vitorpamplona/claude/gifted-ritchie-5XjZf
Exclude author from zap split display logic
2026-05-27 11:09:29 -04:00
Vitor PamplonaandGitHub 7d6bbac200 Merge pull request #3064 from vitorpamplona/claude/sweet-maxwell-UMahG
Add playback error overlay with browser fallback for video codec failures
2026-05-27 11:04:09 -04:00
Claude e876e2b09b feat(zap-splits): hide single-author zap split row in NoteCompose
When a note's only zap split recipient is the post author, the split is
redundant — the author already receives the zap. Skip rendering the row
in those cases by gating on a new `hasZapSplitSetupBesidesAuthor` helper.
2026-05-27 14:54:48 +00:00
Claude 89607376cc feat(playback): surface unsupported codec errors with browser fallback
ExoPlayer entered the ERROR state silently when a codec was missing or the
container/format wasn't supported, leaving a blank video area with no
recourse. Track the player error in MediaControllerState, render an overlay
with the error code, and offer an "Open in browser" button so the user can
fall back to the system browser for codecs the device can't decode.
2026-05-27 14:51:09 +00:00
Vitor PamplonaandGitHub d381cf9109 Merge pull request #3063 from davotoula/feat/avif-support
Comprehensive AVIF support (#837)
2026-05-27 10:32:03 -04:00
davotoula ef25f8c0e6 test(amethyst): instrumented coverage for AVIF upload + decode
Adds 4 instrumented test files + 3 tiny pre-committed AVIF fixtures to
catch regressions in the upload pipeline.
2026-05-27 16:11:20 +02:00
davotoula f8b24c645a fix(chat): hide DM quality slider for AVIF and correct error framing 2026-05-27 16:11:20 +02:00
davotoula 7d580452e4 fix(uploads): surface specific AVIF metadata error instead of 'Upload cancelled' 2026-05-27 16:11:20 +02:00
davotoula 50a81c35cf Code review:
style(nests): import TimeUtils in CreateNestViewModel instead of inline FQN

HIGH-1: import java.io.RandomAccessFile in MetadataStripper instead of
inline fully-qualified name

HIGH-2: catch AvifMetadataNotVerifiableException in the 6 ViewModels
that call MetadataStripper.strip directly (profile picture, emoji pack
list+display, bookmark group, nest, channel)

MEDIUM-1: tighten AvifAnimatedDecoderFactory.createAnimatedImageDecoder
annotation from @RequiresApi(P) to @RequiresApi(S); the outer guard is
already SDK_INT < S.

MEDIUM-2: replace the curried lambda DI seam in MetadataStripper with
a named fun interface (AvifExifReader).

MEDIUM-3: rename isGifUrl -> isAnimatedMediaUrl (MyAsyncImage) and
BaseMediaContent.isGif() -> isAnimatedMedia() (ZoomableContentView)
since both predicates now cover AVIF as well as GIF.

- AvifAnimatedDecoderFactory.isAvif now iterates a single brand list
  with .any { rangeEquals(8, it) } instead of three || branches.
- MetadataStripper.inspectAvifMetadata dropped the outer defensive
  try/catch; the inner catch already converts parse failures to
  AvifMetadataNotVerifiableException and the rest of the function
  cannot realistically throw.
- PreviewMetadataCalculator extracts the shared ImageDecoder allocator
  + exception path from decodeAvifBytes and decodeAvifFromUri into a
  single private decodeAvif(source) helper.
- RobohashFallbackAsyncImage merges its identical Loading and Error
  when branches into one via Kotlin's multi-value branch syntax.
- MediaCompressorTest drops a no-op MockKAnnotations.init(this) call
  and the now-unused import; no @MockK fields exist.
2026-05-27 16:11:20 +02:00
davotoula 03c42f585e AVIF display + thumbnail-cache fixes from manual testing
fix(ui): default avatar contentScale to Crop, not Fit
fix(images): skip thumbnail cache for animated AVIF profile pictures
fix(ui): animate profile pictures regardless of URL extension
2026-05-27 16:11:20 +02:00
davotoula 57724cee8c Comprehensive AVIF support (#837)
feat(ui): hide compression slider for non-compressible files (AVIF, GIF, SVG)
feat(images): custom Coil decoder for animated AVIF
feat(ui): include AVIF in animation-aware MIME predicates
fix(uploads): AVIF extension fallback in BlossomUploader
fix(uploads): AVIF extension fallback for NIP-96 multipart filename
feat(uploads): decode AVIF previews with ImageDecoder for blurhash/thumbhash
feat(uploads): fail-closed AVIF metadata inspection in MetadataStripper
fix(uploads): preserve AVIF bytes through MediaCompressor
feat(uploads): add MediaMimeTypes helper for AVIF detection
2026-05-27 16:11:20 +02:00
davotoulaandClaude Opus 4.7 adc0d36407 docs(amethyst): TDD-style implementation plan for AVIF support (issue #837)
15 bite-sized tasks across 6 phases (A foundation, B upload pipeline, C animation
lifecycle audit, D test fixtures + instrumented tests, E manual on-device
verification, F ship). Each task has exact file paths, full test code, full
patch code, exact commands, expected output, and per-task commits.
Companion to amethyst/plans/2026-05-26-avif-support.md spec.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-27 16:11:20 +02:00
davotoula a99927bf92 Docs: plan for comprehensive AVIF support (issue #837)
docs(amethyst): document strip-toggle-off AVIF EXIF leak as known limitation
docs(amethyst): document Desktop AVIF gaps from spot-check
docs(amethyst): record animated AVIF playback caveats from on-device testing
docs(amethyst): note API < 31 gallery-picker greys out AVIF (OS limit)
docs(amethyst): tighten API < 31 known-limitation with on-device findings
docs(amethyst): plan and design for AVIF instrumented tests
2026-05-27 16:11:20 +02:00
Vitor PamplonaandGitHub 1ef8b7405a Merge pull request #3060 from vitorpamplona/l10n_crowdin_translations
New Crowdin Translations
2026-05-27 09:56:19 -04:00
Crowdin Bot 72afadbb38 New Crowdin translations by GitHub Action 2026-05-27 13:20:50 +00:00
Vitor PamplonaandGitHub b12e160457 Merge pull request #3056 from mstrofnone/feat/namecoin-core-rpc-backend
feat(namecoin): add Namecoin Core RPC backend with optional ElectrumX fallback
2026-05-27 09:17:30 -04:00
Vitor PamplonaandGitHub 7af34762bb Merge pull request #3062 from vitorpamplona/claude/payment-targets-ui-7pgrY
feat(profile): toast when no app handles a payment target scheme
2026-05-27 09:15:29 -04:00
Claude 023a3c6624 feat(profile): toast when no app handles a payment target scheme
Tapping a chip silently failed if no installed app handled the
type-specific URI scheme (bitcoin:, ethereum:, monero:, etc.).
Surface that case through the existing toastManager so users know
to install a compatible wallet.

https://claude.ai/code/session_01R7kRziq14Hc22dPwAnZRAr
2026-05-27 13:10:36 +00:00
Vitor PamplonaandGitHub 9856458c54 Merge pull request #3061 from vitorpamplona/claude/payment-targets-ui-7pgrY
feat(profile): modern chip layout for payment targets
2026-05-27 09:09:09 -04:00
Vitor PamplonaandGitHub 7e44df0d1f Merge pull request #3059 from davotoula/feat/emoji-pack-add-to-list-menu
Add "Add/remove to/from emoji list" row to pack-card menu
2026-05-27 06:34:10 -04:00
Vitor PamplonaandGitHub da5f01011c Merge pull request #3058 from nrobi144/feat/desktop-profile-editing
feat(desktop): full profile editing — 13 fields, image upload, NIP-05 verification, drag-and-drop
2026-05-27 06:33:26 -04:00
davotoula 205b629c9d Code review:
- tighten EmojiListToggleRow null-handling and label branching
2026-05-27 09:58:26 +02:00
davotoula 142bf67678 Add "Add to emoji list" row to pack-card menu
Closes the UX gap where a user who creates a pack via the in-app UI has no
path to add it to their NIP-51 kind-10030 selection without leaving the
pack-management screens.
2026-05-27 09:58:03 +02:00
nrobi144andClaude Opus 4.6 bcf61d53ff feat(desktop): add drag-and-drop for avatar/banner, fix avatar overlay
- Wire DragAndDropTarget on avatar circle and banner area
- Image-only filter (jpg/png/gif/webp/avif)
- Visual drag-over feedback (primary border highlight)
- Fix avatar: only show placeholder icon when no image set
  (previously overlay was visible behind the loaded avatar)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-27 10:22:18 +03:00
m daa7c7913e feat(namecoin): mention umbrel alongside StartOS in docs and UI hints
Both umbrelOS (via getumbrel/umbrel-apps#4962) and StartOS / Start9
(via Start9-Community/namecoin-core-startos) ship a self-hosted
Namecoin Core that this backend can target. Generalize the
help/strings so umbrel users discover the feature too.

No logic changes.
2026-05-27 14:50:37 +10:00
m a58e7164b5 feat(namecoin): add Namecoin Core RPC backend with optional ElectrumX fallback
Adds a second resolution backend alongside the existing ElectrumX path:
users can now point Amethyst directly at a Namecoin Core full node
(e.g. a StartOS / Start9 installation) instead of (or in addition to)
trusting public ElectrumX operators.

Settings -> Namecoin grows three new pieces:

  1. Backend selector (radio) - ElectrumX | Namecoin Core RPC
  2. Core RPC section - URL, username, password, masked password,
     'Test RPC' button that calls getblockchaininfo and reports
     chain / height / sync %, error path with diagnostic message
  3. Fallback policy - independent toggles for falling back to the
     user's custom ElectrumX servers (Core RPC primary only) and/or
     the hardcoded public ElectrumX defaults

Quartz additions:
  - NamecoinBackend enum, NamecoinCoreRpcConfig (kotlinx.serialization),
    NamecoinFallbackPolicy
  - NamecoinNameBackend interface + ElectrumxNameBackend adapter +
    CompositeNamecoinBackend orchestrator (implements IElectrumXClient
    so NamecoinNameResolver is unchanged)
  - NamecoinCoreRpcClient (jvmAndroid) - JSON-RPC name_show /
    getblockchaininfo over OkHttp, reuses
    roleBasedHttpClientBuilder.okHttpClientForNip05() so Tor onion
    endpoints work without extra plumbing

Semantics:
  - Authoritative negatives (NameNotFound, NameExpired) short-circuit
    the chain - no silent privacy leak to other backends
  - Only transport / unreachable failures cascade through the chain
  - All fallback toggles default off (custom servers stay exclusive,
    matching existing behaviour)
  - Settings persisted via NamecoinSharedPreferences DataStore
  - HTTP transport delegated to roleBasedHttpClientBuilder so existing
    Tor/proxy/cert pinning all works for Core RPC too

Tests:
  - CompositeNamecoinBackendTest (8 cases) - short-circuit, cascade,
    authoritative-negative, electrumx-primary path, expired-name,
    random-exception-cascade
  - NamecoinCoreRpcClientTest (7 cases) - success parsing, auth header,
    name-not-found, expired, generic RPC errors, unusable config,
    probe success + auth failure
  - NamecoinSettingsTest (8 cases) - parser plus new backend / fallback
    fields

Builds clean: :amethyst:compileFdroidDebugKotlin, :quartz:jvmTest.
2026-05-27 14:03:47 +10:00
nrobi144andClaude Opus 4.6 ce173debba fix(desktop): center avatar vertically and increase to 120dp
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-27 06:51:35 +03:00
nrobi144andClaude Opus 4.6 2e405f5644 fix(desktop): redesign avatar picker as tappable circle with upload overlay
Replace the awkward small icon button with a full 100dp tappable circle.
Shows surfaceVariant background when empty, semi-transparent overlay with
centered upload icon when image is present. Spinner replaces icon during
upload.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-27 06:46:29 +03:00
nrobi144 b608ca02fb Merge remote-tracking branch 'upstream/main' into feat/desktop-profile-editing
# Conflicts:
#	desktopApp/src/jvmMain/kotlin/com/vitorpamplona/amethyst/desktop/ui/UserProfileScreen.kt
2026-05-27 06:38:47 +03:00
Claude 1e3d2bbd3d feat(profile): modern chip layout for payment targets
Replace plain text payment-target rows with a FlowRow of pill-shaped
clickable chips that carry a type-aware icon, brand color, uppercase
label, and a truncated address. Tap opens a type-specific URI scheme
(bitcoin:, lightning:, ethereum:, monero:, liquidnetwork:, dash:,
payto:// fallback) so wallets can actually pick up the intent; long-press
still copies the authority to the clipboard.
2026-05-27 02:55:15 +00:00
Vitor PamplonaandGitHub 4306f26460 Merge pull request #3055 from vitorpamplona/claude/affectionate-wright-GRP8r
fix(user-metadata): fall back to indexer relays when outbox is exhausted
2026-05-26 21:55:32 -04:00
Claude db2b9551f9 fix(user-metadata): fall back to indexer relays when outbox is exhausted
If a user's NIP-65 outbox advertises only relays that don't hold their
kind 0, profile fetching used to give up after EOSE on those relays.
filterUserMetadataForKey now widens to the account's indexer relays
once every outbox relay has either EOSE'd or is in cannotConnectRelays
and metadata is still missing. UserWatcherSubAssembler invalidates
filters on EOSE so the fallback re-evaluates without waiting for an
unrelated trigger.
2026-05-27 01:20:37 +00:00
Vitor PamplonaandGitHub f6066de92a Merge pull request #3054 from vitorpamplona/claude/pensive-brown-UVDZf
Replace wallet reorder buttons with drag-and-drop UI
2026-05-26 20:33:59 -04:00
Claude 906ac06c57 feat(wallet): drag-and-drop reorder for NWC wallet cards
Replace the up/down chevron IconButtons on each wallet card with a drag
handle, matching the pattern used across the relay-settings screens.
Reuses RelayDragState / rememberRelayDragState / draggableRelayItem /
relayDragHandle from relays/common — same gesture handling, elevation
animation, and swap-on-threshold behavior.

The handle and item modifier are only attached when there is more than
one wallet to reorder.
2026-05-27 00:10:05 +00:00