mirror of
https://github.com/hoornet/vega.git
synced 2026-05-07 12:49:13 -07:00
Add Direct Messages — NIP-04 encrypted DMs (roadmap #13)
Nostr layer: - fetchDMConversations: fetches all kind-4 events to/from the user (both directions in parallel), deduplicates, newest-first - fetchDMThread: fetches both directions for a specific conversation, sorted oldest-first for display - sendDM: NIP-04 encrypts content via NDK signer, publishes kind 4 - decryptDM: decrypts regardless of direction (ECDH shared secret is symmetric — always pass the other party to signer.decrypt) UI (DMView): - Two-panel layout: conversation list (w-56) + active thread - ConvRow: avatar, name, time; shows "🔒 encrypted" preview to avoid decrypting the whole inbox on load - MessageBubble: decrypts lazily on mount; mine right-aligned, theirs left-aligned; shows "Could not decrypt" on failure - ThreadPanel: loads full thread, auto-scrolls to bottom, re-fetches after send; Ctrl+Enter to send - NewConvInput: start a new conversation by pasting an npub1 or hex pubkey; validates and resolves before opening thread - Read-only (npub) accounts see a clear "nsec required" message Navigation: - ✉ messages added to sidebar nav - openDM(pubkey) in UI store → navigates to dm view with pending pubkey - ProfileView: "✉ message" button in action row opens DM thread Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -2,7 +2,7 @@ import { create } from "zustand";
|
||||
|
||||
import { NDKEvent } from "@nostr-dev-kit/ndk";
|
||||
|
||||
type View = "feed" | "search" | "relays" | "settings" | "profile" | "thread" | "article-editor" | "about" | "zaps";
|
||||
type View = "feed" | "search" | "relays" | "settings" | "profile" | "thread" | "article-editor" | "about" | "zaps" | "dm";
|
||||
|
||||
interface UIState {
|
||||
currentView: View;
|
||||
@@ -11,10 +11,12 @@ interface UIState {
|
||||
selectedNote: NDKEvent | null;
|
||||
previousView: View;
|
||||
pendingSearch: string | null;
|
||||
pendingDMPubkey: string | null;
|
||||
setView: (view: View) => void;
|
||||
openProfile: (pubkey: string) => void;
|
||||
openThread: (note: NDKEvent, from: View) => void;
|
||||
openSearch: (query: string) => void;
|
||||
openDM: (pubkey: string) => void;
|
||||
goBack: () => void;
|
||||
toggleSidebar: () => void;
|
||||
}
|
||||
@@ -28,10 +30,12 @@ export const useUIStore = create<UIState>((set, _get) => ({
|
||||
selectedNote: null,
|
||||
previousView: "feed",
|
||||
pendingSearch: null,
|
||||
pendingDMPubkey: null,
|
||||
setView: (currentView) => set({ currentView }),
|
||||
openProfile: (pubkey) => set((s) => ({ currentView: "profile", selectedPubkey: pubkey, previousView: s.currentView as View })),
|
||||
openThread: (note, from) => set({ currentView: "thread", selectedNote: note, previousView: from }),
|
||||
openSearch: (query) => set({ currentView: "search", pendingSearch: query }),
|
||||
openDM: (pubkey) => set({ currentView: "dm", pendingDMPubkey: pubkey }),
|
||||
goBack: () => set((s) => ({
|
||||
currentView: s.previousView !== s.currentView ? s.previousView : "feed",
|
||||
selectedNote: null,
|
||||
|
||||
Reference in New Issue
Block a user