Commit Graph

11947 Commits

Author SHA1 Message Date
Crowdin Bot a9081923f8 New Crowdin translations by GitHub Action 2026-04-24 18:15:56 +00:00
Crowdin Bot b5671b7449 New Crowdin translations by GitHub Action 2026-04-24 16:19:07 +00:00
davotoula 999184cced optimise imports 2026-04-24 18:15:58 +02:00
Vitor Pamplona 6a55c752e8 Merge pull request #2547 from greenart7c3/feat/reorder-payment-targets-icon
feat(reactions): place payment targets icon after zap by default
2026-04-24 11:47:21 -04:00
greenart7c3 e30413a081 feat(reactions): hide payment targets icon by default
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-24 11:15:00 -03:00
greenart7c3 4c8959dbd4 feat(reactions): place payment targets icon after zap by default
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-24 10:34:06 -03:00
Vitor Pamplona f2e7a622f1 Merge pull request #2546 from vitorpamplona/claude/increase-icon-size-x4vc7
Fix Material Symbol icon rendering and adjust bottom bar icon sizes
2026-04-24 09:16:48 -04:00
Claude 15e63c48a2 fix(icons): render Material Symbols with the correct tint
The painter measured the glyph with TextStyle(color = Unspecified) and
tried to override the color at draw time via drawText(layout, color = tint).
That override is unreliable across Compose versions: when the measured
style carries Color.Unspecified, drawText can fall through to Color.Black,
producing black-on-black icons (visible on the back arrow in dark mode).

