feat: 帖子收藏与用户关注

- 新增收藏功能:favorite 服务与 handler、FavoriteButton 组件
- 新增关注功能:follow 服务与 handler、FollowButton 组件
- 用户主页适配关注/粉丝与收藏展示
- flash 消息工具、广告与侧边栏组件微调
This commit is contained in:
2026-09-26 03:02:40 +08:00
parent 31f5360c26
commit d7c6b3a961
40 changed files with 1528 additions and 268 deletions

View File

@@ -3,7 +3,7 @@
import { useState, useEffect, useRef } from "react";
import { useRouter } from "next/navigation";
import Link from "next/link";
import { Bell, CheckCheck, Heart, MessageCircle, CheckCircle2, XCircle, AtSign, ClipboardList, Award, AlertTriangle } from "lucide-react";
import { Bell, CheckCheck, Heart, MessageCircle, CheckCircle2, XCircle, AtSign, ClipboardList, Award, AlertTriangle, UserPlus } from "lucide-react";
import {
apiFetchNotifications,
apiUnreadCount,
@@ -99,13 +99,18 @@ export default function NotificationBell({ initialUnread = 0 }: { initialUnread?
);
}
setOpen(false);
// @提及通知跳到群聊房间;徽章通知无关联帖子(仅标已读);其余跳到帖子/评论
// @提及通知跳到群聊房间;徽章通知无关联帖子(仅标已读);关注通知跳到关注者主页;其余跳到帖子/评论
if (n.type === "mention" && n.room_id) {
router.push(`/chat/${n.room_id}`);
} else if (n.type === "pending_review") {
router.push(n.post_id ? `/post/${n.post_id}` : "/admin/content?status=pending");
} else if (n.type === "badge") {
return;
} else if (n.type === "follow") {
router.push(`/u/${n.actor_id}`);
} else if (n.comment_id > 0) {
// 评论/回复/点赞等:直达评论所在楼层(跨页由帖子页深链定位换页)
router.push(`/post/${n.post_id}#comment-${n.comment_id}`);
} else {
router.push(`/post/${n.post_id}`);
}
@@ -138,6 +143,7 @@ export default function NotificationBell({ initialUnread = 0 }: { initialUnread?
if (n.type === "deleted") return "删除了你的帖子";
if (n.type === "badge") return "向你颁布了徽章";
if (n.type === "necro_reply") return "旧帖回复提醒";
if (n.type === "follow") return "关注了你";
return "点赞了你的帖子";
};
@@ -161,6 +167,8 @@ export default function NotificationBell({ initialUnread = 0 }: { initialUnread?
return { color: "var(--accent)", bg: "var(--accent-soft)", node: <ClipboardList size={15} /> };
if (n.type === "badge")
return { color: "var(--gold)", bg: "var(--gold-soft)", node: <Award size={15} /> };
if (n.type === "follow")
return { color: "var(--accent)", bg: "var(--accent-soft)", node: <UserPlus size={15} /> };
return { color: "var(--accent)", bg: "var(--accent-soft)", node: <MessageCircle size={15} /> };
};