feat(notifications) : read all when bell opens
This commit is contained in:
@@ -13,8 +13,8 @@ import { useSitePushDisabled } from './push-client';
|
||||
type Item = { id: string; title: string; body: string; url: string; readAt: string | null };
|
||||
type Feed = { items: Item[]; unread: number; nextCursor: string | null };
|
||||
const empty: Feed = { items: [], unread: 0, nextCursor: null };
|
||||
type State = Feed & { signedIn: boolean; busy: boolean; error: string; refresh: (more?: boolean) => Promise<void>; read: (id?: string) => Promise<void> };
|
||||
const Context = createContext<State>({ ...empty, signedIn: false, busy: false, error: '', refresh: async () => {}, read: async () => {} });
|
||||
type State = Feed & { signedIn: boolean; busy: boolean; error: string; refresh: (more?: boolean) => Promise<Feed | undefined>; read: (id?: string) => Promise<void> };
|
||||
const Context = createContext<State>({ ...empty, signedIn: false, busy: false, error: '', refresh: async () => undefined, read: async () => {} });
|
||||
export function NotificationProvider({ children }: { children: ReactNode }) {
|
||||
const { data } = authClient.useSession();
|
||||
const userId = data?.user.id;
|
||||
@@ -26,7 +26,7 @@ export function NotificationProvider({ children }: { children: ReactNode }) {
|
||||
useEffect(() => { current.current = feed; }, [feed]);
|
||||
const sequence = useRef(0);
|
||||
const invalidate = useCallback(() => { ++sequence.current; }, []);
|
||||
const refresh = useCallback(async (more = false) => {
|
||||
const refresh = useCallback(async (more = false): Promise<Feed | undefined> => {
|
||||
if (!userId) return;
|
||||
const request = ++sequence.current;
|
||||
const cursor = more ? current.current.nextCursor : null;
|
||||
@@ -38,6 +38,7 @@ export function NotificationProvider({ children }: { children: ReactNode }) {
|
||||
if (sequence.current !== request) return;
|
||||
setFeed(previous => ({ ...result, items: more ? [...previous.items, ...result.items.filter(item => !previous.items.some(old => old.id === item.id))] : result.items }));
|
||||
setOwner(userId); setError('');
|
||||
return result;
|
||||
} catch (cause) { if (sequence.current === request) setError(cause instanceof Error ? cause.message : 'โหลดการแจ้งเตือนไม่สำเร็จ'); }
|
||||
finally { if (sequence.current === request) setBusy(false); }
|
||||
}, [userId]);
|
||||
@@ -63,7 +64,15 @@ export function NotificationBell() {
|
||||
const state = useContext(Context);
|
||||
const disabled = useSitePushDisabled();
|
||||
const [open, setOpen] = useState(false);
|
||||
return <Popover open={open} onOpenChange={value => { setOpen(value); if (value) void state.refresh(); }}>
|
||||
return <Popover open={open} onOpenChange={value => {
|
||||
setOpen(value);
|
||||
if (value) {
|
||||
if (state.unread > 0) void state.read().catch(() => undefined);
|
||||
else void state.refresh().then(result => {
|
||||
if (result?.unread) void state.read().catch(() => undefined);
|
||||
});
|
||||
}
|
||||
}}>
|
||||
<PopoverTrigger render={<Button variant="ghost" size="icon" className="relative shrink-0" aria-label={`การแจ้งเตือน${disabled ? ' ปิดชั่วคราว' : state.unread ? ` ใหม่ ${state.unread} รายการ` : ''}`} />}>{disabled ? <BellOffIcon /> : <BellIcon />}
|
||||
{!disabled && state.unread > 0 && <Badge variant="destructive" className="absolute -right-1 -top-1 min-w-5 justify-center rounded-full bg-destructive px-1 text-background dark:bg-destructive">{state.unread > 9 ? '9+' : state.unread}</Badge>}
|
||||
</PopoverTrigger>
|
||||
|
||||
Reference in New Issue
Block a user