Commit Graph

3524 Commits

Author SHA1 Message Date
Vitor Pamplona e1304ddcab Merge pull request #2147 from vitorpamplona/claude/marmot-protocol-integration-xb3TT
Add Marmot MLS group messaging support to Amethyst
2026-04-05 16:06:12 -04:00
Claude 8b56b3b3bf feat: Phase 6 — Marmot account integration & event routing
Wire Marmot MLS group messaging into the app's Account lifecycle,
event processing pipeline, and relay subscription system.

New files:
- AndroidMlsGroupStateStore: encrypted file-based MLS state storage
  using KeyStoreEncryption (AES/GCM backed by Android KeyStore)
- MarmotManager: central coordinator holding all Marmot components
  (MlsGroupManager, KeyPackageRotationManager, subscription/inbound/
  outbound processors, WelcomeSender)
- MarmotGroupEventsEoseManager: relay subscription manager for
  kind:445 GroupEvent filters via AccountFilterAssembler

Account integration:
- MarmotManager initialized during Account startup with restoreAll()
- Outbound methods: sendMarmotGroupMessage, addMarmotGroupMember,
  publishMarmotKeyPackage(s), createMarmotGroup
- GroupEvent (kind:445) and KeyPackageEvent (kind:30443) added to
  LocalCache.justConsumeInnerInner() dispatch

Event routing:
- GroupEventHandler: processes inbound kind:445 events through
  MarmotInboundProcessor, indexes decrypted inner events in LocalCache
- GiftWrapEventHandler extended: detects kind:444 WelcomeEvent after
  NIP-59 unwrap, routes to MarmotManager.processWelcome(), triggers
  KeyPackage rotation when needed
- WelcomeEvent enhanced with optional "h" tag for group ID routing

https://claude.ai/code/session_01W2LHazEt4E3W4hn8f7gWVW
2026-04-05 19:54:56 +00:00
Crowdin Bot a437d169c0 New Crowdin translations by GitHub Action 2026-04-05 18:48:38 +00:00
Claude ee8bc2bf62 feat: add missing translations for 5 keys across 4 locales
Add translations for call_bluetooth, closed_polls, feed_group_locations,
open_polls, and poll_view_results to cs-rCZ, pt-rBR, sv-rSE, and de-rDE.

https://claude.ai/code/session_01CMo4zT5ZKRt3J1yHgm23Aq
2026-04-05 17:36:48 +00:00
Vitor Pamplona 67184fb72e Add a filter group for locations and moves global and around me to their correct sections 2026-04-05 12:50:52 -04:00
Vitor Pamplona 78010cfd69 Merge pull request #2143 from vitorpamplona/claude/webrtc-nip-ac-tests-f1l1g
Add comprehensive test suite for NIP-AC call state machine
2026-04-05 12:29:30 -04:00
davotoula 9af678bd46 use toast instead of dialogue 2026-04-05 18:27:47 +02:00
davotoula f9c7e3b15c - Added @VisibleForTesting annotation to the three internal methods in ShareHelper.kt — signals test-only intent and triggers lint warnings if called from production code
- Removed redundant deleteOnExit() in test helper — explicit delete() after each test already handles cleanup; the shutdown hook was unnecessary overhead
2026-04-05 18:27:47 +02:00
Vitor Pamplona 6e1526e178 Merge pull request #2137 from vitorpamplona/l10n_crowdin_translations
New Crowdin Translations
2026-04-05 12:26:00 -04:00
Vitor Pamplona de73d54a08 Merge pull request #2140 from vitorpamplona/claude/asyncimage-size-check-IDdPa
Add thumbnail disk cache for profile pictures
2026-04-05 12:24:59 -04:00
Vitor Pamplona 19421db09a Merge pull request #2139 from vitorpamplona/claude/poll-view-only-results-7VBnX
Add poll results preview feature with persistent viewing state
2026-04-05 12:24:41 -04:00
Crowdin Bot 690f1a393d New Crowdin translations by GitHub Action 2026-04-05 16:20:30 +00:00
Claude 6fbbcbbf3b refactor: clean up PeerSessionManager and CallController integration
- Split PeerSession.kt out of PeerSessionManager.kt (types, interface,
  manager are now in separate files)
