Commit Graph

11587 Commits

Author SHA1 Message Date
Claude 003603fd81 feat(live-chat): top zappers leaderboard above live stream chat
Add a horizontally-scrollable strip of pill chips showing the top
zappers on a live stream, placed above the goal header. Each chip has
an avatar, lightning icon, and compact sat total (K/M), matching
zap.stream's TopZappers look. Tapping a chip opens the zapper's
profile.

Aggregates zaps from two sources and dedupes by receipt id so a zap
that tags both #a=<stream> and #e=<goalId> doesn't double-count:
  - stream-scoped host-directed zaps already routed into the
    LiveActivitiesChannel cache via the existing #a subscription
  - goal-scoped zaps reachable through goalNote.zaps when a NIP-75
    goal is attached

Sorts contributors by total sats desc, takes top 10, hides the row
when there are none.

https://claude.ai/code/session_01WJA5PvDABegBYUu6h42YZi
2026-04-20 23:09:10 +00:00
Claude 43435427b2 feat(live-chat): show NIP-75 zap goal at top of live stream chat
Add a goalEventId() accessor on LiveActivitiesEvent that reads the
zap.stream `["goal", "<hex>"]` tag. When a stream channel's 30311 event
references a goal, fetch the kind 9041 event and its zap receipts
(#e=<goalId>) so the existing goal-progress aggregation works.

Render a compact LiveStreamGoalHeader above the chat feed showing the
goal title, the existing GoalProgressBar, and a one-tap ZapReaction
that targets the goal event so viewers can contribute directly to the
fundraiser without leaving the stream.

https://claude.ai/code/session_01WJA5PvDABegBYUu6h42YZi
2026-04-20 22:10:54 +00:00
Claude 8368c9d661 feat(live-chat): display live stream clips (kind 1313) inline
Add a Quartz LiveActivitiesClipEvent for zap.stream's kind 1313 clip
convention, parsing the `a` tag (stream address), `p` tag (host), `r`
tag (video URL), and `title` tag. Register it in EventFactory, subscribe
in the live-activity chat filter, and route clips into the source
LiveActivitiesChannel cache. Render clips as an accent-colored card
showing "X created a clip" with the title and an inline video player
for the clip's playable URL.

https://claude.ai/code/session_01WJA5PvDABegBYUu6h42YZi
2026-04-20 21:53:02 +00:00
Claude 4786647762 feat(live-chat): display live stream raids (kind 1312) inline
Add a Quartz LiveActivitiesRaidEvent for zap.stream's kind 1312 raid
convention with root/mention a-tag markers. Wire it into the event
factory, subscribe to it in the live-activity chat filter, and route
received raids into both source and target LiveActivitiesChannel caches
so viewers on either side see the notification. Render raids as an
accent-colored pill showing "X is raiding Y" that navigates to the
target stream when tapped.

https://claude.ai/code/session_01WJA5PvDABegBYUu6h42YZi
2026-04-20 21:29:50 +00:00
Claude 238c9ea626 feat(live-chat): display zap receipts inline in live stream chat
Subscribe to kind 9735 alongside kind 1311 against the stream's #a tag,
route host-directed zaps into the LiveActivitiesChannel cache, and render
them as a centered, lightning-accented row with the zapper, amount, and
optional zap message. Matches zap.stream's chat behavior for
NIP-53 + NIP-57 interop.

https://claude.ai/code/session_01WJA5PvDABegBYUu6h42YZi
2026-04-20 20:47:12 +00:00
Vitor Pamplona c9880a689d Merge pull request #2465 from vitorpamplona/l10n_crowdin_translations
New Crowdin Translations
2026-04-20 16:10:28 -04:00
Crowdin Bot 00ed676c6b New Crowdin translations by GitHub Action 2026-04-20 20:08:38 +00:00
Vitor Pamplona 1d91218455 Merge pull request #2463 from vitorpamplona/l10n_crowdin_translations
New Crowdin Translations
2026-04-20 16:07:38 -04:00
Vitor Pamplona 617fb38332 Merge pull request #2464 from vitorpamplona/claude/marmot-invite-event-N55Yh
Add daemon diagnostics and heartbeat logging to invite polling
2026-04-20 16:07:07 -04:00
Claude de78bceeae chore(marmot-interop): diagnostics for invite-delivery failures
Adds a dump_daemon_diagnostics helper (relays per type, wn debug health,
relay-control-state, pending invites, wnd stderr tail) and wires it into
Test 02 and Test 04 on both sides of the poll (pre-invite baseline +
post-timeout). wait_for_invite now prints a ~10s heartbeat with the
pending-invite count and the most recent welcome/giftwrap line from wnd
stderr. On timeout the script also prompts the operator to paste the
Amethyst-side MarmotDbg logcat so both ends of the gift-wrap pipe end up
in one log file.
2026-04-20 20:01:51 +00:00
Crowdin Bot da3d9cf0d7 New Crowdin translations by GitHub Action 2026-04-20 19:29:53 +00:00
Vitor Pamplona 6e61653bca Merge pull request #2462 from vitorpamplona/claude/marmot-interoperability-check-vR5ws
MIP-01/MIP-05 compliance: VarInt encoding, validation, and MDK interop
2026-04-20 15:28:33 -04:00
Vitor Pamplona a01cbbf2e1 Merge pull request #2461 from davotoula/claude/hls-resolution-selection-jzBTG
HLS video: Lowest resolution in feed/PiP, auto resolution in full screen
2026-04-20 15:19:30 -04:00
davotoula cb4a987498 refactor(video): apply code review fixes 2026-04-20 20:51:40 +02:00
davotoula 830e410a79 feat(video): default feed/PiP to lowest HLS resolution, fullscreen to auto 2026-04-20 20:51:14 +02:00
Claude 77c3634745 fix: tighten Marmot MIP-00/01/02/05 + RFC 9420 PublicMessage framing
Follow-up to the earlier MIP-spec alignment commit, addressing the
remaining audit items:

- MIP-00 (KeyPackageUtils.isValid): now performs every strict tag-level
  check that MDK does — d tag is exactly 64 hex chars, mls_protocol_version
  is exactly "1.0", mls_ciphersuite is exactly "0x0001", mls_extensions
  contains both 0xf2ee + 0x000a, mls_proposals contains 0x000a, encoding
  is "base64", content non-empty, i tag present. Adds
  `isCryptographicallyValid` for deep checks: i tag matches the computed
  KeyPackageRef (RFC 9420 §5.2), Basic credential identity equals the
  event's pubkey, KeyPackage signature verifies.

- MIP-01 (MarmotGroupData): encoder now omits the v3-only
  `disappearing_message_secs` field when version < 3, so v2 output
  matches MDK's `tls_codec` 0.4 byte-for-byte. Adds byte-level fixture
  tests pinned to the Rust reference (encode + decode in both directions).

- MIP-02 (WelcomeGiftWrap): replaces the redundant sign-then-rumorize
  step with `RumorAssembler.assembleRumor`, producing a true unsigned
  rumor as NIP-59 requires (no wasted secp256k1 signature).

- MIP-05 kind 449 (TokenRemovalEvent.build): no longer accepts a tag
  initializer. Per the MIP-05 spec the token-removal event MUST have
  empty tags; allowing arbitrary caller-supplied tags risks metadata
  leakage and rejection by strict validators (e.g. the MDK reference).

- RFC 9420 §6 PublicMessage framing: encoder/decoder now branch on
  `contentType`. Application messages keep the `opaque<V>` length prefix;
  Proposal/Commit bodies are emitted/parsed as their typed structs (no
  outer length prefix), per the MLS RFC. Previously encoder used
  `putBytes` raw and decoder used `readOpaqueVarInt`, so neither could
  roundtrip and Proposal/Commit PublicMessages from other MLS clients
  would mis-parse.

- KeyPackageUtilsTest fixtures: switch tiny "0"/"1"/"lr" slot ids to
  realistic 64-char hex slot ids, since `isValid` now enforces the
  MIP-00 d-tag format.
2026-04-20 18:02:12 +00:00
Vitor Pamplona c7b8d178f5 Merge pull request #2460 from vitorpamplona/claude/fix-empty-groups-display-MO6E9
Show placeholder notes for empty Marmot groups in chat list
2026-04-20 13:15:26 -04:00
Claude aea1dc53c3 fix: show empty Marmot groups in Messages screen
Previously ChatroomListKnownFeedFilter dropped any group whose
newestMessage was null, so groups the user just created and groups
received via Welcome events without any activity yet never appeared
in the Messages list.

Emit a stable placeholder Note (carrying the chatroom as a gatherer)
per empty group, route it through the existing Marmot row path, and
invalidate dmKnown on MarmotGroupList.groupListChanges so newly added
or promoted groups rebuild the feed.
2026-04-20 17:08:59 +00:00
Claude 1569b45671 fix: align Marmot implementation with MIP specs for interop with MDK
Brings Amethyst's Marmot code into wire-level interoperability with the
reference MDK (Rust) implementation by resolving four spec violations and
tightening the epoch lookback window:

- MIP-01 (NostrGroupData extension): switch to QUIC-style VarInt length
  prefixes (RFC 9000 §16) instead of fixed uint16. MIP-01 mandates this
  encoding (the one produced by the Rust `tls_codec` crate v0.4+), so
  Amethyst's previous uint16 framing could not round-trip with any MDK
  peer. admin_pubkeys, relays (outer + each inner), name, description,
  image_*, and disappearing_message_secs now all use VarInt prefixes.

- MIP-01: enforce "admin_pubkeys MUST NOT contain duplicates" in the
  MarmotGroupData constructor.

- MIP-05 (Push Notifications): token plaintext padded size was 220 (→ 280
  encrypted); spec MUSTs are exactly 1024 bytes plaintext (1084 encrypted).
  A server expecting MIP-05 tokens would reject Amethyst's frames outright.

- MIP-05: HKDF IKM was sha256(shared_point); spec requires the raw 32-byte
  ECDH x-coordinate. The extra sha256 produced a different PRK, so token
  ciphertext authenticated only within Amethyst. Removed the hash; ECDH
  already returns the x-only compact form via pubKeyTweakMulCompact.

- MIP-03: bump EPOCH_RETENTION_WINDOW from 2 to 5 to match MDK's
  DEFAULT_EPOCH_LOOKBACK, so late-arriving GroupEvents whose ChaCha20
  outer key was derived from a prior epoch's exporter secret can still
  be decrypted after a Commit advances the group.

Hand-crafted MarmotGroupData test blobs were updated to emit VarInt
length prefixes.
2026-04-20 17:06:51 +00:00
Vitor Pamplona d7eb11bb08 Merge pull request #2459 from vitorpamplona/claude/relay-icons-group-info-23CZ8
Track relay activity for Marmot group chats
2026-04-20 13:03:47 -04:00
Claude 42e226bf8d feat: show Marmot group relays as clickable icons with activity status
Replaces the comma-separated relay text on the Marmot Group Info
screen with a FlowRow of 55dp relay avatars that open RelayInfo on
tap and copy the URL on long-press, matching the relay-icon pattern
used elsewhere in the app.

To surface whether each configured relay is actually carrying traffic
for the group, MarmotGroupChatroom now tracks the most recent
kind:445 event timestamp observed from each delivering relay and the
info screen renders a green dot when a relay has delivered a group
event within the last 7 days (gray otherwise).
2026-04-20 16:59:19 +00:00
Vitor Pamplona f76e4f4c49 Merge pull request #2458 from vitorpamplona/claude/investigate-github-issue-k17Fx
Add emoji set support and shortcode validation to NIP-30
2026-04-20 12:36:18 -04:00
Claude 254f5bdcc2 NIP-30: use Address type for emoji-set reference instead of raw string
Makes the API safer: callers can't accidentally pass an unparsed or
malformed identifier. EmojiUrlTag.emojiSet is now Address?, serialized
via toValue() and parsed via Address.parse() (which rejects anything
that isn't a well-formed kind:pubkey:dTag).
2026-04-20 16:35:29 +00:00
Vitor Pamplona 99ce01af4e Merge pull request #2457 from vitorpamplona/claude/fix-members-tag-update-OKFvB
Refactor Marmot group members to use reactive state flow
2026-04-20 12:33:34 -04:00
Vitor Pamplona e0094f163b ⏺ fix: derive nostrGroupId from MLS GroupContext in Welcome processing
The "h" tag is optional in MIP-02 Welcome events — senders like
  whitenoise-rs omit it. Instead of failing when the h-tag is absent,
  derive nostrGroupId from the NostrGroupData extension embedded in the
  Welcome's GroupContext (the authoritative MLS source). An h-tag hint,
  if present, is still validated against the MLS-derived value.
2026-04-20 12:32:42 -04:00
Claude b6e31b21c8 refactor: make Marmot group members list reactive
Expose the member list as MutableStateFlow<List<GroupMemberInfo>> on
MarmotGroupChatroom, populated by MarmotManager.syncMetadataTo alongside
the existing memberCount. MarmotGroupInfoScreen and RemoveMemberScreen
now observe it via collectAsStateWithLifecycle, so both remote commits
(already routed through syncMetadataTo in DecryptAndIndexProcessor) and
local mutations refresh the UI without manual re-queries.

Account.addMarmotGroupMember and removeMarmotGroupMember now call
syncMetadataTo right after the MLS state is mutated, mirroring the
pattern already used by updateMarmotGroupMetadata, so the local change
is visible immediately without waiting for the relay round-trip.
2026-04-20 16:27:31 +00:00
Vitor Pamplona 9766cc5901 Fixes the group id parsing 2026-04-20 12:23:46 -04:00
Claude 93c2041f0f NIP-30: add optional emoji-set address and shortcode validation
Per NIP-30 the emoji tag is ["emoji", <shortcode>, <url>, <emoji-set-address>]
with the fourth field optional. EmojiUrlTag only exposed the first three.
Adds emojiSet as an optional field, preserves existing positional callers
via default null, and introduces isValidShortcode() so callers can validate
user input against the spec (alphanumeric, hyphens, underscores).
2026-04-20 16:21:30 +00:00
Vitor Pamplona 4ccabb01e6 Adding capabilities to Amethyst's groups 2026-04-20 12:14:35 -04:00
Vitor Pamplona 4f2f8e180e Generates keypackages with 64 chars 2026-04-20 12:00:31 -04:00
Claude 7e45042c27 fix: refresh Marmot group members list when returning to info screen
Replace LaunchedEffect with LifecycleResumeEffect so the members list
(and chatroom.memberCount) is re-queried every time MarmotGroupInfoScreen
resumes, not just when nostrGroupId changes. Previously, after adding a
member via the AddMemberScreen and popping back, the members count
displayed in the header and chat top bar stayed stale until the screen
was fully recreated.
2026-04-20 15:58:00 +00:00
Vitor Pamplona f71d8eebb8 fixes the script for keypackages not showing up in wn 2026-04-20 11:42:29 -04:00
Vitor Pamplona e41ad84096 Merge branch 'main' of https://github.com/vitorpamplona/amethyst 2026-04-20 11:22:50 -04:00
Vitor Pamplona 1c797107bc fixes script on macos 2026-04-20 11:20:38 -04:00
Vitor Pamplona ad624031bf Improves the FetchFirst to not fail in the first eose from the first relay. 2026-04-20 11:15:09 -04:00
Vitor Pamplona d7cc99b196 update dependencies 2026-04-20 10:51:40 -04:00
Vitor Pamplona 37af8b0c45 fixes deprecation on vico charts 2026-04-20 10:51:18 -04:00
Vitor Pamplona 036323fa53 This was added by the secp rewrite, but we moved them out to another repo, so now it can be removed. 2026-04-20 10:43:54 -04:00
Vitor Pamplona 57af16302e Merge pull request #2455 from vitorpamplona/claude/fix-scaffold-scrolling-NHdRa
Refactor DisappearingScaffold to use unified bar state management
2026-04-20 10:42:01 -04:00
Vitor Pamplona 3e488623d4 Merge pull request #2453 from vitorpamplona/l10n_crowdin_translations
New Crowdin Translations
2026-04-20 10:24:23 -04:00
Crowdin Bot e808d40834 New Crowdin translations by GitHub Action 2026-04-20 14:19:21 +00:00
Claude 7637596835 Merge remote-tracking branch 'origin/main' into claude/fix-scaffold-scrolling-NHdRa 2026-04-20 14:19:18 +00:00
Vitor Pamplona 5551f2f7d2 Merge pull request #2456 from vitorpamplona/claude/nip51-interest-sets-p0f6r
Add Interest Sets feature for organizing hashtags
2026-04-20 10:17:31 -04:00
Claude 4c82011a25 fix(interest-sets): consume kind 30015 locally + empty-state polish
- LocalCache.consume dispatcher now handles InterestSetEvent via
  consumeBaseReplaceable, so the event created by the user is stored
  and flows through newEventBundles → interestSets.newNotes → listFeedFlow
  refresh. Without this, the just-signed event sat in the sendMyPublicAndPrivateOutbox
  call but the UI never saw the update.
- List screen: icon + centered text for the empty state and dividers
  between rows.
2026-04-20 14:11:06 +00:00
Vitor Pamplona 282b3a436b Merge branch 'main' of https://github.com/vitorpamplona/amethyst 2026-04-20 09:53:18 -04:00
Vitor Pamplona 0e077c02d0 linting 2026-04-20 09:41:58 -04:00
Vitor Pamplona 83837ad090 Merge branch 'main' of https://github.com/vitorpamplona/amethyst 2026-04-20 09:38:12 -04:00
davotoula 5eb91145df style: strip trailing whitespace and fix indent flagged by spotless
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-20 15:37:56 +02:00
Vitor Pamplona 2f3d035d3c Fixes failing tests due to dispatcher misconfiguration 2026-04-20 09:34:48 -04:00
Claude 198e950f37 feat(interest-sets): NIP-51 Interest Sets (kind 30015) UI + TopNav chips
Add Android UI for managing NIP-51 Interest Sets and surface them as chips
in the TopNav feed filter alongside followed hashtags. InterestSetEvent was
already implemented in Quartz; this wires it into the app.

- InterestSetsState mirrors LabeledBookmarkListsState: listFeedFlow with
  versioned refresh, decrypted hashtag cache keyed by dTag for the feed
  filter pipeline, and suspend helpers for create/rename/delete/clone/add
  hashtag/remove hashtag/move (public<->private).
- New screens under ui/screen/loggedIn/interestSets/: list, metadata edit
  (create/rename, title-only since InterestSetEvent has no image field),
  and display screen with add/remove + public/private toggle per hashtag.
- TopFilter.InterestSet(address) feeds into a new MultiHashtagFeedFlow
  that reuses HashtagTopNavFilter with Set<String>.
- TopNavFilterState.mergeInterests emits interest-set chips; FeedFilterSpinner
  groups them under a new "Interest Sets" category.
- Drawer entry + HomeScreen FAB branch.
2026-04-20 13:28:51 +00:00