fix(notifications): add per-row mark as read
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
This commit is contained in:
parent
36ab2eba9b
commit
3d8655aa8a
1 changed files with 39 additions and 21 deletions
|
|
@ -3,7 +3,7 @@
|
|||
import { useCallback, useEffect, useRef, useState } from "react";
|
||||
import { useRouter } from "next/navigation";
|
||||
import Link from "next/link";
|
||||
import { BellIcon } from "lucide-react";
|
||||
import { BellIcon, CheckIcon } from "lucide-react";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import {
|
||||
DropdownMenu,
|
||||
|
|
@ -83,26 +83,28 @@ export function NotificationsBell() {
|
|||
};
|
||||
}, [fetchNotifications]);
|
||||
|
||||
async function handleItemClick(item: NotificationItem) {
|
||||
if (marking) return;
|
||||
if (!item.read) {
|
||||
setMarking(true);
|
||||
setActionError(null);
|
||||
try {
|
||||
const result = await markNotificationsRead([item.id]);
|
||||
if (result.success) {
|
||||
setNotifications((prev) =>
|
||||
prev.map((n) => (n.id === item.id ? { ...n, read: true } : n)),
|
||||
);
|
||||
} else {
|
||||
setActionError(result.error ?? "Failed to update notifications.");
|
||||
}
|
||||
} catch (err) {
|
||||
setActionError(err instanceof Error ? err.message : "Failed to update notifications.");
|
||||
} finally {
|
||||
setMarking(false);
|
||||
async function handleMarkOne(item: NotificationItem) {
|
||||
if (item.read || marking) return;
|
||||
setMarking(true);
|
||||
setActionError(null);
|
||||
try {
|
||||
const result = await markNotificationsRead([item.id]);
|
||||
if (result.success) {
|
||||
setNotifications((prev) =>
|
||||
prev.map((n) => (n.id === item.id ? { ...n, read: true } : n)),
|
||||
);
|
||||
} else {
|
||||
setActionError(result.error ?? "Failed to update notifications.");
|
||||
}
|
||||
} catch (err) {
|
||||
setActionError(err instanceof Error ? err.message : "Failed to update notifications.");
|
||||
} finally {
|
||||
setMarking(false);
|
||||
}
|
||||
}
|
||||
|
||||
async function handleItemClick(item: NotificationItem) {
|
||||
if (!item.read) await handleMarkOne(item);
|
||||
if (item.link) {
|
||||
router.push(item.link);
|
||||
setOpen(false);
|
||||
|
|
@ -194,8 +196,24 @@ export function NotificationsBell() {
|
|||
)}
|
||||
{item.title}
|
||||
</span>
|
||||
<span className="shrink-0 text-[10px] text-muted-foreground">
|
||||
{timeAgo(item.createdAt)}
|
||||
<span className="flex shrink-0 items-center gap-1.5">
|
||||
{!item.read && (
|
||||
<button
|
||||
type="button"
|
||||
title="Mark as read"
|
||||
aria-label={`Mark "${item.title}" as read`}
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
void handleMarkOne(item);
|
||||
}}
|
||||
className="rounded-sm p-0.5 text-muted-foreground/60 transition-colors hover:bg-accent hover:text-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring"
|
||||
>
|
||||
<CheckIcon className="size-3.5" />
|
||||
</button>
|
||||
)}
|
||||
<span className="text-[10px] text-muted-foreground">
|
||||
{timeAgo(item.createdAt)}
|
||||
</span>
|
||||
</span>
|
||||
</span>
|
||||
{item.message && (
|
||||
|
|
|
|||
Loading…
Reference in a new issue