- Remove webRtcSessions duplication in CallController — PeerSessionManager
  is now the single source of truth for session tracking; WebRtcCallSession
  is retrieved via the adapter cast when WebRTC-specific APIs are needed
- Initialize PeerSessionManager eagerly with localPubKey (passed to
  CallController constructor) instead of lazy suspend init — fixes early
  ICE candidates being silently dropped before first suspend call
- Extract FakePeerSession into its own file for reuse across test files
- Remove assertion-only glare tiebreaker tests from NipACStateMachineTest
  (now properly tested with real logic in PeerSessionManagerTest)

https://claude.ai/code/session_01AfRYTRCvtKqqDxeKQujUrx
2026-04-05 15:38:28 +00:00
Claude c79ef634de fix: store expiration timestamps in viewedPollResultNoteIds to prevent unbounded growth
Changes viewedPollResultNoteIds from Set<String> to Map<String, Long> where
the value is the expiration timestamp. Uses the poll's endsAt date if set and
in the future, otherwise now + 24 hours. Expired entries are pruned on each
new insertion. Serialized as JSON instead of StringSet.

https://claude.ai/code/session_01EkUYT4giQPUvbAJZ54o1se
2026-04-05 15:38:08 +00:00
Claude 361ef6798e refactor: simplify thumbnail cache API and remove unused code
- ThumbnailDiskCache: combine get()+decodeThumbnail() into single load()
  method. Move bitmap resize logic into generateFromFile() so callers
  just pass a source file and the cache handles decode+resize+save.
- ProfilePictureFetcher: remove unused `options` field, remove @Stable
  annotation, delegate all resize logic to ThumbnailDiskCache.
- UserAvatar: fix stale doc comment.

https://claude.ai/code/session_01PBJS3HDrurLP3i5n4tMsCv
2026-04-05 15:36:34 +00:00
davotoula fd9de754b1 Merge branch 'main' into fix-image-sharing-error
Resolve conflict in ZoomableContentView.kt: keep viewModelScope
from this branch (prevents cancellation on dialog dismiss) and
toast notification from main.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-05 17:31:19 +02:00
Claude 0df4c5e19c refactor: use typed ProfilePictureUrl class instead of URI scheme string
Replace profilepic:// string scheme with a ProfilePictureUrl data class.
Coil routes to the fetcher by type via Fetcher.Factory<ProfilePictureUrl>
instead of parsing a URI scheme string on every load. This avoids string
concatenation at the call site and string parsing in the fetcher/keyer.

https://claude.ai/code/session_01PBJS3HDrurLP3i5n4tMsCv
2026-04-05 15:22:38 +00:00
Claude e9f1fcbd2a refactor: extract PeerSessionManager for testable ICE buffering and glare handling
Extract the ICE candidate buffering, answer routing, and renegotiation
glare logic from CallController into a platform-independent
PeerSessionManager in commons/. This makes the three most critical
NIP-AC spec requirements testable as JVM unit tests with FakePeerSession:

1. Two-layer ICE candidate buffering (global + per-session)
   - Global buffer: candidates arriving before any PeerConnection exists
   - Per-session buffer: candidates arriving before setRemoteDescription
   - Candidates buffered while ringing are preserved when accepting

2. Renegotiation glare handling (pubkey comparison tiebreaker)
   - Higher pubkey wins, lower pubkey rolls back local offer
   - Full glare scenario test with two PeerSessionManagers

3. Callee-to-callee mesh initiation (lower pubkey initiates)

CallController now delegates to PeerSessionManager via a PeerSession
interface, with WebRtcPeerSessionAdapter bridging to WebRtcCallSession.

https://claude.ai/code/session_01AfRYTRCvtKqqDxeKQujUrx
2026-04-05 15:17:29 +00:00
Claude 3f032710d2 perf: avoid byte array allocation on first profile picture load
On cache miss, instead of reading the entire downloaded image into a
byte array (which could be 50MB+ for a malicious profile picture),
the fetcher now passes the NetworkFetcher result straight through to
Coil's normal streaming decoder pipeline — zero overhead vs current.

