From 3a196cb9a05480779b63dcc418355acadd93c26c Mon Sep 17 00:00:00 2001 From: Jure <44338+hoornet@users.noreply.github.com> Date: Wed, 11 Mar 2026 20:39:30 +0100 Subject: [PATCH] =?UTF-8?q?Bump=20to=20v0.2.0=20=E2=80=94=20Phase=202:=20E?= =?UTF-8?q?ngagement=20&=20Reach?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Four features shipped in this release: - Feed reply context: replies show "↩ replying to @name" above the note content; clicking fetches and opens the parent thread - NIP-65 outbox model: fetchUserRelayList + publishRelayList + fetchUserNotesNIP65 in client.ts; profile notes fetched via the author's write relays; "Publish relay list to Nostr" button in Settings (kind 10002) - Notifications: new store (notifications.ts) + NotificationsView; πŸ”” sidebar nav item with unread badge; DM nav item also shows unread conversation count; badges clear on open/select - Keyboard shortcuts: useKeyboardShortcuts hook + HelpModal; n=compose, /=search, j/k=feed nav with ring highlight, Esc=back, ?=help overlay Co-Authored-By: Claude Sonnet 4.6 --- README.md | 21 +++-- ROADMAP.md | 37 +++----- package.json | 2 +- src-tauri/tauri.conf.json | 2 +- src/App.tsx | 9 ++ src/components/dm/DMView.tsx | 12 ++- src/components/feed/ComposeBox.tsx | 1 + src/components/feed/Feed.tsx | 6 +- src/components/feed/NoteCard.tsx | 48 ++++++++++- .../notifications/NotificationsView.tsx | 76 +++++++++++++++++ src/components/profile/ProfileView.tsx | 4 +- src/components/search/SearchView.tsx | 1 + src/components/shared/HelpModal.tsx | 42 +++++++++ src/components/shared/SettingsView.tsx | 31 ++++++- src/components/sidebar/Sidebar.tsx | 44 ++++++---- src/hooks/useKeyboardShortcuts.ts | 42 +++++++++ src/lib/nostr/client.ts | 58 ++++++++++++- src/lib/nostr/index.ts | 3 +- src/stores/feed.ts | 6 +- src/stores/notifications.ts | 85 +++++++++++++++++++ src/stores/ui.ts | 7 +- src/stores/user.ts | 5 +- 22 files changed, 479 insertions(+), 63 deletions(-) create mode 100644 src/components/notifications/NotificationsView.tsx create mode 100644 src/components/shared/HelpModal.tsx create mode 100644 src/hooks/useKeyboardShortcuts.ts create mode 100644 src/stores/notifications.ts diff --git a/README.md b/README.md index 3515ad5..b0c1bdd 100644 --- a/README.md +++ b/README.md @@ -31,6 +31,7 @@ Grab the latest release from the [Releases page](https://github.com/hoornet/wrys - Global and following feeds with live relay connection - Compose notes, inline replies, full thread view - **Image paste in compose** β€” paste an image from clipboard β†’ auto-uploads and inserts the URL +- **Feed reply context** β€” replies show "↩ replying to @name"; click to jump to the parent thread - Reactions (NIP-25) with live network counts - Follow / unfollow (NIP-02) with contact list publishing - **Quote & Repost** (NIP-18) β€” one-click repost or quote with compose modal @@ -38,7 +39,12 @@ Grab the latest release from the [Releases page](https://github.com/hoornet/wrys - Long-form article editor + reader (NIP-23) β€” write with title, tags, cover image, auto-save; click any `nostr:naddr1…` link to open in the in-app reader - **Quoted note inline preview** β€” `nostr:note1…` / `nostr:nevent1…` renders as an inline card - Note rendering: images, video, mentions, hashtags, njump.me link interception -- **Direct Messages** (NIP-04) β€” conversation list, thread view, per-message decryption +- **Direct Messages** (NIP-04) β€” conversation list, thread view, per-message decryption; unread badge in sidebar +- **Notifications** β€” mentions view (πŸ”” in sidebar) with unread badge; clears on open + +**Relay & network** +- Relay management: add/remove relays with live connection status +- **NIP-65 outbox model** β€” reads user relay lists (kind 10002) so you see notes from people who publish to their own relays; publish your own relay list to Nostr from Settings **Lightning & zaps** - **Per-account NWC wallet** β€” each account remembers its own Lightning wallet; switching accounts loads the correct one automatically @@ -53,8 +59,8 @@ Grab the latest release from the [Releases page](https://github.com/hoornet/wrys - **SQLite note cache** β€” feed loads instantly from local cache on startup; profiles cached for immediate avatar display - **System tray** β€” close button hides to tray; "Quit" in tray menu to fully exit - Collapsible sidebar (icon-only mode) +- **Keyboard shortcuts** β€” `n` compose, `/` search, `j`/`k` navigate feed, `Esc` back, `?` help overlay - Search: NIP-50 full-text, `#hashtag`, people search with inline follow -- Relay management: add/remove relays with live connection status ## Stack @@ -82,11 +88,12 @@ npm run tauri build # production binary See [ROADMAP.md](./ROADMAP.md) for the full prioritised next steps. -Up next (Phase 2): -- Notifications β€” mentions, replies, DM badge, OS native alerts -- NIP-65 outbox model β€” fetch notes from the right relay set per author -- Feed reply context β€” show "↩ replying to @name" for replies in the feed -- Keyboard shortcuts β€” N, R, /, J/K, Escape, ? help overlay +Up next (Phase 3): +- NIP-17 DMs (gift wrap) β€” proper sender/recipient privacy, replacing NIP-04 +- Image lightbox β€” click to expand images full-size +- Bookmark list (NIP-51 kind 10003) +- Follow suggestions / discovery +- UI polish pass ## Support diff --git a/ROADMAP.md b/ROADMAP.md index 951b27b..5df4682 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -31,31 +31,15 @@ Bugs found during testing are fixed before Phase N+1 starts. A release is cut be --- -## Phase 2 β€” Engagement & reach -*Test v0.1.7 on Windows first. Fix any issues before starting.* +## Phase 2 β€” Engagement & reach βœ“ COMPLETE -### 5. Notifications -- No way to see mentions, replies to own notes, or incoming DMs without manually checking -- Badge on the messages nav item for unread DMs -- Notifications view: mentions of your pubkey, replies to your notes, new DMs -- System notification (OS native) for DMs and mentions β€” Tauri has a notification plugin +*Shipped in v0.1.11.* -### 6. NIP-65 outbox model (relay lists, kind 10002) -- Without NIP-65, we miss notes from people who publish to their own relay set -- On profile open: fetch their kind 10002 relay list, query those relays for their notes -- On publish: write to own relay list (configurable in settings) -- Dramatically improves note discovery and reach - -### 7. Feed reply context -- In the feed, replies look identical to top-level posts β€” no visual distinction -- Show "↩ replying to @name" above the note content for kind-1 events with `e` tags -- Clicking the context navigates to the parent note thread - -### 8. Keyboard shortcuts -- A writing-focused desktop app should be keyboard-navigable -- N β€” compose new note, R β€” reply to focused note, / β€” focus search -- J/K β€” navigate feed up/down, Escape β€” close modal/back -- Show shortcuts in a `?` help overlay +- βœ“ **Feed reply context** β€” "↩ replying to @name" shown above reply notes; click to open parent thread +- βœ“ **NIP-65 outbox model** β€” fetch user relay lists (kind 10002) for better note discovery; "Publish relay list" button in Settings; profile notes fetched via write relays +- βœ“ **Notifications** β€” mentions view with unread badge; πŸ”” nav item in sidebar; badge clears on view +- βœ“ **DM unread badge** β€” messages nav item shows badge count; clears when conversation opened +- βœ“ **Keyboard shortcuts** β€” n (compose), / (search), j/k (feed nav), Esc (back), ? (help modal) --- @@ -116,6 +100,13 @@ Bugs found during testing are fixed before Phase N+1 starts. A release is cut be ## What's already shipped +### v0.2.0 β€” Phase 2: Engagement & Reach +- **Feed reply context** β€” replies show "↩ replying to @name" above the note; click to open the parent thread +- **NIP-65 outbox model** β€” reads kind 10002 relay lists so you see notes from people who publish to their own relays; profile notes fetched via their write relays; "Publish relay list to Nostr" button in Settings +- **Notifications view** β€” πŸ”” sidebar nav item; lists recent mentions with unread badge; badge clears on open +- **DM unread badge** β€” messages nav item shows count of conversations with new messages; clears when conversation is opened +- **Keyboard shortcuts** β€” `n` focus compose, `/` focus search, `j`/`k` navigate feed with ring highlight, `Esc` go back, `?` help overlay + ### v0.1.10 - **Fix: Bitcoin QR to right edge** β€” Support page QR section uses `justify-between` so Lightning sits left, Bitcoin sits right diff --git a/package.json b/package.json index 10bfa5e..85ed934 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "wrystr", "private": true, - "version": "0.1.10", + "version": "0.2.0", "type": "module", "scripts": { "dev": "vite", diff --git a/src-tauri/tauri.conf.json b/src-tauri/tauri.conf.json index f922d9a..85dc717 100644 --- a/src-tauri/tauri.conf.json +++ b/src-tauri/tauri.conf.json @@ -1,7 +1,7 @@ { "$schema": "https://schema.tauri.app/config/2", "productName": "Wrystr", - "version": "0.1.10", + "version": "0.2.0", "identifier": "com.hoornet.wrystr", "build": { "beforeDevCommand": "npm run dev", diff --git a/src/App.tsx b/src/App.tsx index 4fd96df..f010f5d 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -12,8 +12,11 @@ import { OnboardingFlow } from "./components/onboarding/OnboardingFlow"; import { AboutView } from "./components/shared/AboutView"; import { ZapHistoryView } from "./components/zap/ZapHistoryView"; import { DMView } from "./components/dm/DMView"; +import { NotificationsView } from "./components/notifications/NotificationsView"; +import { HelpModal } from "./components/shared/HelpModal"; import { useUIStore } from "./stores/ui"; import { useUpdater } from "./hooks/useUpdater"; +import { useKeyboardShortcuts } from "./hooks/useKeyboardShortcuts"; function UpdateBanner() { const { available, version, installing, error, install, dismiss } = useUpdater(); @@ -40,10 +43,14 @@ function UpdateBanner() { function App() { const currentView = useUIStore((s) => s.currentView); + const showHelp = useUIStore((s) => s.showHelp); + const toggleHelp = useUIStore((s) => s.toggleHelp); const [onboardingDone, setOnboardingDone] = useState( () => !!localStorage.getItem("wrystr_pubkey") ); + useKeyboardShortcuts(); + if (!onboardingDone) { return setOnboardingDone(true)} />; } @@ -65,8 +72,10 @@ function App() { {currentView === "about" && } {currentView === "zaps" && } {currentView === "dm" && } + {currentView === "notifications" && } + {showHelp && } ); } diff --git a/src/components/dm/DMView.tsx b/src/components/dm/DMView.tsx index 0638bb8..220e466 100644 --- a/src/components/dm/DMView.tsx +++ b/src/components/dm/DMView.tsx @@ -2,6 +2,7 @@ import { useEffect, useRef, useState } from "react"; import { NDKEvent, nip19 } from "@nostr-dev-kit/ndk"; import { useUserStore } from "../../stores/user"; import { useUIStore } from "../../stores/ui"; +import { useNotificationsStore } from "../../stores/notifications"; import { fetchDMConversations, fetchDMThread, sendDM, decryptDM, getNDK } from "../../lib/nostr"; import { useProfile } from "../../hooks/useProfile"; import { timeAgo, shortenPubkey } from "../../lib/utils"; @@ -306,6 +307,12 @@ export function DMView() { if (!selectedPubkey && !pendingDMPubkey && grouped.size > 0) { setSelectedPubkey(Array.from(grouped.keys())[0]); } + // Compute DM unread counts + const convList = Array.from(grouped.entries()).map(([partnerPubkey, msgs]) => ({ + partnerPubkey, + lastAt: msgs[0]?.created_at ?? 0, + })); + useNotificationsStore.getState().computeDMUnread(convList); }) .finally(() => setLoading(false)); }, [pubkey, hasSigner]); @@ -354,7 +361,10 @@ export function DMView() { partnerPubkey={partner} lastEvent={events[0]} selected={selectedPubkey === partner} - onSelect={() => setSelectedPubkey(partner)} + onSelect={() => { + setSelectedPubkey(partner); + useNotificationsStore.getState().markDMRead(partner); + }} /> ))} diff --git a/src/components/feed/ComposeBox.tsx b/src/components/feed/ComposeBox.tsx index dcfbd01..ce082af 100644 --- a/src/components/feed/ComposeBox.tsx +++ b/src/components/feed/ComposeBox.tsx @@ -93,6 +93,7 @@ export function ComposeBox({ onPublished }: { onPublished?: () => void }) {