Commit Graph

287 Commits

Author SHA1 Message Date
nrobi144 9c9c7d3f24 feat(cache): seed NotificationsScreen from cache on compose
- Add localCache param to NotificationsScreen
- Seed reactions and zaps from cache on initial compose
- Notifications now persist across tab navigation
- All screens now have cache seeding (Feed, Thread, Profile,
  Bookmarks, Reads, Notifications)
2026-03-26 07:20:22 +02:00
nrobi144 072e437277 feat(cache): wire State classes on DesktopIAccount for GC retention
- Add BookmarkListState, Kind3FollowListState, Nip65RelayListState
  to DesktopIAccount — pins important AddressableNotes via strong refs
- Create stub Kind3FollowListRepository + Nip65RelayListRepository
  (no persistence yet, null backups)
- Update followingKeySet() to read from Kind3FollowListState.flow
- Revert accidental long-form branch changes in DeckColumnContainer
2026-03-26 07:17:39 +02:00
nrobi144 cf6a74b162 feat(cache): add periodic memory cleanup for Desktop
- Add cleanMemory() to DesktopLocalCache (sweeps stale WeakRef entries)
- Add startCleanupLoop() to Coordinator with heap monitoring
  (MemoryMXBean, checks every 30s, cleans at >75% heap or every 5min)
- cleanObservers() clears unused NoteFlowSets on notes + addressables
- Try-catch per operation so one failure doesn't skip the rest
- 2-minute startup grace period before first cleanup
- Cleanup job cancelled on clear() (account switch / logout)
2026-03-26 07:14:15 +02:00
nrobi144 3dbbc039b3 feat(cache): replace BoundedLargeCache with LargeSoftCache on Desktop
- DesktopLocalCache now uses LargeSoftCache (WeakReference-based, GC-driven)
  instead of BoundedLargeCache (strong refs, 50k cap, arbitrary eviction)
- Delete BoundedLargeCache.kt — no longer needed
- Rewrite findUsersStartingWith to use forEach instead of values()
- Remove BoundedLargeCache eviction tests (no longer applicable)
- Notes now only disappear when nothing references them, not arbitrarily

Per Vitor's feedback: "this maximum size approach might not work well,
as things will just disappear"
2026-03-26 07:13:27 +02:00
nrobi144 3b489fb3cb feat(cache): seed Reads and Bookmarks screens from cache on compose
Both screens now show cached data instantly on navigation instead of
showing loading states while re-fetching from relays.

- BookmarksScreen: reads cached BookmarkListEvent from addressableNotes,
  seeds public bookmark events from notes cache
- ReadsScreen: seeds long-form notes (kind 30023) from notes cache

Relay subscriptions still run to fetch fresh data in parallel.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-26 07:13:27 +02:00
nrobi144 f27c8811d5 fix(cache): don't reset followersCount to 0 on subscription restart
Cached value now stays visible until relay data exceeds it, preventing
the count jumping from cached→0→ticking back up on each navigation.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-26 07:13:27 +02:00
nrobi144 f2169e0236 feat(cache): cache profile metadata and follower/following counts
Profile screen now reads initial values from cache so navigating back
shows data instantly instead of re-fetching everything from scratch.

- Seed displayName/about/picture from User.metadataOrNull() on compose
- Add followerCounts/followingCounts maps to DesktopLocalCache
- Write counts on each update, read on profile open
- Follower count still ticks up live (good UX) but starts from cached value
- 4 new tests for profile count caching + metadata cache reads

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-26 07:13:27 +02:00
nrobi144 3e41bab5d5 fix(cache): use relayStatuses (available) instead of connectedRelays for subscriptions
Root cause of "0 relays, 0 notes, 0 follows": all screens used
connectedRelays as the relay set for subscriptions, but NostrClient
uses relay-on-demand — relays only connect when openReqSubscription
is called with their URL. This created a deadlock: screens waited for
connected relays, but relays only connect when screens subscribe.

Fix: use relayStatuses.keys (registered/available relays) which are
populated by addDefaultRelays() at startup. openReqSubscription
triggers connection on-demand.

Affected screens: FeedScreen, UserProfileScreen, ThreadScreen,
NotificationsScreen, BookmarksScreen, ReadsScreen, NewDmDialog.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-26 07:13:27 +02:00
nrobi144 1e1bc1b172 fix(cache): add missing relay subscriptions for FeedScreen and UserProfileScreen
FeedScreen and UserProfileScreen used DesktopFeedViewModel (cache-backed)
but never subscribed to relays for kind 1/kind 3 events, causing 0 notes,
0 followed, and 0 relays on the home feed.