Thumbnail generation runs in background from Coil's disk cache file
using file-based BitmapFactory.decodeFile(), which is seekable
(supports two-pass bounds+decode) and never buffers the full file.

https://claude.ai/code/session_01PBJS3HDrurLP3i5n4tMsCv
2026-04-05 15:14:48 +00:00
Claude 6c9f6531cf fix: prevent race conditions in thumbnail disk cache
- Atomic writes: write to temp file then rename, so readers never see
  a partially written JPEG
- Deduplication: ConcurrentHashMap tracks in-flight URLs so multiple
  composables requesting the same profile picture don't trigger
  duplicate decode+save work

https://claude.ai/code/session_01PBJS3HDrurLP3i5n4tMsCv
2026-04-05 15:07:17 +00:00
Vitor Pamplona b6507561af Merge pull request #2135 from davotoula/video-share-download-toast
show toast while downloading video for sharing
2026-04-05 10:58:40 -04:00
Claude 80ece9ac9d perf: run thumbnail generation in background on first load
On cache miss, the fetcher now returns the decoded bitmap immediately for
display while saving the thumbnail to disk in a background coroutine. This
means the first load is just as fast as the normal Coil pipeline — no
blocking on resize + JPEG compress + disk write. The thumbnail is ready
for the next load.

https://claude.ai/code/session_01PBJS3HDrurLP3i5n4tMsCv
2026-04-05 13:58:06 +00:00
Claude 9b356d31ba feat: add thumbnail disk cache for profile pictures
Profile pictures are displayed at small fixed sizes (18-100dp) but Coil's
disk cache stores full-size originals, causing expensive re-decode on every
memory cache miss. With 60 avatars on screen, this creates significant I/O.

Adds a ProfilePictureFetcher with a dedicated ThumbnailDiskCache that stores
pre-resized 256px JPEG thumbnails (~5-10KB each). On cache hit, reads a tiny
file instead of re-decoding the full original. The zoomable full-screen dialog
bypasses this cache and loads the original via the normal Coil pipeline.

https://claude.ai/code/session_01PBJS3HDrurLP3i5n4tMsCv
2026-04-05 03:06:52 +00:00
Claude 3a78dd6afe feat: add "View results" option to polls that prevents voting after viewing
Adds a "View results" link below poll voting options. Once a user clicks it,
the poll results are shown and the user can no longer vote on that poll.
The viewed state is persisted in account settings via SharedPreferences.

https://claude.ai/code/session_01EkUYT4giQPUvbAJZ54o1se
2026-04-05 02:22:18 +00:00
Claude 8b944a0c68 feat: show toast while downloading video for sharing
Display a "Downloading video…" toast when the user taps share on a
remote video, so they know the download is in progress before the
share sheet appears. Uses the existing `downloading_video_for_sharing`
string resource that was already translated in multiple locales.

https://claude.ai/code/session_01B7HLXqbnicfj3eg1Wjgz6Z
2026-04-04 16:31:16 +00:00
Claude c88c961488 refactor: simplify scope usage and fix test temp file leak
- Remove unnecessary local alias; use accountViewModel.viewModelScope
  directly at call sites for clarity about scope ownership
- Add deleteOnExit() to test temp files so they're cleaned up even
  if assertions fail

https://claude.ai/code/session_01Euj5mXfjneNCx9m49YTp92
2026-04-04 15:56:21 +00:00
Claude 074cde0bd1 test: add media extension detection tests for ShareHelper
Make getImageExtension, getVideoExtension, and getMediaExtension
internal for testing. Add tests covering all supported image formats
(JPEG, PNG, GIF, WebP) and video formats (WebM, AVI, MP4, MOV),
plus edge cases (unknown formats, empty files, short headers).

https://claude.ai/code/session_01Euj5mXfjneNCx9m49YTp92
2026-04-04 15:52:21 +00:00
Claude 7a70c98158 fix: image sharing fails due to coroutine scope cancellation
The share media dialog used `rememberCoroutineScope()` which shadowed
the outer `accountViewModel.viewModelScope`. When onDismiss() was called
immediately after launching the share coroutine, the dialog-scoped
coroutine was cancelled, causing CancellationException to be caught
as a generic error and showing "Unable to share image" toast.

