Commit Graph

13226 Commits

Author SHA1 Message Date
davotoula c7ff30a5d5 fix(quartz): drop empty/whitespace-only imeta values
NIP-92 imeta tag entries are encoded as "key SPACE value" strings.
When IMetaTagBuilder.add() received an empty (or whitespace-only)
value the encoder produced "key " with a trailing space, which
schema-validating relays reject as a malformed tag value.
2026-05-08 19:28:11 +02:00
Vitor Pamplona ab09c520ce Merge pull request #2758 from mstrofnone/feat/nip9a-community-rules
feat(quartz): NIP-9A community rules parser + validator (kind:34551)
2026-05-08 08:30:50 -04:00
Vitor Pamplona ce42a36882 Merge pull request #2780 from mstrofnone/fix/desktop-sidebar-scrollable
fix(desktop): make the single-pane navigation rail scrollable
2026-05-08 08:30:01 -04:00
m 01f6c1c24b fix(desktop): make the single-pane navigation rail scrollable
The left navigation rail in single-pane mode renders a fixed list of
pinned screens (Home, Reads, Notifications, ...) plus a 'More' launcher
and a stack of bottom controls (relay health, bunker heartbeat, tor
status, account switcher).

Material3 `NavigationRail` lays its children out in a non-scrollable
`Column`. When the window is short — either because the OS window is
small or the user pinned several screens — the bottom items in the
list (and the 'More' button) get clipped and become unreachable.

Replace the `NavigationRail` with a `Column` that mirrors the rail's
container styling and splits content into two regions:

- A scrollable region (weight(1f) + verticalScroll) holding the pinned
  screens and the 'More' launcher. Overflow now scrolls instead of
  clipping.
- A fixed bottom region holding the relay health indicator, bunker
  heartbeat, tor status indicator, and the account switcher. These
  remain anchored at the bottom of the rail.

Item visuals are preserved by keeping `NavigationRailItem` for the
items themselves, with `NavigationRailItemDefaults.colors()`.

No behavior change when the rail content already fits the window.
2026-05-08 22:28:17 +10:00
Vitor Pamplona 36112c8b11 Merge branch 'main' of https://github.com/vitorpamplona/amethyst 2026-05-08 08:25:49 -04:00
Vitor Pamplona 2097089d3d Merge pull request #2788 from vitorpamplona/l10n_crowdin_translations
New Crowdin Translations
2026-05-08 08:20:44 -04:00
Vitor Pamplona 15e93c8838 spotless apply 2026-05-08 08:18:28 -04:00
Crowdin Bot 7dc9adb856 New Crowdin translations by GitHub Action 2026-05-08 12:17:53 +00:00
Vitor Pamplona f601836bc3 Merge pull request #2787 from vitorpamplona/l10n_crowdin_translations
New Crowdin Translations
2026-05-08 08:16:48 -04:00
Vitor Pamplona 753f259959 Merge pull request #2786 from greenart7c3/claude/add-blossom-cache-support-ts8mK
Add local Blossom cache bridge for transparent media proxying
2026-05-08 08:15:51 -04:00
Vitor Pamplona 06d6425369 Merge branch 'main' into claude/add-blossom-cache-support-ts8mK 2026-05-08 08:15:44 -04:00
Crowdin Bot 4ae51ca8bb New Crowdin translations by GitHub Action 2026-05-08 12:15:30 +00:00
Vitor Pamplona 8b1b49007c Merge pull request #2785 from greenart7c3/claude/disable-client-tags-option-Nb3b4
Add option to disable NIP-89 client tag in published events
2026-05-08 08:13:49 -04:00
Vitor Pamplona e8e1e44807 Merge pull request #2783 from greenart7c3/claude/add-copy-address-button-LgZSZ
Add copy-to-clipboard functionality for payment targets
2026-05-08 08:13:26 -04:00
Vitor Pamplona 90b98fa5f4 Merge pull request #2782 from davotoula/fix/scheduled-posts-lint-fixes
Lint and sonar cleanup on scheduled-posts
2026-05-08 08:13:17 -04:00
Vitor Pamplona 317b205858 Merge pull request #2781 from mstrofnone/fix/selectable-error-messages
fix(desktop): make error messages selectable so users can copy them
2026-05-08 08:12:49 -04:00
Vitor Pamplona fcb5b8cc55 Merge pull request #2779 from davotoula/fix/plurals-use-format-arg
Various language fixes to scheduled posts
2026-05-08 08:12:04 -04:00
Vitor Pamplona 0127ed4412 Merge pull request #2778 from nrobi144/fix/bunker-timeout-and-decrypt
feat(desktop): custom feeds system with creation, discovery, and author search
2026-05-08 08:11:55 -04:00
David Kaspar 3fb04bd6f2 Merge branch 'main' into fix/plurals-use-format-arg 2026-05-08 13:40:34 +02:00
David Kaspar ce9e83b791 Merge pull request #2784 from vitorpamplona/l10n_crowdin_translations
New Crowdin Translations
2026-05-08 13:40:07 +02:00
Claude bb871f6c71 fix(blossom): preserve upstream path prefix in xs= hint
The bridge was emitting `xs=https://cdn.nostr.build` for URLs like
`https://cdn.nostr.build/i/<sha>.jpg`, dropping the `/i/` prefix. The
local cache appends `/<sha>` to the xs server hint per spec, so it would
try to fetch `https://cdn.nostr.build/<sha>` — a 404 on nostr.build's
non-Blossom CDN scheme.

