diff --git a/src/app/api/notifications/read/route.ts b/src/app/api/notifications/read/route.ts index 37dd3a28..d255ed4c 100644 --- a/src/app/api/notifications/read/route.ts +++ b/src/app/api/notifications/read/route.ts @@ -1,15 +1,30 @@ -import { countUnreadNotifications } from "@/db/notification-read-queries" +import { countUnreadNotifications, findCommentsWithPostByIds, findNotificationTargetById } from "@/db/notification-read-queries" import { markNotificationAsRead } from "@/db/notification-queries" import { apiSuccess, createUserRouteHandler, readJsonBody, requireStringField } from "@/lib/api-route" import { notificationEventBus } from "@/lib/notification-event-bus" import { invalidateNotificationUserCache } from "@/lib/notification-redis-cache" +import { revalidatePostCommentCache } from "@/lib/post-detail-cache" import { revalidateUserSurfaceCache } from "@/lib/user-surface" export const POST = createUserRouteHandler(async ({ request, currentUser }) => { const body = await readJsonBody(request) const notificationId = requireStringField(body, "notificationId", "缺少通知 ID") - await markNotificationAsRead(currentUser.id, notificationId) + const [notification] = await Promise.all([ + findNotificationTargetById(currentUser.id, notificationId), + markNotificationAsRead(currentUser.id, notificationId), + ]) + + if (notification?.relatedType === "COMMENT") { + const [comment] = await findCommentsWithPostByIds([notification.relatedId]) + if (comment?.post) { + revalidatePostCommentCache({ + postId: comment.post.id, + slug: comment.post.slug, + }) + } + } + await invalidateNotificationUserCache(currentUser.id) const unreadNotificationCount = await countUnreadNotifications(currentUser.id) revalidateUserSurfaceCache(currentUser.id) diff --git a/src/components/comment/comment-thread.tsx b/src/components/comment/comment-thread.tsx index 1f27a35b..ace7dd8e 100644 --- a/src/components/comment/comment-thread.tsx +++ b/src/components/comment/comment-thread.tsx @@ -365,11 +365,6 @@ export function CommentThread({ threadId, comments, flatComments = [], postId, p const highlightedFromSearch = searchParams.get("highlight") if (highlightedFromSearch) { triggerCommentHighlight(highlightedFromSearch) - const nextSearchParams = new URLSearchParams(searchParams.toString()) - nextSearchParams.delete("highlight") - const nextSearch = nextSearchParams.toString() - const hash = typeof window === "undefined" ? "" : window.location.hash - window.history.replaceState(null, "", `${pathname}${nextSearch ? `?${nextSearch}` : ""}${hash}`) return } @@ -437,6 +432,16 @@ export function CommentThread({ threadId, comments, flatComments = [], postId, p stableAttempts = isSettled ? stableAttempts + 1 : 0 if (stableAttempts >= COMMENT_ANCHOR_SCROLL_STABLE_ATTEMPTS && elapsed >= COMMENT_ANCHOR_SCROLL_MIN_SETTLE_MS) { + const currentUrl = new URL(window.location.href) + if (currentUrl.searchParams.get("highlight") === highlightedCommentId) { + currentUrl.searchParams.delete("highlight") + currentUrl.hash = `comment-${highlightedCommentId}` + window.history.replaceState( + window.history.state, + "", + `${currentUrl.pathname}${currentUrl.search}${currentUrl.hash}`, + ) + } return } diff --git a/src/components/notification/notification-list-item.tsx b/src/components/notification/notification-list-item.tsx index 690bf0e3..cde24dc0 100644 --- a/src/components/notification/notification-list-item.tsx +++ b/src/components/notification/notification-list-item.tsx @@ -1,6 +1,5 @@ "use client" -import Link from "next/link" import { useRouter } from "next/navigation" import { useState } from "react" import { Trash2 } from "lucide-react" @@ -25,12 +24,16 @@ export function NotificationListItem({ id, href, isRead, typeLabel, title, conte const [isDeleting, setIsDeleting] = useState(false) async function handleClick(event: React.MouseEvent) { - if (isRead || isPending) { + if (event.button !== 0 || event.metaKey || event.ctrlKey || event.shiftKey || event.altKey || isRead) { return } event.preventDefault() + if (isPending) { + return + } + setIsPending(true) try { @@ -46,7 +49,7 @@ export function NotificationListItem({ id, href, isRead, typeLabel, title, conte return } - router.push(href) + window.location.assign(href) } finally { setIsPending(false) } @@ -102,14 +105,14 @@ export function NotificationListItem({ id, href, isRead, typeLabel, title, conte - +

{title}

{content}

来源:{senderName} {isDeleting ? "删除中..." : isPending ? "跳转中..." : "点击查看详情"}
- +
) } diff --git a/src/db/notification-read-queries.ts b/src/db/notification-read-queries.ts index 4aeada25..27205096 100644 --- a/src/db/notification-read-queries.ts +++ b/src/db/notification-read-queries.ts @@ -12,6 +12,19 @@ export function countUnreadNotifications(userId: number) { }) } +export function findNotificationTargetById(userId: number, notificationId: string) { + return prisma.notification.findFirst({ + where: { + id: notificationId, + userId, + }, + select: { + relatedType: true, + relatedId: true, + }, + }) +} + export async function countUnreadNotificationsByUserIds(userIds: number[]) { const normalizedUserIds = [...new Set(userIds.filter((userId) => Number.isInteger(userId) && userId > 0))]