Fix: use viewModelScope for share operations so they survive dialog
dismissal, and re-throw CancellationException in shareImageFile.

https://claude.ai/code/session_01Euj5mXfjneNCx9m49YTp92
2026-04-04 15:43:10 +00:00
Vitor Pamplona c3524d988f Merge pull request #2130 from vitorpamplona/claude/polls-feed-tabs-Cr3Ow
Add tabbed interface to Polls screen for open/closed polls
2026-04-04 09:52:06 -04:00
Vitor Pamplona 8ec6c5e13a Merge pull request #2129 from vitorpamplona/claude/fix-dark-mode-polls-92zS2
Set explicit surface color for Card in notification feed
2026-04-04 09:49:26 -04:00
Vitor Pamplona 19037e8f02 Merge pull request #2128 from vitorpamplona/claude/fix-poll-draft-editing-3j8cG
Add routing support for Poll and ZapPoll draft edits
2026-04-04 09:48:00 -04:00
Vitor Pamplona 53d4fb7910 Merge pull request #2127 from vitorpamplona/claude/faster-broadcast-tracker-disappear-Rm4p9
Improve broadcast completion detection and dismissal timing
2026-04-04 09:47:40 -04:00
Vitor Pamplona ff3e0bbd44 Merge pull request #2126 from vitorpamplona/l10n_crowdin_translations
New Crowdin Translations
2026-04-04 09:39:39 -04:00
Crowdin Bot a827c5baba New Crowdin translations by GitHub Action 2026-04-04 13:38:38 +00:00
Claude 7a3542c4b4 feat: split polls feed into Open and Closed tabs
Adds tabbed navigation to the polls screen, separating polls into
"Open" (active/no end date) and "Closed" (ended) tabs. Uses the same
tab pattern as HomeScreen with HorizontalPager and SecondaryTabRow.

https://claude.ai/code/session_0168P3u8aMmn4HnSBDXR7yv7
2026-04-04 03:27:04 +00:00
Claude 2384d1f58b fix: reduce broadcast tracker dismiss delay from 11s to 3s after completion
Previously the broadcast tracker always waited TIMEOUT_SECONDS + 1 (11s) before
disappearing, even when all relays had already responded. Now it only auto-dismisses
after 3 seconds once all relays have responded, and stays visible while broadcasts
are still in progress.

https://claude.ai/code/session_017KzfovYfAWCgrwMy8RWLb9
2026-04-04 02:46:03 +00:00
Claude 904711f0e7 fix: add routing for poll draft editing
The routeEditDraftTo() function was missing cases for PollEvent and
ZapPollEvent, causing poll drafts to fall through to the else->null
branch and preventing navigation to the poll editor.

https://claude.ai/code/session_01WedCekkgCwNVCq2ybSTvqE
2026-04-04 02:45:06 +00:00
Claude ce91327ebe fix: use surface color for open poll cards in dark mode notifications
The Card wrapping open polls in the notification feed was using the
default Material3 containerColor (surfaceContainerHighest), which is
too bright in dark mode where the app uses pure black backgrounds.
Explicitly set the container color to surface to match the theme.

https://claude.ai/code/session_012ErcSBhXugYCRAwHRXrFr7
2026-04-04 02:13:11 +00:00
Vitor Pamplona 802798bd6b Merge pull request #2124 from vitorpamplona/claude/dismiss-poll-notifications-AEVw5
Add ability to dismiss poll notifications
2026-04-03 18:19:13 -04:00
Vitor Pamplona a4c02ae235 Merge pull request #2123 from vitorpamplona/claude/nip-ac-ephemeral-giftwrap-SGk3d
Replace NIP-59 Gift Wraps with Ephemeral Gift Wraps for WebRTC calls
2026-04-03 18:18:21 -04:00
Claude 799ebd692f feat: switch NIP-AC from GiftWrap with expiration to EphemeralGiftWrap
Use EphemeralGiftWrapEvent (kind 21059) instead of GiftWrapEvent (kind
1059) for WebRTC call signaling. The ephemeral kind signals to relays
that these events are transient and should not be persisted, eliminating
the need for expiration tags on both inner signaling events and outer
wraps.