Anchor the server-base extraction on the slash immediately preceding the
sha-bearing path segment so we emit `xs=https://cdn.nostr.build/i`.
The cache then appends `/<sha>` and reaches the original blob.

Affects all three bridge entry points:
- MediaUrlContent.toCoilModel (note media)
- bridgeProfilePictureUrl (profile pictures)
- LocalBlossomCacheRedirectInterceptor (everything else via OkHttp)

Flat Blossom paths (`<host>/<sha>`) keep emitting `xs=<host>` unchanged.
2026-05-08 11:35:53 +00:00
Claude aa598b9949 fix(blossom): profile pictures now route through local cache
bridgeProfilePictureUrl previously returned a `blossom:` URI, but
profile pictures go through ProfilePictureFetcher (Coil routes by type
on `ProfilePictureUrl`), which feeds the URL straight to NetworkFetcher
and never sees BlossomFetcher. NetworkFetcher then tries to issue an
HTTP request against the `blossom:` scheme and fails — silently — so
profile pictures from Blossom servers stopped loading.

Return a direct `http://127.0.0.1:24242/<sha>.<ext>?xs=<host>&as=<pub>`
URL instead. The OkHttp request goes to the local cache normally; the
new redirect interceptor sees it's already on localhost and passes
through unchanged.
2026-05-08 10:25:30 +00:00
Claude 9451f7ca9a feat(blossom): catch all sha256-keyed URLs via OkHttp interceptor
Pictures hosted on Blossom servers (URLs with a 64-char sha256 hex in
the path) bypassed the bridge whenever they were rendered through a
composable that wasn't explicitly updated to call toCoilModel — link
previews, video thumbnails, badge images, audio room covers, etc.

Add LocalBlossomCacheRedirectInterceptor as an app-level OkHttp
interceptor that catches every HTTP request transparently:

- Detects a 64-char hex segment in any path segment
- Rewrites the request URL to http://127.0.0.1:24242/<sha>.<ext>?xs=<host>
- Coil's disk cache continues to key by the original URL so cached
  blobs survive toggling the bridge off

Activates only when the master toggle is on, the profile-pictures-only
restriction is off, and the probe sees localhost up. Profile-only mode
still routes profile pics via the existing composable bridge.
2026-05-08 10:02:08 +00:00
Claude 8559712a55 refactor(privacy): move disableClientTag into NIP-78-synced security settings
Aligns the toggle with its Security Filters siblings (warnAboutPostsWithReports,
filterSpamFromStrangers, etc.): stored in AccountSecurityPreferences{Internal},
synced across devices via the app-specific data event, and updated through
account.updateDisableClientTag -> sendNewAppSpecificData.

The signer wrapper now reads the live value from synced settings, so changes
still apply immediately to the next signed event.

https://claude.ai/code/session_01KhNVTm8LLdVphqyxVkZtDS
2026-05-08 09:23:30 +00:00
Crowdin Bot f81fb48bd4 New Crowdin translations by GitHub Action 2026-05-08 09:06:29 +00:00
David Kaspar 6fbab4a601 Merge pull request #2777 from vitorpamplona/l10n_crowdin_translations
New Crowdin Translations
2026-05-08 11:04:53 +02:00
davotoula 9565411e3d Extract UNSET_LABEL and ALPN_HQ_INTEROP constants in InteropClient 2026-05-08 11:02:36 +02:00
Claude 119ddf7cad feat(blossom): add as= author hint and profile-pictures-only mode
- `as=<authorPubKey>`: when converting plain http(s) imeta URLs into
  `blossom:` URIs we now include the note author's pubkey, letting the
  local cache consult their kind:10063 BUD-03 server list on miss.
  Plumbed via RichTextParser/CachedRichTextParser/RichTextViewer/
  ExpandableRichTextViewer/TranslatableRichTextViewer.