Include the tint in the measured TextStyle directly. The (symbol, fontFamily,
density, tint, rtl) remember key on rememberMaterialSymbolPainter was already
keyed on tint, so the TextMeasurer cache still hits for every (symbol, color)
pair used by the app.
2026-04-24 13:04:30 +00:00
Claude 7ab23ce30e fix: increase bottom bar icon size to compensate for MaterialSymbols padding
MaterialSymbols glyphs have more internal padding than the previous
MaterialIcons, so at 20dp they looked smaller than before. Bump the
NavigationBar icons to 24dp (Material's default NavigationBar icon size)
and widen the notification-dot box accordingly to preserve its offset.
2026-04-24 12:55:28 +00:00
Vitor Pamplona 9874a44956 Merge pull request #2545 from vitorpamplona/l10n_crowdin_translations
New Crowdin Translations
2026-04-24 08:14:41 -04:00
Crowdin Bot 222a3255d1 New Crowdin translations by GitHub Action 2026-04-24 12:13:39 +00:00
Vitor Pamplona 6a001cd807 Merge pull request #2544 from vitorpamplona/l10n_crowdin_translations
New Crowdin Translations
2026-04-24 08:12:07 -04:00
Vitor Pamplona e0605058da Merge pull request #2543 from davotoula/fix/hand-raised-bug-and-sonar-fixes
Fix: hand raised toggle bug and sonar fixes
2026-04-24 08:11:57 -04:00
Crowdin Bot 5298684892 New Crowdin translations by GitHub Action 2026-04-24 12:08:04 +00:00
Vitor Pamplona 5c2d5dafb7 Merge pull request #2542 from davotoula/docs/security-policy
add SECURITY.md with private vulnerability reporting policy
2026-04-24 08:06:08 -04:00
davotoula 20367af3db fix(cli): warn when FileStores delete() fails
Delete() returning false on a still-existing file meant we silently lost
scratch state for MLS group / key-package / message stores. Route the
four call sites through a `deleteOrWarn` helper that logs via the
KMP-safe quartz Log when the file refuses to disappear.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-24 11:10:39 +02:00
davotoula 33dacaa260 fix(audio-room): give hand-raise button a visible toggled state
Replace FilledTonalIconButton with FilledTonalIconToggleButton so the
container color reflects raised/lowered state, and drop the tautological
`if (handRaised) PanTool else PanTool` conditional flagged by Sonar.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-24 11:09:14 +02:00
davotoula 59b36080ea docs: add SECURITY.md with private vulnerability reporting policy
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-24 09:54:12 +02:00
Vitor Pamplona 82bbfa3d94 Merge pull request #2541 from vitorpamplona/claude/fix-arrowback-icon-0v6aT
Optimize Material Symbols rendering with shared font and text measurer
2026-04-23 23:11:10 -04:00
Claude 72bef059f2 perf(icons): share FontFamily + TextMeasurer across Material Symbol icons
Material Symbol icons were allocating a fresh Font wrapper on every
composition (Font(resource = ...) returns a new instance each call),
which invalidated the remember(font) cache in rememberMaterialSymbolsFontFamily
and forced a fresh TextMeasurer per call site. Tint was also baked into
the TextStyle passed to measure(), so any tint change (selected /
unselected reaction icons, hover states) produced a cache miss.

Hoist the FontFamily + TextMeasurer to CompositionLocals provided once
at the root (AmethystTheme on Android, MaterialTheme wrapper on desktop)
via ProvideMaterialSymbols. Every icon in the subtree now reuses:

- One FontFamily (Skia typeface cache hit for every subsequent glyph)
- One TextMeasurer with a 64-entry LRU, shared across all call sites
  so its measurement cache is hit across 300+ MaterialSymbols usages
- Tint applied via drawText(layout, color = tint) instead of TextStyle,
  so the measured TextLayoutResult is reused across tint variations

Icon(symbol = ...) now delegates to Material3's Icon(painter = ...),
restoring proper sizing/semantics without the custom Box+drawBehind
workaround. autoMirror handled inside the painter's onDraw. The
previously-unused MaterialSymbolPainter is now the single render path.

The weight parameter is dropped from the Icon/Painter APIs since the
app uses a single weight globally (MaterialSymbolsDefaults.WEIGHT).

A local fallback path keeps @Preview composables that don't wrap in
the theme renderable (per-composition allocation, same cost as before).

https://claude.ai/code/session_01V9esGUFH72ujCQNWCAnjKN
2026-04-24 03:01:23 +00:00
Claude a436df7a21 Merge remote-tracking branch 'origin/main' into claude/fix-arrowback-icon-0v6aT 2026-04-24 02:44:48 +00:00
Vitor Pamplona e2df0f036b Merge pull request #2540 from vitorpamplona/claude/review-wakeup-notification-tPUzb
Improve WakeUp event handling for multiple referenced events
2026-04-23 22:11:31 -04:00
Vitor Pamplona 5b1be8ded5 Maybe 300 weight is better for these fonts 2026-04-23 22:10:18 -04:00
Claude ae6ed9ecb2 chore: drop MAX_WAKEUP_REFS cap in wakeUpFor
Filter assemblers already handle batching and sizing; capping here adds
nothing.
2026-04-24 02:04:59 +00:00
Claude 6a9e0a79a6 fix(icons): render MaterialSymbol Icons that rely on defaultMinSize
The Canvas-based MaterialSymbol Icon collapsed to 0x0 whenever callers
didn't pass an explicit size modifier (e.g. ArrowBackIcon, ClearTextIcon).

Canvas is a Spacer + drawBehind, and Spacer's measure policy only uses
maxWidth/maxHeight when the incoming constraints are fixed. Inside an
IconButton (state layer 40dp, content constraints 0..40), defaultMinSize
only lifts minWidth/minHeight, leaving the constraints unfixed - so
Spacer picked 0x0 and the glyph never drew. Icons that passed
Modifier.size(N.dp) worked only because size(...) produces fixed
constraints.

Swap Canvas for an empty Box with drawBehind. Box's EmptyBoxMeasurePolicy
uses minWidth/minHeight, so defaultMinSize(24, 24) now takes effect when
no size modifier is supplied - matching Material3's own Icon behavior.

https://claude.ai/code/session_01V9esGUFH72ujCQNWCAnjKN
2026-04-24 02:00:11 +00:00
Claude 9574f4b68d fix: align WakeUp handling with spec semantics (p-tags = authors)
A previous commit misread the spec: p-tags on a WakeUp identify the
AUTHORS of the referenced events (the people whose events are the
subject of the wake-up), not the recipients. The consumer's existing
npub-in-p-tag match is therefore correct — "is this logged-in account
the author of a referenced event?".

- Revert WakeUpEvent.build() back to notify(about.toPTag()) and replace
  the comment with a spec-accurate one.
- In wakeUpFor, source author pubkeys from event.authorKeys() (p-tags,
  canonical) first, merge in the e-tag author hints, and fall back to
  the WakeUp signer only when both are empty. Bound by MAX_WAKEUP_REFS.

computeReplyTo and the referenced-event fetch path remain untouched —
those fixes are orthogonal to the p-tag semantic.
2026-04-24 01:54:48 +00:00
Claude 3c6b2bec9f fix: make WakeUp events actually deliver notifications
The WakeUp notification path had three latent bugs that caused pushed
WakeUps to silently not deliver anything to the user:

- WakeUpEvent.build() tagged the about event's AUTHOR as the recipient
  (via EventHintBundle.toPTag()), so a WakeUp about a zap from Alice to
  Bob would p-tag Alice. The consumer matches recipients by p-tag, so
  Bob — the intended recipient — was filtered out. Forward the about
  event's audience (its own p-tags) instead.

- wakeUpFor() passed the WakeUp's own note to EventFinderQueryState. The
  finder's filterMissingEvents only queries for the note itself (already
  in cache) or its replyTo (empty because computeReplyTo had no WakeUp
  case). The referenced events were never REQ'd on relays. Link the
  referenced events via computeReplyTo and subscribe the finder on each
  referenced note directly so filterMissingEvents picks them up.

