From e7e4a1fa5ced5b8cdcee2cd3db0b4cce50cfde3f Mon Sep 17 00:00:00 2001 From: "shakespeare.diy" Date: Wed, 18 Feb 2026 20:54:01 -0600 Subject: [PATCH] Add encrypted notification tracking with visual indicators - Add notificationsCursor to EncryptedSettings interface - Create useNotifications hook to track read/unread status - Update NotificationsPage to mark as read after 1 second - Highlight new notifications with accent background and left border - Add dot indicator to Bell icon in sidebar and mobile nav when unread - Auto-mark notifications as read when viewing the page - Sync notification cursor across devices via NIP-78 Co-authored-by: shakespeare.diy --- src/components/LeftSidebar.tsx | 11 +++- src/components/MobileBottomNav.tsx | 11 +++- src/hooks/useEncryptedSettings.ts | 2 + src/hooks/useNotifications.ts | 79 ++++++++++++++++++++++++++++ src/pages/NotificationsPage.tsx | 83 ++++++++++++++++++------------ 5 files changed, 149 insertions(+), 37 deletions(-) create mode 100644 src/hooks/useNotifications.ts diff --git a/src/components/LeftSidebar.tsx b/src/components/LeftSidebar.tsx index f039220b..b057836c 100644 --- a/src/components/LeftSidebar.tsx +++ b/src/components/LeftSidebar.tsx @@ -15,6 +15,7 @@ import { useLoggedInAccounts, type Account } from '@/hooks/useLoggedInAccounts'; import { useLoginActions } from '@/hooks/useLoginActions'; import { useTheme } from '@/hooks/useTheme'; import { useFeedSettings } from '@/hooks/useFeedSettings'; +import { useNotifications } from '@/hooks/useNotifications'; import { EXTRA_KINDS } from '@/lib/extraKinds'; import { genUserName } from '@/lib/genUserName'; import { cn } from '@/lib/utils'; @@ -25,19 +26,23 @@ interface NavItemProps { icon: React.ReactNode; label: string; active?: boolean; + showIndicator?: boolean; } -function NavItem({ to, icon, label, active }: NavItemProps) { +function NavItem({ to, icon, label, active, showIndicator }: NavItemProps) { return ( {icon} {label} + {showIndicator && ( + + )} ); } @@ -50,6 +55,7 @@ export function LeftSidebar() { const { logout } = useLoginActions(); const { theme, setTheme } = useTheme(); const { feedSettings } = useFeedSettings(); + const { hasUnread } = useNotifications(); const [loginDialogOpen, setLoginDialogOpen] = useState(false); const [signupDialogOpen, setSignupDialogOpen] = useState(false); const [accountPopoverOpen, setAccountPopoverOpen] = useState(false); @@ -138,6 +144,7 @@ export function LeftSidebar() { icon={item.icon} label={item.label} active={location.pathname === item.to} + showIndicator={item.to === '/notifications' && hasUnread} /> ))} diff --git a/src/components/MobileBottomNav.tsx b/src/components/MobileBottomNav.tsx index b010b4f5..2d0ce3b3 100644 --- a/src/components/MobileBottomNav.tsx +++ b/src/components/MobileBottomNav.tsx @@ -1,31 +1,37 @@ import { Link, useLocation } from 'react-router-dom'; import { Home, Bell, Search } from 'lucide-react'; import { cn } from '@/lib/utils'; +import { useNotifications } from '@/hooks/useNotifications'; interface NavTabProps { to: string; icon: React.ReactNode; label: string; active: boolean; + showIndicator?: boolean; } -function NavTab({ to, icon, label, active }: NavTabProps) { +function NavTab({ to, icon, label, active, showIndicator }: NavTabProps) { return ( {icon} {label} + {showIndicator && ( + + )} ); } export function MobileBottomNav() { const location = useLocation(); + const { hasUnread } = useNotifications(); return (