- Profile-pictures-only mode: a new per-account toggle that restricts
  the bridge to profile pictures (RobohashFallbackAsyncImage). When on,
  feed images and videos skip the bridge entirely. Profile pictures
  recover their sha256 from the URL path when present (covers
  Blossom-hosted avatars like https://nostr.build/i/<sha>.jpg).
2026-05-08 08:45:02 +00:00
Claude f1650ad9ce feat(privacy): add per-account toggle to disable NIP-89 client tag
Adds a "Don't add client tag to my events" switch under Security Filters.
NostrSignerWithClientTag now consults a runtime predicate at sign-time, so
the toggle takes effect immediately on the live session without rewrapping
the signer or rebuilding Account.

https://claude.ai/code/session_01KhNVTm8LLdVphqyxVkZtDS
2026-05-08 08:44:32 +00:00
Claude c6cd2fc95d feat(payments): add copy-to-clipboard option in profile payment button
Mirrors the option added to the reactions-row payment popup so the address
can also be copied from a user's profile header.
2026-05-08 08:21:31 +00:00
davotoula 9bdef97dd0 Log when File.delete() fails in ScheduledPostStore.persist() 2026-05-08 09:03:17 +02:00
davotoula 1a13ce3ad4 Convert scheduled-posts logout strings to <plurals> 2026-05-08 08:52:48 +02:00
davotoula e8db6ec6d9 Add pluralStringRes helper for non-composable scope 2026-05-08 08:52:36 +02:00
m 3e246e9e0b fix(desktop): make error messages selectable so users can copy them
Several user-visible error messages on Desktop are rendered with plain
`Text` composables, which means they can't be selected or copied. That
makes it awkward to share an error in a bug report or paste a hex error
string into a search.

Wrap the error text in `SelectionContainer` at the canonical sites:

- `commons.ui.components.LoadingState`: wrap the description in
  `EmptyState` and the message in `ErrorState`. `EmptyState` is reused
  as the in-feed error renderer (e.g. 'Error loading feed' with the
  underlying error in `description`), so this covers feed/loading
  errors across screens that use these helpers.
- `ComposeNoteDialog`: wrap the validation error and the upload error
  in the compose-note dialog.
- `auth/LoginCard` (Nostr Connect): wrap the connection error.
- `auth/KeyInputField`: wrap the supporting-text error so the inline
  message under the nsec input field can be copied.

No visual changes \u2014 `SelectionContainer` does not affect layout or
styling. Selection works on Compose Desktop (mouse drag) and on Android
(long-press) without further changes.
2026-05-08 16:32:48 +10:00
davotoula 6039e20879 reduce lint errors 2026-05-08 08:08:59 +02:00
davotoula 581a18de95 use %d in scheduled-posts plurals "one" forms. Fix english locale to prevent future translation errors 2026-05-08 07:11:06 +02:00
nrobi144 de879b9472 fix(desktop): use SearchBarState + rememberSubscription for author search
Replace manual relay subscribe + Channel approach with the proven
SearchBarState + rememberSubscription pattern (same as NewDmDialog).
This properly handles relay connection lifecycle and NIP-50 search,
returning results from all connected relays.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-08 07:37:27 +03:00
nrobi144 41920a363a feat(desktop): author search in feed builder with relay NIP-50 + avatars
- Hoist author search state to FeedsDrawerTab (AlertDialog can't run LaunchedEffect)
- NIP-50 relay search via connected relays with Channel-based result streaming
- 28dp UserAvatar in author suggestion rows
- "No users found" empty state
- 32dp dialog margins, 300dp max results height, 30 result limit
- FindUsersTest: 8 tests verifying cache search with/without metadata
- Fix: use connectedRelays instead of unconnected DEFAULT_SEARCH_RELAYS

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-08 07:23:44 +03:00
Crowdin Bot 20b4fad5d5 New Crowdin translations by GitHub Action 2026-05-08 02:11:48 +00:00
Vitor Pamplona 4d2019a30c Merge branch 'main' of https://github.com/vitorpamplona/amethyst
# Conflicts:
#	amethyst/src/main/res/values-pt-rBR/strings.xml
2026-05-07 22:05:39 -04:00
Vitor Pamplona cc2c0b2725 Merge pull request #2776 from vitorpamplona/claude/fix-portuguese-strings-quantity-CnV2F
Fix plural string formatting in Brazilian Portuguese translations
2026-05-07 22:04:17 -04:00
Vitor Pamplona 80330864bf solves linit issues 2026-05-07 22:02:48 -04:00
Claude 295b007d35 fix(i18n): use %d in pt-rBR plurals where 'one' covers 0 and 1
Brazilian Portuguese's 'one' quantity matches both 0 and 1, so a
hardcoded '1' is wrong when the count is 0. Switch the 'one' branch
of scheduled_posts_subtitle_due_suffix and scheduled_posts_relay_count
to use the %d argument like the 'other' branch.

https://claude.ai/code/session_01UmYZfFy8QqaDaXbcXguu8f
2026-05-08 02:01:05 +00:00
Vitor Pamplona 8a62a56b8f Merge pull request #2775 from vitorpamplona/claude/fix-permission-check-43aAZ
fix(scheduled-posts): explicit POST_NOTIFICATIONS check before notify
2026-05-07 21:08:03 -04:00
Claude 13f1101338 fix(scheduled-posts): explicit POST_NOTIFICATIONS check before notify
Lint flagged the notify() call as MissingPermission because there was no
visible check that POST_NOTIFICATIONS was granted. Gate the call behind
areNotificationsEnabled() — matching the pattern used in
EventNotificationConsumer — and catch SecurityException for the
narrow window where permission could be revoked between the check and
the notify().
2026-05-08 01:06:45 +00:00
Vitor Pamplona 4db81b72ff Merge pull request #2774 from vitorpamplona/claude/fix-nostr-repeat-sub-test-mOpDe
test(quartz): make repeat-sub test tolerant of resub timing race
2026-05-07 21:04:01 -04:00
Claude 28fb3b0e83 test(quartz): make repeat-sub test tolerant of resub timing race
The mid-stream resub pattern (resub1 at events.size==1, resub2 at
events.size==5) has four receive() calls between the two subscribe
calls. On a slow CI worker that's enough time for resub1
(filtersShouldIgnore, kind 10002 limit 500) to actually reach the
relay and start streaming events before resub2 replaces it, so the
loop's second EOSE can come from filtersShouldIgnore — which can
deliver up to 50 events (the preloaded count for that kind), well
past the previous 1..11 bound.

Drop the phase-specific count asserts and verify only structural
invariants: exactly two EOSEs, the loop stopped on the second, every
non-EOSE entry is a valid 64-char id, and the total event count
stays within the worst-case envelope.

https://claude.ai/code/session_01L4qS7BhX3L7ApiS3HsCozn
2026-05-08 00:59:57 +00:00
Vitor Pamplona 79bf7d708d Merge pull request #2773 from vitorpamplona/claude/harden-quic-audio-rooms-kwqnS
feat(quic): retire fully-settled streams to keep tracker bounded under audio-room churn
2026-05-07 20:19:13 -04:00
Claude 71e14fe639 chore(quic): audit cleanup — drop redundant copy, rename queue, extract test fixture
Three small follow-ups from the audit pass:

1. Drop redundant `challengeData.copyOf()` in
   `queuePathResponseLocked` — the parser produces a fresh
   ByteArray per PATH_CHALLENGE via `QuicReader.readBytes`'s
   `copyOfRange`, so the defensive copy was a wasted allocation.
   One-line fix.

2. Rename `pendingPathResponses` → `pendingPathChallengePayloads`.
   The queue holds inbound CHALLENGE payloads we owe RESPONSES
   for — old name conflated the two. Pure rename across
   QuicConnection / Parser / Writer / PathValidationTest.

3. Extract shared `newConnectedClient(...)` test fixture
   (`ConnectedClientFixture.kt`). The 6 test files each repeated
   ~40 lines of identical handshake-pipe boilerplate; folded into
   one parameterized helper accepting transport-cap overrides.
   Net −164 lines across the test tree; per-test helper is now a
   one-liner that documents the cap shape.

No behavior change. Full quic suite + amethyst hook test green.

https://claude.ai/code/session_018KPKWRg5baX5Anf7zfEyec
2026-05-08 00:03:47 +00:00
Vitor Pamplona 97766b1c5b Merge pull request #2772 from vitorpamplona/claude/review-fanout-index-e1Uqq
Add FilterIndex for inverted-index fanout dispatch
2026-05-07 19:58:17 -04:00