- UserFinder was subscribed against the WakeUp's own pubKey (typically a
  push bot), not the author of the event the notification is about.
  Pull author pubkeys from the e-tag author hint, falling back to the
  WakeUp signer only when the hint is absent.

Also: bound e-tag count per WakeUp to 16 to prevent subscription
flooding, log the 30s timeout, extract WAKEUP_WINDOW_MS constant, drop
the now-unused Note parameter from wakeUpFor.
2026-04-24 01:26:50 +00:00
Vitor Pamplona 81819b6fbe updates kotlin file 2026-04-23 19:31:30 -04:00
Vitor Pamplona 75fb4845e5 Update AGP and dependencies 2026-04-23 19:15:12 -04:00
Vitor Pamplona 9db1353fbf Fixes test cases after moving files 2026-04-23 19:14:42 -04:00
Vitor Pamplona 9b5c8fe646 Merge pull request #2537 from vitorpamplona/l10n_crowdin_translations
New Crowdin Translations
2026-04-23 19:03:28 -04:00
Crowdin Bot 0017d01685 New Crowdin translations by GitHub Action 2026-04-23 23:02:10 +00:00
Vitor Pamplona 9854ccec1e Merge pull request #2536 from vitorpamplona/claude/review-marmot-implementation-BSrzC
Add leave/rejoin, reactions, deletions, and Marmot reset functionality
2026-04-23 19:00:24 -04:00
Vitor Pamplona d9b156d413 Merge pull request #2535 from vitorpamplona/claude/fix-banner-positioning-susFe
Improve broadcast UI styling with better spacing and rounded corners
2026-04-23 18:59:11 -04:00
Claude 655e56791b Merge branch 'main' into claude/review-marmot-implementation-BSrzC
# Conflicts:
#	amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/settings/AllSettingsScreen.kt
#	cli/src/main/kotlin/com/vitorpamplona/amethyst/cli/commands/Commands.kt
2026-04-23 22:36:29 +00:00
Vitor Pamplona 6ac6d84cb1 Merge pull request #2530 from vitorpamplona/l10n_crowdin_translations
New Crowdin Translations
2026-04-23 18:31:40 -04:00
Claude c0e5cc617e Merge remote-tracking branch 'origin/main' into claude/fix-banner-positioning-susFe
# Conflicts:
#	amethyst/src/main/java/com/vitorpamplona/amethyst/ui/broadcast/BroadcastBanner.kt
2026-04-23 22:28:31 +00:00
Crowdin Bot 185861da2d New Crowdin translations by GitHub Action 2026-04-23 22:25:17 +00:00
Vitor Pamplona 5707d4351a Merge pull request #2533 from vitorpamplona/claude/thin-material-icons-9SFWk
Replace Material Icons with Material Symbols
2026-04-23 18:23:42 -04:00
Vitor Pamplona 776160f6ef Merge pull request #2534 from vitorpamplona/claude/add-notification-filtering-RF0Td
Refactor notification pipeline to use cache observer pattern
2026-04-23 18:23:09 -04:00
Claude 5194190f5e test(marmot): cover MarmotManager leave + rejoin commons-layer round-trip
The quartz-layer companion (testLeaveAndRejoin_SameGroupIdEndToEnd in
MlsGroupManagerTest) only exercises the MLS engine. This one goes one
layer up and drives the full commons-layer pipeline end to end:

