167 lines
4.3 KiB
TypeScript
167 lines
4.3 KiB
TypeScript
import { useContext } from "react";
|
||
import { PostDetailContext } from "./context/PostDetailContext";
|
||
import { motion } from "framer-motion";
|
||
import {
|
||
CalendarIcon,
|
||
UserCircleIcon,
|
||
LockClosedIcon,
|
||
LockOpenIcon,
|
||
StarIcon,
|
||
ClockIcon,
|
||
EyeIcon,
|
||
ChatBubbleLeftIcon,
|
||
} from "@heroicons/react/24/outline";
|
||
import { Button, Typography, Space, Tooltip } from "antd";
|
||
import { useVisitor } from "@nice/client";
|
||
import { PostState, VisitType } from "@nice/common";
|
||
import {
|
||
CalendarOutlined,
|
||
ClockCircleOutlined,
|
||
CommentOutlined,
|
||
EyeOutlined,
|
||
FileTextOutlined,
|
||
FolderOutlined,
|
||
LikeFilled,
|
||
LikeOutlined,
|
||
LockOutlined,
|
||
UnlockOutlined,
|
||
UserOutlined,
|
||
} from "@ant-design/icons";
|
||
import dayjs from "dayjs";
|
||
import { TitleSection } from "./PostHeader/TitleSection";
|
||
import {
|
||
AuthorBadge,
|
||
DateBadge,
|
||
TermBadge,
|
||
UpdatedBadge,
|
||
VisibilityBadge,
|
||
} from "./PostHeader/InfoBadge";
|
||
import { StatsSection } from "./PostHeader/StatsSection";
|
||
import { PostBadge } from "./badge/PostBadge";
|
||
|
||
const { Title, Paragraph, Text } = Typography;
|
||
export default function PostHeader() {
|
||
const { post, user } = useContext(PostDetailContext);
|
||
const { like, unLike } = useVisitor();
|
||
|
||
function likeThisPost() {
|
||
if (!post?.liked) {
|
||
post.likes += 1;
|
||
post.liked = true;
|
||
like.mutateAsync({
|
||
data: {
|
||
visitorId: user?.id || null,
|
||
postId: post.id,
|
||
type: VisitType.LIKE,
|
||
},
|
||
});
|
||
} else {
|
||
post.likes -= 1;
|
||
post.liked = false;
|
||
unLike.mutateAsync({
|
||
where: {
|
||
visitorId: user?.id || null,
|
||
postId: post.id,
|
||
type: VisitType.LIKE,
|
||
},
|
||
});
|
||
}
|
||
}
|
||
|
||
return (
|
||
<motion.div
|
||
initial={{ opacity: 0, y: -20 }}
|
||
animate={{ opacity: 1, y: 0 }}
|
||
transition={{ duration: 0.5 }}
|
||
className="relative bg-gradient-to-br from-primary-250 via-primary-150 to--primary-350 rounded-lg p-6 shadow-lg border border-[#97A9C4]/30">
|
||
{/* Corner Decorations */}
|
||
<div className="absolute top-0 left-0 w-5 h-5 border-t-4 border-l-4 border-primary rounded-tl-lg" />
|
||
<div className="absolute bottom-0 right-0 w-5 h-5 border-b-4 border-r-4 border-primary rounded-br-lg" />
|
||
|
||
{/* Title Section */}
|
||
|
||
<TitleSection></TitleSection>
|
||
|
||
<div className="space-y-4">
|
||
{/* 收件人信息行 */}
|
||
<Space>
|
||
<UserOutlined className="text-secondary-400" />
|
||
<span className="text-secondary-400">收件人:</span>
|
||
<Text strong>
|
||
{post?.receivers?.map((receiver) => receiver?.showname)}
|
||
</Text>
|
||
</Space>
|
||
|
||
{/* First Row - Basic Info */}
|
||
<div className="flex flex-wrap items-center gap-1">
|
||
{/* Author Info Badge */}
|
||
<Space>
|
||
<UserOutlined className="text-secondary-400" />
|
||
<span className="text-secondary-400">发件人:</span>
|
||
<Text strong>
|
||
{post?.author?.showname || "匿名用户"}
|
||
</Text>
|
||
</Space>
|
||
<Text type="secondary">|</Text>
|
||
{/* Date Info Badge */}
|
||
<Space>
|
||
<CalendarOutlined className="text-secondary-400" />
|
||
|
||
<Text>
|
||
创建于:
|
||
{dayjs(post?.createdAt).format("YYYY-MM-DD")}
|
||
</Text>
|
||
</Space>
|
||
<Text type="secondary">|</Text>
|
||
{/* Last Updated Badge */}
|
||
<Space>
|
||
<ClockCircleOutlined className="text-secondary-400" />
|
||
<Text>
|
||
更新于:
|
||
{dayjs(post?.updatedAt).format("YYYY-MM-DD")}
|
||
</Text>
|
||
</Space>
|
||
<Text type="secondary">|</Text>
|
||
{/* Visibility Status Badge */}
|
||
<Space>
|
||
{post?.isPublic ? (
|
||
<UnlockOutlined className="text-secondary-400" />
|
||
) : (
|
||
<LockOutlined className="text-secondary-400" />
|
||
)}
|
||
<Text>{post?.isPublic ? "公开" : "私信"}</Text>
|
||
</Space>
|
||
</div>
|
||
{/* Second Row - Term and Tags */}
|
||
<div className="flex flex-wrap gap-1">
|
||
{/* Tags Badges */}
|
||
{post?.meta?.tags &&
|
||
post.meta.tags.length > 0 &&
|
||
post.meta.tags.map((tag, index) => (
|
||
<Space key={index}>
|
||
<PostBadge
|
||
type="tag"
|
||
value={`#${tag}`}></PostBadge>
|
||
</Space>
|
||
))}
|
||
</div>
|
||
</div>
|
||
|
||
{/* Content Section */}
|
||
<motion.div
|
||
initial={{ opacity: 0 }}
|
||
animate={{ opacity: 1 }}
|
||
transition={{ delay: 0.6 }}
|
||
className="mt-6 text-secondary-700">
|
||
<div
|
||
className="ql-editor p-0 space-y-4 leading-relaxed duration-300"
|
||
dangerouslySetInnerHTML={{ __html: post?.content || "" }}
|
||
/>
|
||
</motion.div>
|
||
|
||
{/* Stats Section */}
|
||
<StatsSection></StatsSection>
|
||
</motion.div>
|
||
);
|
||
}
|