Adds rememberSubscription calls for:
- Contact list (kind 3) in FeedScreen → populates followedUsers
- Global/following feed (kind 1) in FeedScreen → populates cache
- Profile notes (kind 1) in UserProfileScreen → populates cache

Also adds 33 integration tests covering the full cache pipeline:
- DesktopCachePipelineTest: cache → filter → ViewModel (25 tests)
- CoordinatorPipelineTest: coordinator → cache → ViewModel (8 tests)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-26 07:13:26 +02:00
nrobi144 0621e8f7c2 refactor(cache): P3 simplifications — remove dead code, reduce abstractions
- Replace SubscriptionHealth map + data class with single lastEventAt
  Long? timestamp (only consumer was RelayHealthIndicator taking max())
- Replace consumer HashMap registry with when block (safe casts,
  compiler-checked, 9 kinds doesn't benefit from O(1) lookup)
- Remove dead loadReactionsForNotes() (superseded by requestInteractions)
- Remove unused BoundedLargeCache methods: containsKey, isEmpty,
  mapNotNull, forEach (zero external callers)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-26 07:13:26 +02:00
nrobi144 4f5665a2bd fix(cache): address review findings — subscription churn, O(n) size, atomicity
P1 fixes from compound engineering review:
- Fix subscription churn flooding relays: DisposableEffect keyed on
  feedMode/noteId (stable) instead of feedState (changes every 250ms)
- BoundedLargeCache: AtomicInteger counter replaces O(n)
  ConcurrentSkipListMap.size() — eliminates 50K traversal per event
- Non-atomic updateHealth(): use MutableStateFlow.update{} for
  atomic compare-and-set instead of read-modify-write race

P2 fixes:
- DesktopThreadFilter: LinkedHashSet for O(1) containment checks,
  down from O(R²) with MutableList
- consumeContactList: createdAt guard ensures newer events always win
- consumeEvent error logging: include event ID and relay URL

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-26 07:13:26 +02:00
nrobi144 c598c6135c feat(cache): ThreadScreen → DesktopFeedViewModel + DesktopThreadFilter
Replace EventCollectionState + 7 rememberSubscription blocks + inline
count maps with DesktopFeedViewModel + DesktopThreadFilter (graph walk).
Keep root note + replies relay subscriptions to populate cache. Use
FeedNoteCard for both root and reply notes (reads counts from Note.flowSet).
Remove ~200 lines of inline state management (zapsByEvent, reactionIdsByEvent,
replyIdsByEvent, repostIdsByEvent, bookmarkList, bookmark subscription).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-26 07:13:26 +02:00
nrobi144 8c1e54e147 feat(ui): relay health indicator in NavigationRail
Add RelayHealthIndicator component — shows elapsed time since last
relay event. Hidden when <30s (healthy), shows "45s ago" / "3m ago"
when stale. Placed above BunkerHeartbeatIndicator in NavigationRail.
Reads from Coordinator's subscriptionHealth StateFlow.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-26 07:13:26 +02:00
nrobi144 50eb03bc3e feat(cache): UserProfileScreen notes feed → DesktopFeedViewModel
Replace EventCollectionState + createUserPostsSubscription + manual cache
hydration with DesktopFeedViewModel + DesktopProfileFeedFilter. Profile
header subscriptions (metadata, contact list, followers) kept as-is since
they're not feed concerns. DisposableEffect cleanup on profile switch.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-26 07:13:26 +02:00
nrobi144 c63ace1d9c feat(cache): FeedScreen → DesktopFeedViewModel + Note-based FeedNoteCard
Rewrite FeedScreen from inline EventCollectionState + 8 rememberSubscription()
calls to cache-centric DesktopFeedViewModel pattern:

- ViewModel keyed on feedMode with DisposableEffect cleanup (destroy())
- FeedState pattern matching (Loading/Loaded/Empty/Error)
- FeedNoteCard takes Note instead of Event, reads counts from Note.flowSet
- DisposableEffect for note.clearFlow() cleanup in LazyColumn
- DisposableEffect for Coordinator interaction subscription lifecycle
- LaunchedEffect for rate-limited metadata loading via Coordinator
- Extract FeedHeader into separate composable
- Add destroy() to DesktopFeedViewModel (ViewModel.clear() is internal in KMP)
- Update UserProfileScreen to look up Note from cache for FeedNoteCard

Removes ~350 lines of inline state management (EventCollectionState,
zapsByEvent, reactionIdsByEvent, replyIdsByEvent, repostIdsByEvent,
followedUsers, bookmarkList, 8 rememberSubscription blocks).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-26 07:13:26 +02:00
nrobi144 e8ede21c17 feat(cache): Coordinator error handling + interaction subscriptions
Add try-catch in consumeEvent() so one bad event doesn't kill the
pipeline. Add requestInteractions()/releaseInteractions() with Job
tracking via ConcurrentHashMap for screen-scoped subscription lifecycle.
Add SubscriptionHealth tracking (lastEventReceivedAt, eoseReceived)
exposed as StateFlow for UI consumption.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-26 07:13:25 +02:00
nrobi144 684ba158ab feat(cache): kind-based consumer registry + new event types
Replace when-dispatch with HashMap<Int, Handler> for O(1) event routing.
Add consume methods for: RepostEvent (kind 6), ContactListEvent (kind 3),
LongTextNoteEvent (kind 30023), BookmarkListEvent (kind 30001).
Replace @Volatile followedUsers with MutableStateFlow for thread safety
and Compose observability.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-26 07:13:25 +02:00
nrobi144 e834dbd1e5 fix(ui): show display names instead of npubs in feed
toNoteDisplayData() now reads User.toBestDisplayName() from cache
instead of always showing npub. Falls back to npub if no metadata.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-26 07:13:25 +02:00
nrobi144 e80473a7d9 feat(cache): Phase 3 — route all screen events through cache
All screens now pipe relay events into DesktopLocalCache via
coordinator.consumeEvent(). Existing per-screen EventCollectionState
kept for rendering (incremental migration), but data also enters
cache for persistence across navigation.

- FeedScreen: route global + following feed events
- ThreadScreen: route root note + reply events
- UserProfileScreen: route posts + add cache hydration on mount
  (LaunchedEffect queries cache for existing posts → instant display)
- BookmarksScreen: route public + private bookmark events
- ReadsScreen: route long-form feed events (add coordinator param)
- NotificationsScreen: route notification events

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-26 07:13:25 +02:00
nrobi144 c198df57fd feat(cache): Phase 2+3 foundation — FeedFilters + DesktopFeedViewModel
Phase 2: 8 FeedFilter implementations:
- DesktopGlobalFeedFilter (kind 1, limit 2500)
- DesktopFollowingFeedFilter (kind 1 + followed pubkeys)
- DesktopThreadFilter (root + reply graph walk)
- DesktopProfileFeedFilter (notes by pubkey, limit 1000)
- DesktopBookmarkFeedFilter (notes by ID set)
- DesktopReadsFeedFilter (kind 30023, limit 500)
- DesktopNotificationFeedFilter (events tagging user)
- DesktopSearchFeedFilter (content text search)

All use AdditiveFeedFilter for incremental updates (O(batch)
after initial load). Limits are 5x Android's.

Phase 3 foundation: DesktopFeedViewModel subclass that calls
refreshSuspended() on init to load existing cache data —
fixes navigation persistence (back shows instant data).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-26 07:12:46 +02:00
nrobi144 543f7a329c feat(cache): Phase 1 — cache-centric architecture foundation
- Add BoundedLargeCache: LargeCache wrapper with size enforcement
  (50k notes, 25k users, 10k addressable). Lock-free reads via
  ConcurrentSkipListMap, evicts 10% when cap exceeded.

- Switch DesktopLocalCache from ConcurrentHashMap to BoundedLargeCache.
  Exposes filterIntoSet, values, etc. matching Android's query patterns.

- Add consume methods for kinds 1 (TextNote), 7 (Reaction),
  9734 (ZapRequest), 9735 (Zap). Uses tagsWithoutCitations() for
  reply parsing, originalPost()+taggedAddresses() for reactions,
  zappedPost() for zaps. Follows Android LocalCache pattern.

- Add consumeEvent() bridge in coordinator with BasicBundledInsert
  (250ms batching). Routes relay onEvent callbacks to cache.

- Fix SharedFlow: extraBufferCapacity=64, DROP_OLDEST. Prevents
  blocking emitters and dropped events.

- Set -Xmx2g JVM arg for long desktop sessions.

- Clear cache on logout (coordinator first, then cache).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-26 07:12:46 +02:00
Vitor Pamplona 2e9beacb13 no message 2026-03-25 16:14:20 -04:00
Vitor Pamplona bf8791088c Trying to resolve the build issue between spotless and vlcSetup without running vlcSetup when applying spotless from Claude. 2026-03-25 16:05:29 -04:00
Vitor Pamplona c903996cc2 Merge pull request #1939 from vitorpamplona/claude/nip89-client-tag-LZYhd
feat: NIP-89 client tag on all signed events
2026-03-25 15:42:31 -04:00
Vitor Pamplona e90945d201 Removing the solution to a warning about spotless dependencies because claude can't run the vlc step 2026-03-25 15:34:32 -04:00
nrobi144 e037254b39 fix(highlights): shared store, context menu, link rendering, publish, profile tabs
- Share DesktopHighlightStore and DesktopDraftStore at app level instead
  of per-column to fix cross-deck reactivity and draft persistence
- Replace broken clipboard-based highlight creation with
  LocalContextMenuRepresentation that piggybacks on Copy to get selected text
- Render highlights as highlight:// links instead of bold/italic markers
- Add collapsible highlights panel in article reader with edit/delete/publish
- Publish highlights to relays as NIP-84 events via HighlightPublishAction
- Add Reads tab (kind 30023) and Highlights tab (kind 9802) to profile
- Rewrite MarkdownToolbar with MarkdownEditorState for selection-aware
  toggle, Material icons, and keyboard shortcuts (Cmd+B/I/E/K)
- Wire DraftsScreen "New Draft" button to ArticleEditorScreen navigation

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-25 06:59:41 +02:00
Claude 36af9e2346 feat: add NIP-89 client tag to all signed events via NostrSignerWithClientTag decorator
Introduces a signer decorator that automatically appends the NIP-89
client tag to every event before signing, ensuring all events carry
client identification without modifying individual event creation sites.
Works transparently with internal, external (NIP-55), and remote (NIP-46) signers.

https://claude.ai/code/session_01WRtuk1ySCfqXzfFrieKsDs
2026-03-25 03:24:49 +00:00
davotoula 86fd92e505 feat(chess): add dismiss/clear-all for finished games in lobby
Persist dismissed game IDs locally (SharedPreferences on Android,
java.util.prefs on Desktop) so completed games can be permanently
hidden from the chess lobby. Adds per-game dismiss button and
"Clear all" action when 2+ finished games exist.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-24 21:30:46 +01:00
nrobi144 efa294ea72 feat(highlights): add article highlights and note-taking system
Phase 1: HighlightData model + DesktopHighlightStore (Preferences-based)
Phase 2: Text selection highlight via Cmd+H/Cmd+Shift+H, inline bold/italic
         rendering, HighlightAnnotationDialog, SelectionContainer wrapping
Phase 3: MyHighlightsScreen with grouped view, HighlightPublishAction for
         NIP-84 (kind 9802), deck/sidebar/menu wiring

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-24 12:16:54 +02:00
nrobi144 c75176891f feat(reads): integrate long-form into deck layout with zoom and reactions
- Wire Article, Editor, Drafts into DeckColumnType + DeckColumnContainer
- Add Drafts to sidebar nav, Add Column dialog, and Window menu
- Add article navigation (onNavigateToArticle) in SinglePaneLayout
- Add NoteActionsRow to ReadsScreen LongFormCards (zaps, reactions, replies)
- Add Cmd+/Cmd- zoom in ArticleReaderScreen via fontScale on RenderMarkdown
- Add kind 30023 to ProfileSubscription for long-form in user profiles
- Add fontScale param to RenderMarkdown using scaled LocalDensity

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-24 11:18:37 +02:00
nrobi144 328fcf86d7 refactor(reads): address all review findings — security, performance, simplicity
P1 Critical:
- Add 300ms debounce on editor preview to prevent AST re-parse per keystroke
- Hoist Regex constants in ReadingTimeCalculator and TableOfContents
- Add URI scheme allowlist to editor onLinkClick (was missing vs reader)
- Add MAX_CONTENT_BYTES (100KB) validation before publish

P2 Important:
- Delete MarkdownSpikeScreen (278 LOC spike artifact)
- Delete HighlightCreator (95 LOC, zero callers — YAGNI)
- Delete DraftLongTextNoteEvent (162 LOC, no consumers — YAGNI)
- Replace ArticleMediaRenderer interface with onLinkClick lambda
- Extract inline subscription to FilterBuilders.longFormByAddress()
- Replace SimpleDateFormat with thread-safe DateTimeFormatter
- Remove dead placeholders (bookmark button, reactions row, unused param)
- Cache draft index in memory to avoid redundant disk reads
- Add TODO for publish-before-relay-ack issue

P3 Nice-to-have:
- Remove timestamp from subscription subId for stability
- Remove redundant slug ".." removal (regex handles it)
- Add metadata field length limits (title 256, summary 1024)
- Fix identical isMacOS branches in editor
- Validate image URLs (http/https only) in ArticleHeader

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-24 11:03:29 +02:00
nrobi144 4fcbffdb3b feat(reads): Phase 2 — editor, drafts, publish, highlights
- Create DraftLongTextNoteEvent (kind 30024) in quartz with EventFactory registration
- Create LongFormPublishAction for signing + publishing kind 30023 events
- Create split-pane ArticleEditorScreen with live markdown preview
- Create MarkdownToolbar (bold, italic, heading, link, image, code, quote)
- Create MetadataPanel (title, summary, banner, tags, slug)
- Create DesktopDraftStore with JSON index + .md files, atomic writes, slug sanitization
- Create DraftsScreen with draft list, delete, new draft button
- Create HighlightCreator dialog for kind 9802 highlight creation
- Add Drafts nav rail entry and Editor/Drafts screen routing

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-24 10:59:52 +02:00
nrobi144 5ef36363cd feat(reads): Phase 1 — article reader with markdown, ToC, reading time
- Add richtext-commonmark deps to commons for shared markdown rendering
- Create RenderMarkdown composable with URL scheme allowlist (security)
- Create ArticleMediaRenderer interface for platform-specific media
- Create ArticleHeader with banner, title, author, reading time metadata
- Create TableOfContents with heading extraction and scroll-spy
- Create ReadingTimeCalculator (238 WPM prose, 80 WPM code, image decay)
- Create ArticleReaderScreen with Medium-style typography (680dp, 1.58x)
- Add DesktopScreen.Article navigation with address tag routing
- Update ReadsScreen to navigate via address tags instead of event IDs
- Responsive ToC sidebar (shown when window > 1100dp)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-24 10:59:52 +02:00
davotoula 6c8021f219 code review fixes:
Deduplicate chess notify() into shared notifyChessEvent() helper using BaseChessEvent
Remove addCompletedGameDirectly, extend moveToCompleted with optional liveState param
Hoist currentUser lookup to top of ChessLobbyContent, remove 4 duplicate remembers
Add missing imports in desktop ChessScreen, replace all FQN usages with short names
Remove redundant distinctBy in completed games UI (state already prevents duplicates)
2026-03-23 20:39:54 +01:00
davotoula c86daa6950 fix(chess): filter own games from live list, dismissable errors, clickable avatars and completed games
- Filter user's own games from public "Live Games" list in lobby
- Fix broken addSpectatingGame filter (playerPubkey is always viewerPubkey)
- Add removeGame() to clean up stale entries when "Game not found"
- Make player avatars clickable to open profile on game screen
- Make completed game cards clickable to view final board state

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-23 19:31:11 +01:00
davotoula 292c6de08a feat(chess): exit spectator mode with Leave Game button
Adds ability to leave a spectated game on both Android and Desktop:
- Added stopSpectating() to ChessLobbyLogic (removes from state + stops polling)
- Both ViewModels now delegate to logic.stopSpectating() instead of bypassing it
- LiveChessGameScreen accepts onLeaveSpectating callback shown in spectator info area
- Android: Leave Game button pops back stack and stops spectating
- Desktop: Leave Game button clears selectedGameId (returns to lobby) and stops spectating

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-23 19:31:11 +01:00
davotoula 006a2229fe feat(chess): avatar vs avatar display on board and game list
Add ChessPlayerChip.kt with three shared composables:
- ChessPlayerChip: single player avatar + name with active border
- ChessPlayerVsHeader: mirrored White vs Black header above the board
- OverlappingAvatars: compact overlapping circular avatars for list cards

Wire avatars into:
- LiveChessGameScreen: vs header with active player green border
- DesktopChessGameLayout: vs header above the chess board
- Android & Desktop lobby cards: overlapping avatars in ActiveGameCard,
  ChallengeCard, and OutgoingChallengeCard avatar slots

Uses existing UserAvatar (coil3 AsyncImage + Robohash fallback) from
commons for KMP compatibility.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-23 19:31:10 +01:00
davotoula 638bdb7b2d fix(chess): finished games transition from active to completed
Wire onGameEndDismiss callback so the "Continue" button on game end
overlay moves the game to the completed list. Add auto-detection of
finished games during polling refresh so games transition even if the
user navigates away without clicking the button. Discovered games that
are already finished now go straight to completed instead of appearing
in the active list.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-23 19:31:10 +01:00
Vitor Pamplona a39688dabb remove warnings 2026-03-23 10:46:55 -04:00
Vitor Pamplona 9d086df37a Updating all dependencies 2026-03-23 10:28:33 -04:00
Vitor Pamplona d964816fd3 Fixes build dependency bug: Spotless needs vlcSetup for... something... 2026-03-23 09:06:52 -04:00
Vitor Pamplona 439b392aa0 Moves EmptyNostrClient to a class to avoid auto-import issues 2026-03-22 15:14:49 -04:00
Vitor Pamplona cee4291ba9 Merge pull request #1878 from vitorpamplona/claude/event-sync-screen-sYGtN
Claude/event sync screen
2026-03-19 15:36:38 -04:00
Vitor Pamplona 3f4e265dc0 Brings the creation of Zap Polls back. 2026-03-19 12:40:14 -04:00
Claude 695d3c7a23 feat: deprecate NIP-04 DM sending, always use NIP-17
NIP-04 encryption for sending DMs is now deprecated. All new messages
are sent using NIP-17 (gift-wrapped sealed messages). Reading NIP-04
messages remains supported for backward compatibility.

When a recipient lacks both a DM relay list (kind 10050) and NIP-65
inbox relays, the send button is disabled and a warning is shown
explaining that messages cannot be delivered.

Changes:
- ChatNewMessageState: Remove nip17/requiresNip17 toggles, add
  recipientsMissingDmRelays state, always send via NIP-17
- ChatNewMessageViewModel: Always use NIP-17, remove NIP-04 send
  paths, add recipient relay status checking
- ToggleNip17Button -> Nip17Indicator: Replace toggle with static
  NIP-17 indicator (always on)
- PrivateMessageEditFieldRow: Show warning when recipients lack
  DM relay lists, hide message input
- ChatFileSender: sendAll() always uses NIP-17
- Desktop ChatPane: Remove NIP-17 toggle, show relay warning
- ChatroomView: Reactively check recipient DM relay availability

https://claude.ai/code/session_01T7QhUW9cZogk4DxDXbbbJJ
2026-03-19 15:22:13 +00:00
Vitor Pamplona 78ffb9fce4 Merge branch 'main' into claude/event-sync-screen-sYGtN 2026-03-19 09:54:20 -04:00
Vitor Pamplona 3f841c94ec Merge pull request #1873 from nrobi144/feat/desktop-media
feat(desktop): Full media parity — images, video, audio, encrypted DMs, upload, lightbox
2026-03-19 08:44:21 -04:00
nrobi144 5b2f2ca42b feat(chats): per-message encryption badge — lock for NIP-17, lock-open for NIP-04
Show a small lock icon next to the timestamp on each DM message:
- NIP-17 (ChatMessageEvent, ChatMessageEncryptedFileHeaderEvent): filled
  lock in primary color — relay can't see sender/recipient
- NIP-04 (PrivateDmEvent): open lock in muted gray — legacy encryption,
  metadata visible to relays

Matches Android's IncognitoBadge pattern. Both NIP-04 and NIP-17 messages
in the same 1-on-1 conversation produce identical ChatroomKeys, so they
merge into one conversation — the badge is the only way to tell them apart.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-19 09:23:06 +02:00
nrobi144 f3e55f9958 fix(chats): handle ChatMessageEncryptedFileHeaderEvent in GiftWrap unwrap
The GiftWrap inner event handler only processed ChatMessageEvent (kind 14)
and ReactionEvent. ChatMessageEncryptedFileHeaderEvent (kind 15) was
silently dropped, so encrypted file attachments never appeared in DMs.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-19 09:11:46 +02:00
nrobi144 35b58e489b feat(chats): right-click context menu on encrypted files — download + forward
Right-click on encrypted file attachments in DMs now shows a context menu:
- "Save to disk" — decrypts and saves via native file dialog
- "Forward" — placeholder callback for forwarding to another conversation

Also caches decrypted bytes (not just image bitmap) so save doesn't
re-download. Non-image files also get decrypted on load for save support.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-19 09:06:26 +02:00