- NIP-59 gift wrap / unseal of the Welcome (kind:1059 -> kind:13 ->
  kind:444) via MarmotManager.ingest,
- ChaCha20-Poly1305 outer + MLS PrivateMessage decryption of the group
  event (kind:445) via the same ingest entrypoint,
- persisted inner-event log via MarmotMessageStore (asserts log is
  wiped on leave and does not resurrect pre-leave messages on rejoin),
- subscription bookkeeping on MarmotSubscriptionManager (asserts
  subscribe on join, unsubscribe on leave, re-subscribe on rejoin),
- KeyPackage bundle lifecycle via KeyPackageRotationManager +
  KeyPackageBundleStore (two distinct KPs generated for the two joins).

Same admin-kick workaround as the quartz-layer test: the standalone-
proposal ingestion path in MarmotInboundProcessor still returns
"Standalone proposals not yet supported", so the test has Alice
commit the Remove directly and documents that inline.

Self-contained in-memory stores live alongside the test so we don't
need to cross-depend on quartz's test utilities. Runs under commons
androidHostTest (same source set that already pulls in secp256k1
JVM bindings for real crypto).

https://claude.ai/code/session_01Unm6uLHGLj9UcBY7hWfJVW
2026-04-23 22:20:01 +00:00
Claude dd30f03bba fix(notifications): route Welcome events via direct invocation
WelcomeEvent (kind 444) has no `p` tag, so the cache-observer path's
tag-based account matching in consumeFromCache silently dropped them.
Route Welcomes instead via a dedicated notifyWelcome entry point invoked
from processMarmotWelcomeFlow — the one place we reliably know which
account the invite was for (the one whose signer just unsealed it and
joined the MLS group).

Drop the dead processWelcome workaround from the old notify(WelcomeEvent)
body; with the cache-first architecture, MLS processing always completes
before the notification fires.

- NotificationDispatcher: remove WelcomeEvent from the observer filter,
  expose a public `notifyWelcome(event, account)` entry.
- EventNotificationConsumer: rename notify(WelcomeEvent) to public
  notifyWelcome, add notification-enabled + foreground-suppression checks,
  drop the manager.processWelcome fallback.
- DecryptAndIndexProcessor: on WelcomeResult.Joined, invoke
  Amethyst.instance.notificationDispatcher.notifyWelcome to fire the
  "You've been added to <group>" notification.
2026-04-23 22:14:03 +00:00
Claude c1d98f057d Merge remote-tracking branch 'origin/main' into claude/thin-material-icons-9SFWk
# Conflicts:
#	desktopApp/src/jvmMain/kotlin/com/vitorpamplona/amethyst/desktop/Main.kt
2026-04-23 22:12:15 +00:00
Claude 405befaa68 feat(broadcast): float banner as a rounded card
The broadcast banner was rendered full-width with a 50dp bottom
offset that assumed a bottom navigation bar underneath. On screens
without the bottom bar the flat rectangle hovered above empty space,
which looked like a misaligned attachment rather than an overlay.

Restyle the banner as a floating card: 94% width (capped at 560dp for
tablets), rounded corners, shadow elevation. Keep it above a bottom
bar when present (50dp bar + 8dp float gap), but since it now reads
as an intentional floating toast, the same offset looks correct on
screens without a bottom bar as well.
2026-04-23 22:10:30 +00:00
Claude 73becef038 test(marmot): cover MlsGroupManager leave + rejoin round-trip
The existing testReaddAfterRemove_RejoinerCanEncryptAndDecrypt in
MlsGroupLifecycleTest exercises the MLS layer (two raw MlsGroup
instances, admin kicks peer, fresh KP, rejoin). Nothing covered the
same flow one level up at the MlsGroupManager layer, where the
persistent state store, retained-epoch wipe, and Welcome routing for
the same nostrGroupId actually live.

Adds testLeaveAndRejoin_SameGroupIdEndToEnd: two MlsGroupManager
instances (Alice + Bob) with separate InMemoryGroupStateStores go
through the full cycle — Alice creates with a MarmotGroupData
extension, Bob joins via Welcome, bidirectional message round-trip,
Bob calls manager.leaveGroup (asserts store + retained epochs both
wiped), Alice removes Bob's leaf, Bob's second KeyPackage is admin-
added, Bob processes the second Welcome under the same nostrGroupId,
and bidirectional round-trip works again at the new epoch.

