refactor(notifications): fetch from backend API instead of local generation

Rewrite store/notifications.ts to fetch via listNotifications
API and remove local generateId-based notification creation.
The store now acts as a simple cache with fetch/invalidate methods.
Header unread count reads from this API-backed cache.
This commit is contained in:
zetaloop
2026-05-01 04:21:03 +08:00
parent d76866ac3b
commit cd469d3d54
4 changed files with 84 additions and 74 deletions
+51 -40
View File
@@ -10,6 +10,7 @@ import { toApiError } from "@/lib/errors"
import { notifyInfo } from "@/lib/toast"
import type { Notification } from "@/lib/types"
import { cn } from "@/lib/utils"
import { useNotificationStore } from "@/store/notifications"
import { Bell, CheckCheck, Loader2, MessageSquare, ShoppingBag } from "lucide-react"
import Link from "next/link"
import { useCallback, useEffect, useMemo, useRef, useState } from "react"
@@ -100,31 +101,37 @@ export default function NotificationsPage() {
const [loading, setLoading] = useState(true)
const [loadingError, setLoadingError] = useState<string | null>(null)
const [markingAll, setMarkingAll] = useState(false)
const setStoreNotifications = useNotificationStore((state) => state.setNotifications)
const markStoreAllRead = useNotificationStore((state) => state.markAllRead)
const loadNotifications = useCallback(async function loadNotifications() {
setLoading(true)
setLoadingError(null)
const loadNotifications = useCallback(
async function loadNotifications() {
setLoading(true)
setLoadingError(null)
try {
const res = await requestWithAuth(() => listNotifications({ offset: 0, limit: 50 }), {
onUnauthorized: () => loadNotifications(),
})
try {
const res = await requestWithAuth(() => listNotifications({ offset: 0, limit: 50 }), {
onUnauthorized: () => loadNotifications(),
})
if (!mountedRef.current) return
if (!mountedRef.current) return
if (res === null) {
if (res === null) {
setLoading(false)
return
}
setNotifications(res)
setStoreNotifications(res)
setLoading(false)
return
} catch (err: unknown) {
if (!mountedRef.current) return
setLoading(false)
setLoadingError(toApiError(err).msg)
}
setNotifications(res)
setLoading(false)
} catch (err: unknown) {
if (!mountedRef.current) return
setLoading(false)
setLoadingError(toApiError(err).msg)
}
}, [])
},
[setStoreNotifications],
)
useEffect(() => {
mountedRef.current = true
@@ -134,32 +141,36 @@ export default function NotificationsPage() {
}
}, [loadNotifications])
const markAllAsRead = useCallback(async function markAllAsRead() {
if (markAllPendingRef.current) return
const markAllAsRead = useCallback(
async function markAllAsRead() {
if (markAllPendingRef.current) return
markAllPendingRef.current = true
setMarkingAll(true)
markAllPendingRef.current = true
setMarkingAll(true)
try {
const res = await requestWithAuth(() => markAllNotificationsAsRead(), {
onUnauthorized: () => markAllAsRead(),
})
try {
const res = await requestWithAuth(() => markAllNotificationsAsRead(), {
onUnauthorized: () => markAllAsRead(),
})
if (!mountedRef.current) return
if (!mountedRef.current) return
if (res === null) {
return
if (res === null) {
return
}
setNotifications((prev) => prev.map((n) => (n.read ? n : { ...n, read: true })))
markStoreAllRead()
} catch (err: unknown) {
if (!mountedRef.current) return
notifyInfo(toApiError(err).msg)
} finally {
markAllPendingRef.current = false
if (mountedRef.current) setMarkingAll(false)
}
setNotifications((prev) => prev.map((n) => (n.read ? n : { ...n, read: true })))
} catch (err: unknown) {
if (!mountedRef.current) return
notifyInfo(toApiError(err).msg)
} finally {
markAllPendingRef.current = false
if (mountedRef.current) setMarkingAll(false)
}
}, [])
},
[markStoreAllRead],
)
const unreadCount = useMemo(
() => notifications.filter((notification) => !notification.read).length,