Commit Graph

4480 Commits

Author SHA1 Message Date
Claude 208d9a8f7f feat(nests): tap an audience avatar to open the participant sheet
The host's primary path to add an audience member to the stage was the
long-press → "Promote to Speaker" row, which is hard to discover and was
also being swallowed by ClickableUserPicture (which ignores onLongClick
when onClick is null). Wire a tap handler on audience cells so a single
tap opens the same per-participant sheet, surfacing Promote to Speaker
for hosts and View Profile / Follow / Mute for everyone else.
2026-04-29 22:43:23 +00:00
Vitor Pamplona c8327e829f Merge pull request #2647 from vitorpamplona/l10n_crowdin_translations
New Crowdin Translations
2026-04-29 18:26:59 -04:00
Claude 8d228db440 fix(nests): preserve relays tag on participant updates + sync action bar with stage grid
Two related bugs the user hit while testing against nostrnests:

1. Approving a hand-raise made the Hands tab disappear (the host's
   local app saw the role grant) but the audience member did not
   appear on stage anywhere else.

   Cause: RoomParticipantActions.rebuild() rebuilt the kind-30312
   without the original room's `relays` tag. The recently-added
   broadcast fan-out path (Account.computeRelayListToBroadcast) keys
   off `event.allRelayUrls()` for kind 30312, so a republished room
   event with no relays tag only reaches the host's outbox — not
   nostrnests's fixed five reads or the audience member subscribing
   on those reads. The audience member never sees their SPEAKER tag
   and stays absent from the participant list.

   Fix: copy `original.relays()` into the rebuild so every republish
   (approve, demote, kick → re-broadcast) keeps the relay routing
   that lets the room reach its full audience.