Changes:
- Remove expiration tags from all 6 call event types (25050-25055)
- Switch WebRtcCallFactory to produce EphemeralGiftWrapEvent wraps
- Update CallManager and CallController publishEvent types
- Update Account.publishCallSignaling signature
- Use CallManager.MAX_EVENT_AGE_SECONDS for staleness checks
- Update NIP-AC spec to document EphemeralGiftWrap usage
- Remove expiration-related tests

https://claude.ai/code/session_014kyBgZx7cNyeUXYWV25M4j
2026-04-03 22:11:19 +00:00
Claude 93e3f22c0d feat: add dismiss button to active poll notification cards
Allow users to individually dismiss poll notification cards at the top
of the notification screen, matching the existing Zap the Devs dismiss
pattern. Dismissed poll IDs are persisted in SharedPreferences.

https://claude.ai/code/session_013ECrmmshiF9SNTxr9SXkvC
2026-04-03 22:10:43 +00:00
Claude 8169c40bb6 fix: handle WebRTC offer glare during renegotiation
When two peers simultaneously trigger renegotiation (e.g., both enable
video), both create local offers and enter HAVE_LOCAL_OFFER state. When
a remote offer arrives in this state, setRemoteDescription fails with
"Called in wrong state: have-local-offer".

Fix by implementing standard WebRTC glare handling: use pubkey comparison
as a tiebreaker (higher pubkey wins). The losing peer rolls back their
local offer before accepting the remote one.

https://claude.ai/code/session_01XLbnNVx3GDhHrPZKPXfFdD
2026-04-03 21:41:49 +00:00
Claude 2bb860e97f fix: show video UI when upgrading voice call to video
The call screen used state.callType (set at call initiation) to decide
whether to render video. When a voice call was upgraded to video via
the toggle button, callType remained VOICE so the video grid and local
preview were never shown despite the track being created and sent.

Replace the static isVideoCall check with hasActiveVideo that also
considers isVideoEnabled and the presence of remote video tracks.
Fixes both the full-screen group call UI and the PIP overlay.

https://claude.ai/code/session_015TvWJoAEsfc2ohiRoT9sTr
2026-04-03 21:13:26 +00:00
Crowdin Bot b070bbb98f New Crowdin translations by GitHub Action 2026-04-03 20:50:19 +00:00
Vitor Pamplona 2159bcff6b Merge pull request #2119 from vitorpamplona/claude/fix-video-call-ui-kNo2m
Improve video call UI to show active peer videos with fallback avatars
2026-04-03 16:48:40 -04:00
Claude 9af306282b fix: video call UI per-peer video activity and layout improvements
- Track per-peer video activity in CallController so each peer's video
  state is independent (fixes frozen frames and incorrect fallback to
  phone-call UI when one peer disables video)
- Replace single RemoteVideoGrid with PeerVideoGrid that shows video
  for active peers and avatar/name for inactive peers
- Use call type (VIDEO vs VOICE) to determine layout instead of relying
  solely on isRemoteVideoActive boolean
- Remove extra spacing in AddParticipantDialog between search field and
  user list
- Fix group video monitor using separate job to avoid conflict with P2P
  monitor

https://claude.ai/code/session_01EFDCu97SLYp3TCeBSAttAe
2026-04-03 20:45:08 +00:00
davotoula 4ecc471d82 update translations: CZ, DE, PT, SE 2026-04-03 22:38:09 +02:00
Vitor Pamplona e36a6e4ae2 Merge pull request #2115 from vitorpamplona/claude/fix-webrtc-multiple-videos-EXBGs
Support multiple remote video tracks in group calls
2026-04-03 16:10:58 -04:00
Vitor Pamplona 4cf9418582 Merge pull request #2113 from vitorpamplona/l10n_crowdin_translations
New Crowdin Translations
2026-04-03 16:10:36 -04:00