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 <assistant@shakespeare.diy>
This commit is contained in:
@@ -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 (
|
||||
<Link
|
||||
to={to}
|
||||
className={cn(
|
||||
'flex items-center gap-4 px-4 py-3 rounded-full transition-colors text-lg hover:bg-secondary/60',
|
||||
'flex items-center gap-4 px-4 py-3 rounded-full transition-colors text-lg hover:bg-secondary/60 relative',
|
||||
active ? 'font-bold' : 'font-normal text-muted-foreground',
|
||||
)}
|
||||
>
|
||||
{icon}
|
||||
<span>{label}</span>
|
||||
{showIndicator && (
|
||||
<span className="absolute right-4 size-2 bg-primary rounded-full" />
|
||||
)}
|
||||
</Link>
|
||||
);
|
||||
}
|
||||
@@ -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}
|
||||
/>
|
||||
))}
|
||||
|
||||
|
||||
@@ -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 (
|
||||
<Link
|
||||
to={to}
|
||||
className={cn(
|
||||
'flex flex-col items-center justify-center gap-0.5 flex-1 py-2 transition-colors',
|
||||
'flex flex-col items-center justify-center gap-0.5 flex-1 py-2 transition-colors relative',
|
||||
active ? 'text-foreground' : 'text-muted-foreground',
|
||||
)}
|
||||
>
|
||||
{icon}
|
||||
<span className="text-[10px] font-medium">{label}</span>
|
||||
{showIndicator && (
|
||||
<span className="absolute top-1.5 left-1/2 translate-x-1 size-2 bg-primary rounded-full" />
|
||||
)}
|
||||
</Link>
|
||||
);
|
||||
}
|
||||
|
||||
export function MobileBottomNav() {
|
||||
const location = useLocation();
|
||||
const { hasUnread } = useNotifications();
|
||||
|
||||
return (
|
||||
<nav className="fixed bottom-0 left-0 right-0 z-20 flex items-center bg-background/95 backdrop-blur-md border-t border-border sidebar:hidden safe-area-bottom">
|
||||
@@ -40,6 +46,7 @@ export function MobileBottomNav() {
|
||||
icon={<Bell className="size-5" />}
|
||||
label="Notifications"
|
||||
active={location.pathname === '/notifications'}
|
||||
showIndicator={hasUnread}
|
||||
/>
|
||||
<NavTab
|
||||
to="/search"
|
||||
|
||||
Reference in New Issue
Block a user