2. Tapping "Leave Stage" as the host emptied the StageGrid
   ("Waiting for speakers…") but the action bar still showed the
   Talk + Leave Stage cluster.

   Cause: two different definitions of "on stage" were in play.
   StageGrid uses ParticipantGrid (role + presence.onstage flag),
   so flipping onStageNow=false drops the host out. The action bar
   gated on the role-only `onStage: List<ParticipantTag>` (the
   host's HOST tag never goes away), so the cluster stayed.

   Fix: derive isOnStageMe from participantGrid.onStage so both
   surfaces share the same "stepped off" semantics. The host who
   taps Leave Stage now drops to the audience-style mute toggle
   and can rejoin (kind-10312 onstage flips back to 1) without
   the controls lying about their state.

https://claude.ai/code/session_016G7oP5BotPjUBgvMYrrrxh
2026-04-29 22:13:36 +00:00
Vitor Pamplona 9628d17f5f Improves the relay list used for linked Notes to include their relay urls 2026-04-29 21:48:32 +00:00
Claude b0e7c62bb5 fix(nests): broadcast presence/chat to the linked room's full relay list
Hand-raise toggles in Amethyst weren't appearing in nostrnests's UI.

nostrnests's NestsUI v2 routes reads against a fixed five-relay list
(NestsUI-v2/src/lib/const.ts: relay.snort.social, nos.lol, relay.damus.io,
relay.ditto.pub, relay.primal.net) — no outbox model. Its presence query
is just `kinds:[10312], "#a":[roomATag]` over those five relays.

Commit 637174ef already taught computeRelayListToBroadcast to fan a kind
30312 *room* event out to its `relays` tag. But kind 10312 presence (and
chat / reactions, etc.) is a different event type that *links* to the
room via an `a` tag. For those, broadcast was only reaching:

  - the broadcaster's outbox relays
  - the single firstOrNull() hint baked into the `a` tag
  - the relay we happened to receive the room event from

If none of those overlap with nostrnests's fixed five reads, the
hand-raise update is silently dropped.

Extend the `linkedAddressIds` block in computeRelayListToBroadcast: when
the linked address resolves to a MeetingSpaceEvent / MeetingRoomEvent /
LiveActivitiesEvent, also add that linked event's allRelayUrls(). This
covers presence updates and any other room-scoped event that points at
a Nest via `#a`.

https://claude.ai/code/session_016G7oP5BotPjUBgvMYrrrxh
2026-04-29 21:48:32 +00:00
Crowdin Bot 8dec96710f New Crowdin translations by GitHub Action 2026-04-29 21:31:01 +00:00
Claude 2fae726e5e fix(nests): place Audience/Hands tab counter beside the label, not over it
PrimaryTabRow's BadgedBox places the count badge in the top-end corner
of its anchor, which on tight tab labels lands directly on top of the
text. Lay them out as a Row instead so "Audience  3" reads cleanly,
matching the tabbar comment's "Hands · 3" intent.
2026-04-29 20:53:38 +00:00
Claude 0fafd34537 fix(nests): emit canonical title tag and keep mute badge visible while muted
Two interrelated bugs from the deployed-schema flip:

1. Rooms created in Amethyst didn't appear in nostrnests's "Live Now"
   lobby. The lobby filter in NestsUI's skeleton.js requires a `title`
   tag on every kind:30312 — events without one are dropped from
   `live`/`planned`/`ended`. RoomNameTag still emitted the legacy
   `room` name, so our rooms passed our own readers (which tolerate
   both names) but were invisible to NestsUI. Flip canonical to
   `title`, keep `room` as legacy on read.

2. The mute badge over the avatar disappeared shortly after toggling
   mute, even though the broadcaster was still muted. Two causes:

   - ParticipantsGrid gated both the muted ring and the MicStateBadge
     on `member.publishing`. But per the deployed kind-10312 semantics
     `publishing=0` whenever `muted=1` (NestRoomPresencePublisher
     mirrors this — `publishingNow = broadcasting && !isMuted`), so
     the badge that was supposed to *indicate* mute was hidden
     exactly when it became relevant. Show the badge when on stage
     and either publishing OR muted; gate the muted ring on `muted`
     alone.

   - The presence heartbeat captured `micMutedTag` / `publishingTag`
     / `onstageTag` / `handRaised` at LaunchedEffect launch and never
     refreshed them. The 500 ms debounced LaunchedEffect did publish
     the new mute state, but ~30 s later the heartbeat overwrote it
     with the captured pre-toggle values, flipping `publishing`
     back to 1 and `muted` back to 0 server-side. Wrap the dynamic
     fields in `rememberUpdatedState` so the heartbeat reads the
     latest snapshot on every refresh; keep the existing key set
     (event/handRaised/onstage) for prompt re-emission on those
     transitions.

Tests updated for the new canonical `title` emission; both jvmTest
suites pass.
2026-04-29 20:45:32 +00:00
Claude aadb347b84 feat(nests): adopt deployed nostrnests schema (streaming/auth/live + paired 10112)
The deployed nostrnests reference (NestsUI v2 + moq-auth + moq-rs)
emits a different on-the-wire schema than the previous EGG-01 / EGG-09
drafts and Quartz writers. Verified by reading the production
NestsUI bundle. The deployed schema is now canonical:

kind:30312 (room event)
  - relay URL → ["streaming", url]   (was ["endpoint", url])
  - auth URL  → ["auth",      url]   (was ["service",  url])
  - live status → ["status", "live"]   (was "open")
  - ended status → ["status", "ended"] (was "closed")
  - room name → ["title", name]        (was ["room", name])

kind:10112 (user MoQ-server list)
  - 3-element entries: ["server", relay, auth]
  - first-element name "relay" accepted as a synonym (legacy)
  - 2-element entries: derive auth host by replacing leading "moq."
    with "moq-auth.", or prepending "moq-auth." otherwise

Implementation
- ServiceUrlTag.TAG_NAME = "auth", LEGACY_TAG_NAME = "service"
- EndpointUrlTag.TAG_NAME = "streaming", LEGACY_TAG_NAME = "endpoint"
- StatusTag enum: PLANNED/LIVE/PRIVATE/ENDED with code "live"/"ended";
  legacy "open"/"closed" still parsed on read
- NestsServersEvent: emit/read 3-element [server, relay, auth] tags
  with the legacy-shape tolerances above; expose a NestsServer pair
- Account.nestsServers.flow now produces List<NestsServer> pairs
- NestsServersScreen: rewritten edit-field asks for both URLs,
  recommended row shows the nostrnests pair, list rows show both URLs
- NestsScreen first-time setup writes the nostrnests pair, not a
  single URL; gate the create FAB on both URLs being parseable
- CreateNestViewModel: drop the resolveServerPair hardcoded mapping —
  the saved pair is now authoritative; defaults stay correct for an
  empty kind-10112 list

Specs
- EGG-01 rewritten to canonical streaming/auth/live/ended with a
  legacy-spelling table; example uses the real nostrnests pair
- EGG-02 references "auth" tag throughout; error taxonomy says "ended"
- EGG-09 rewritten to 3-element server tag with derivation fallback
- New nestsClient/specs/nip-53-proposed.md — a single self-contained
  proposed update to upstream NIP-53 covering kind 30312 + kind 10112
  with the deployed schema and the JWT-mint flow

All existing unit tests adjusted; quartz:jvmTest, amethyst:testPlayDebugUnitTest,
and nestsClient:jvmTest pass.
2026-04-29 19:32:53 +00:00
Claude 63555db48a fix(nests): use moq-auth.nostrnests.com for JWT mint
The previous defaults pointed both `service` and `endpoint` URLs at
`moq.nostrnests.com:4443`. That host only listens on UDP/QUIC for the
WebTransport relay — TCP :4443 is unreachable. OkHttp can't speak HTTP/3,
so the JWT-mint POST hangs and fails with `ConnectException`, leaving
the room screen stuck on "Reconnecting".

The real nostrnests deployment co-locates auth and relay on different
hosts (verified against the production NestsUI bundle):

    relay (WebTransport, QUIC only):  https://moq.nostrnests.com:4443
    auth  (JWT mint, regular HTTPS):  https://moq-auth.nostrnests.com

Split the defaults accordingly and add a `resolveServerPair` helper that
maps a single saved kind-10112 URL onto the (service, endpoint) pair the
kind-30312 event needs. The helper recognises both the auth-host and
the legacy relay-host the prior defaults wrote, so users upgrading from
the broken version migrate transparently the next time they open the
create-room sheet. Community deployments that genuinely co-locate keep
working via the fallback.

Update the recommended-servers list and the kind-10112 documentation to
match: the stored `server` URL is the moq-auth base (the URL that ends
up in the kind-30312 `service` tag).
2026-04-29 18:20:21 +00:00
Crowdin Bot 893ac76e3f New Crowdin translations by GitHub Action 2026-04-29 12:11:59 +00:00
Vitor Pamplona 94d3982f88 Merge pull request #2645 from greenart7c3/claude/add-payment-targets-settings-QgGFg
Add payment targets navigation to settings screen
2026-04-29 08:10:12 -04:00
Claude 8078e72bc1 feat: expose payment targets settings in account settings
Add a navigation row in AllSettingsScreen to open the existing
PaymentTargetsScreen, so users can configure their NIP-A3 payment
targets (kind 10133) from the account settings menu.
2026-04-29 11:59:39 +00:00
Vitor Pamplona aef0683bd6 Merge pull request #2639 from davotoula/fix-sonar-empty-functions
Fix sonar empty functions and string literals
2026-04-29 07:53:22 -04:00
Vitor Pamplona fe02f61d46 Merge pull request #2642 from mstrofnone/fix/last-seen-formatting
Fix Last Seen formatting for older timestamps ("Last seen Jan 14 ago" → "Last seen on Jan 14, 2026 (3 months ago)")
2026-04-29 07:53:07 -04:00
M 1df6504aac Fix Last Seen formatting for older timestamps
The profile 'Last seen' line was rendered by feeding timeAgo() output
into the 'Last seen %1$s ago' template. timeAgo() returns a bare
absolute date (e.g. 'Jan 14' or 'Jul 27, 2024') for anything older
than a month, so the result was nonsensical:

  - 'Last seen Jan 14 ago'
  - 'Last seen Jul 27, 2024 ago'

Replace the call with a new lastSeenSentence() helper that always
returns a self-contained, grammatical sentence:

  - 'Last seen 5 minutes ago'
  - 'Last seen 2 hours ago'
  - 'Last seen 3 days ago'
  - 'Last seen 2 weeks ago'
  - 'Last seen on Jul 27, 2024 (9 months ago)'
  - 'Last seen on Jan 14, 2024 (1 year ago)'

Anything older than a week now also shows the absolute date alongside
a coarse relative duration, so users can see exactly when the activity
happened without giving up the human-readable 'X ago' framing.

Adds plural-aware duration_minutes/hours/days/weeks/months/years
resources plus last_seen_on_date / last_seen_just_now / last_seen_never
helper strings. The existing 'last_seen' string keeps its placeholder
shape so existing translations continue to work for the recent-duration
case.
2026-04-29 19:11:09 +10:00
Crowdin Bot 5d314a3939 New Crowdin translations by GitHub Action 2026-04-29 08:42:00 +00:00
David Kaspar f581574173 Merge branch 'main' into fix-sonar-empty-functions 2026-04-29 10:41:43 +02:00
Crowdin Bot a6d5640206 New Crowdin translations by GitHub Action 2026-04-29 08:17:06 +00:00
davotoula 3ebf6cf02a l10n: translate Nests audio-rooms, AI alt-text, and tracked broadcasts strings
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>
2026-04-29 10:14:54 +02:00
davotoula fdcd83e0c6 extract duplicated string literals into named constants 2026-04-29 09:59:27 +02:00
davotoula 3332d8b530 add no-op comments to empty methods 2026-04-29 08:55:36 +02:00
Crowdin Bot b18b645233 New Crowdin translations by GitHub Action 2026-04-29 03:56:15 +00:00
Vitor Pamplona ee7eada00e Fixes top bar filter for Nests 2026-04-28 20:38:34 -04:00
Vitor Pamplona fd1b9f44b4 Merge pull request #2635 from vitorpamplona/claude/review-nostrnests-filtering-3EoIe
Add presence-based freshness filtering for Nests feed
2026-04-28 20:17:10 -04:00
Claude 5eea6ec8e6 fix(nests): keep chat textfield visible above keyboard on NestFullScreen
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.
2026-04-28 22:51:07 +00:00
Claude 0c875b150b feat(nests): tighten lobby filter to mirror NostrNests
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.
2026-04-28 22:45:18 +00:00
Vitor Pamplona 2723c48f88 Better design for audiences 2026-04-28 18:20:38 -04:00
Vitor Pamplona a538138b25 Merge pull request #2633 from vitorpamplona/claude/fix-amethyst-nest-reconnecting-T8DSP
Update MOQ relay URLs to include port 4443
2026-04-28 18:04:36 -04:00
Claude 882a7b5501 Revert "fix(net): prefer IPv4 over IPv6 for OkHttp + QUIC dial"
This reverts commit 23fab05c8a.
2026-04-28 21:56:30 +00:00
Crowdin Bot 3fd30cc820 New Crowdin translations by GitHub Action 2026-04-28 21:54:06 +00:00
Claude 045bc36371 fix(nests): respect navigation bar insets in NestActionBar
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.
2026-04-28 20:59:22 +00:00
Claude 23fab05c8a fix(net): prefer IPv4 over IPv6 for OkHttp + QUIC dial
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.
2026-04-28 20:26:49 +00:00
Vitor Pamplona 17e04fc692 Centralizes the empty mesaage. 2026-04-28 16:18:24 -04:00
Claude 412b2a285c fix(nests): point default moq.nostrnests.com URLs at port 4443
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.
2026-04-28 20:10:35 +00:00
Vitor Pamplona 629915dc6c Merge pull request #2628 from vitorpamplona/l10n_crowdin_translations
New Crowdin Translations
2026-04-28 15:58:45 -04:00
Vitor Pamplona 7818fd5db3 Merge pull request #2629 from vitorpamplona/claude/expandable-nestfullscreen-summary-H38Ey
Refactor nest room UI: collapsible summary and stage header redesign
2026-04-28 15:58:31 -04:00
Claude 7fe88ac4be Collapse Nest summary by default; tap title to expand
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.
2026-04-28 19:55:40 +00:00
Crowdin Bot c99ad72794 New Crowdin translations by GitHub Action 2026-04-28 19:52:19 +00:00
Vitor Pamplona 0f35777bd6 Better size for the Admin badge on Nests 2026-04-28 15:35:05 -04:00
Vitor Pamplona 24336cb940 Fixes scroll to newest when a new message arrives 2026-04-28 15:19:08 -04:00
Vitor Pamplona ee860b92a1 Adjusts the roudabout way of making the chat screen 2026-04-28 15:11:17 -04:00
Vitor Pamplona 2a7683a7c2 Smaller Avatars on Nest 2026-04-28 15:10:54 -04:00
Vitor Pamplona 17054fc35a Links the Tor okhttpclient with nests 2026-04-28 14:45:14 -04:00
Vitor Pamplona 66557268f7 only leaves if the event changes while watching. 2026-04-28 14:44:43 -04:00
Vitor Pamplona 98e62b167b Merge pull request #2621 from vitorpamplona/claude/review-nostr-nests-compliance-hKBnS
feat(nests): proactive JWT refresh + reconnect for speaker path
2026-04-28 13:56:06 -04:00
Vitor Pamplona 090f7499ae Merge pull request #2624 from vitorpamplona/l10n_crowdin_translations
New Crowdin Translations
2026-04-28 11:23:44 -04:00
Claude 4c63e58e68 feat(nests): PIP focuses on active speakers, surfaces self + connection state
Pulls three high-signal features from NestScreen into the PIP layout
without crowding the small floating window:

- Audio-level pulsing rings on active speakers, mirroring the
  full-screen StageGrid contract. The ring width tracks live audio
  level so the PIP visibly responds to who is talking, instead of
  showing static borders.
- Adaptive speaker selection: when anyone is speaking, PIP shows only
  the active speakers (up to 4); when exactly one person is talking
  the avatar bumps to 56dp for a clear focus mode. With no one
  speaking, falls back to the first 4 on-stage participants so the
  PIP isn't blank during silence. Fixes the 'same 4 faces while
  someone else talks off-screen' problem in big rooms.
- Self-status row (mic broadcasting / muted, hand-raised), so a
  glance at the PIP answers 'am I muted?' without expanding back.
  Hidden for pure audience listeners with no hand to keep clutter low.
- Reconnecting / Failed caption over a dimmed body so audio drops
  are obvious — previously the avatars stayed lit even with no audio
  arriving.

https://claude.ai/code/session_01BpwAxN9gs79CJuWKqf3BEB
2026-04-28 15:17:44 +00:00
Claude 4756348221 feat(nests): back gesture and Minimize button enter PiP instead of leaving
The only way to keep audio playing while leaving the room screen was the
Home/Recents gesture (onUserLeaveHint). Back swiped the user out of the
session entirely, and there was no in-UI affordance signalling that PiP
existed. Users wanting to step away had no obvious path that preserved
audio.

- BackHandler in NestActivityContent: while connected and PIP is
  supported, back minimizes to PIP. Disconnected/Connecting/Failed
  fall through to the default finish() so cancelling still works.
- Minimize IconButton in the room TopAppBar (hidden when the device
  doesn't advertise FEATURE_PICTURE_IN_PICTURE).
- Public NestActivity.enterPip() consolidates the connected + feature
  guards so onUserLeaveHint, BackHandler, and the Minimize button all
  share one entry point.

https://claude.ai/code/session_01BpwAxN9gs79CJuWKqf3BEB
2026-04-28 15:03:34 +00:00
Crowdin Bot 33dccc1d7a New Crowdin translations by GitHub Action 2026-04-28 14:35:17 +00:00