mirror of
https://github.com/hoornet/vega.git
synced 2026-04-28 08:40:01 -07:00
Bump to v0.2.0 — Phase 2: Engagement & Reach
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 <noreply@anthropic.com>
This commit is contained in:
76
src/components/notifications/NotificationsView.tsx
Normal file
76
src/components/notifications/NotificationsView.tsx
Normal file
@@ -0,0 +1,76 @@
|
||||
import { useEffect, useRef } from "react";
|
||||
import { useUserStore } from "../../stores/user";
|
||||
import { useNotificationsStore } from "../../stores/notifications";
|
||||
import { NoteCard } from "../feed/NoteCard";
|
||||
|
||||
export function NotificationsView() {
|
||||
const { pubkey, loggedIn } = useUserStore();
|
||||
const {
|
||||
notifications,
|
||||
unreadCount,
|
||||
lastSeenAt,
|
||||
loading,
|
||||
fetchNotifications,
|
||||
markAllRead,
|
||||
} = useNotificationsStore();
|
||||
|
||||
// Capture lastSeenAt at mount time so unread highlights persist during this view session
|
||||
const prevLastSeenAtRef = useRef(lastSeenAt);
|
||||
|
||||
useEffect(() => {
|
||||
if (!pubkey) return;
|
||||
fetchNotifications(pubkey).then(() => {
|
||||
setTimeout(() => markAllRead(), 500);
|
||||
});
|
||||
}, [pubkey]);
|
||||
|
||||
if (!loggedIn || !pubkey) {
|
||||
return (
|
||||
<div className="h-full flex items-center justify-center text-text-dim text-[12px]">
|
||||
Log in to see notifications.
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="h-full flex flex-col">
|
||||
<header className="border-b border-border px-4 py-2.5 flex items-center justify-between shrink-0">
|
||||
<h1 className="text-text text-sm font-medium tracking-wide">Notifications</h1>
|
||||
{unreadCount > 0 && (
|
||||
<button
|
||||
onClick={markAllRead}
|
||||
className="text-[11px] text-text-dim hover:text-accent transition-colors"
|
||||
>
|
||||
mark all read
|
||||
</button>
|
||||
)}
|
||||
</header>
|
||||
|
||||
<div className="flex-1 overflow-y-auto">
|
||||
{loading && notifications.length === 0 && (
|
||||
<div className="px-4 py-8 text-text-dim text-[12px] text-center">
|
||||
Loading notifications…
|
||||
</div>
|
||||
)}
|
||||
|
||||
{!loading && notifications.length === 0 && (
|
||||
<div className="px-4 py-8 text-text-dim text-[12px] text-center">
|
||||
No mentions yet.
|
||||
</div>
|
||||
)}
|
||||
|
||||
{notifications.map((event) => {
|
||||
const isUnread = (event.created_at ?? 0) > prevLastSeenAtRef.current;
|
||||
return (
|
||||
<div
|
||||
key={event.id}
|
||||
className={isUnread ? "border-l-2 border-accent/40" : ""}
|
||||
>
|
||||
<NoteCard event={event} />
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user