Closes the "MarmotManager.leaveGroup + rejoin" and "persistent-store
leave + fresh Welcome for same nostrGroupId" gaps from the review.
Documents inline why Alice drives the eviction commit directly: the
standalone-proposal ingestion pipeline (MarmotInboundProcessor) is a
separate, still-unimplemented concern.

https://claude.ai/code/session_01Unm6uLHGLj9UcBY7hWfJVW
2026-04-23 22:05:12 +00:00
Vitor Pamplona bbdcd8adbe Merge pull request #2532 from vitorpamplona/claude/group-members-as-name-h9PgW
Enhance Marmot group UI with member avatars and dynamic naming
2026-04-23 17:58:12 -04:00
Claude c84fc6fcee refactor(notifications): observe unwrapped payloads instead of wrappers
Rely on the existing Account → DecryptAndIndexProcessor flow to unwrap
GiftWrap → Seal → inner payload, and observe the final inner kinds instead
of duplicating the unwrap logic in EventNotificationConsumer. The only wrap
left to handle in the notification path is the outermost server-added
GiftWrap from FCM / UnifiedPush, whose recipient tag the push server strips.

- Let LocalCache store kind:444 WelcomeEvent (consumeRegularEvent). The Seal
  handler used to early-return on Welcomes because kind:444 had no cache
  handler; now it does, so Welcome lands in cache and still routes to MLS
  processing.
- DecryptAndIndexProcessor: cache the inner event first, then branch into
  processMarmotWelcomeFlow vs eventProcessor.consumeEvent — both paths now
  emit to the observer.
- NotificationDispatcher observes the final payload kinds (ChatMessage 14,
  ChatMessageEncryptedFileHeader 15, CallOffer 25050, Welcome 444) in
  addition to the direct-arrival kinds. Wrappers (1059/21059/13) are no
  longer in the filter.
- EventNotificationConsumer drops unwrapFully entirely; consumeFromCache
  now just matches the account by `p` tag on an already-unwrapped event.
- New PushWrapDecryptor helper probes each saved account signer to decrypt
  the outer server wrap and feeds the inner GiftWrap to LocalCache — where
  the per-account EventProcessor takes over.
- FCM (PushNotificationReceiverService) and UnifiedPush (PushMessageReceiver)
  call PushWrapDecryptor instead of LocalCache.justConsume directly.
2026-04-23 21:57:23 +00:00
Claude b31e253b5b chore(drawables): drop unused lock and lock_plus drawables
No callers in code or XML.

https://claude.ai/code/session_016gV9gEaSud6kDMdcaqy8ti
2026-04-23 21:52:29 +00:00
Claude bed4554364 feat(marmot): show member pictures and names when group has no name
Marmot groups without a configured display name now fall back to the
NIP-17 strategy: a stacked avatar of the members and a comma-separated
list of their first names. Applied to both the group list row and the
chat top app bar.

NonClickableUserPictures and DisplayUserSetAsSubject get List<HexKey>
overloads so the renderers can be reused without dragging Marmot through
ChatroomKey (which is also a NIP-17 dedup key — different Marmot groups
can share the same member set).
2026-04-23 21:47:36 +00:00
Claude f08ad845f1 test(marmot-interop): un-skip tests 14-15 now that filtered UpdatePath works
Tests 14 (wn removes A) and 15 (wn_c leaves) were skipped with a
rationale referring to the pre-filter applyUpdatePath that rejected
wn/mdk-core commits whose UpdatePath omitted nodes with empty-copath
resolution per RFC 9420 §7.7. That code path was fixed in 3279c246
("use filtered direct path in UpdatePath per RFC 9420 §7.9") which
made both outbound generation and inbound validation operate on the
filtered path. The inverted-role interop tests can now actually run.

Test 14 creates a dedicated Interop-14 group, has wn_b admin-remove A,
and asserts amy flips to "not_member" on her next sync.

Test 15 creates Interop-15, has wn_c self-remove, nudges wn_b to
commit the SelfRemove via a rename, and asserts amy sees (C gone,
A present) AND can still publish a kind:9 that B receives — proves
the filtered commit applied cleanly and encryption survived.

https://claude.ai/code/session_01Unm6uLHGLj9UcBY7hWfJVW
2026-04-23 21:46:14 +00:00