109 lines
4.2 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { ThumbsUp, X } from "lucide-react";
import { useState } from "react";
import type { Comment, User } from "../types";
import { formatRelativeTime, formatAbsoluteUTC } from "../utils";
import { CommentText } from "./CommentText";
interface CommentListProps {
author: User;
createdAt: string | Date;
comments: Comment[];
}
export function CommentList({ author, createdAt, comments }: CommentListProps) {
const [previewImage, setPreviewImage] = useState<string | null>(null);
return (
<>
<header className="flex items-center gap-4 mb-5">
<div className="size-10 rounded-full overflow-hidden bg-zinc-700/60">
{author.avatar_url ? (
<img src={author.avatar_url} alt="avatar" className="w-full h-full object-cover" />
) : null}
</div>
<div>
<div className="font-medium text-white/95 text-sm sm:text-base">{author.nickname}</div>
<div className="text-xs text-white/50" title={formatAbsoluteUTC(createdAt)}>
{formatRelativeTime(createdAt)}
</div>
</div>
</header>
<ul className="space-y-4 sm:space-y-5">
{comments.map((c) => (
<li key={c.cid} className="flex items-start gap-3 sm:gap-4">
<div className="size-8 rounded-full overflow-hidden bg-zinc-700/60 shrink-0">
{c.user.avatar_url ? (
<img src={c.user.avatar_url} alt="avatar" className="w-full h-full object-cover" />
) : null}
</div>
<div className="min-w-0 flex-1">
<div className="flex items-center gap-2">
<span className="font-medium text-white/95 text-sm">{c.user.nickname}</span>
<span className="text-xs text-white/50">{formatRelativeTime(c.created_at)}</span>
</div>
<p className="mt-1 text-sm leading-relaxed text-white/90 break-words">
<CommentText text={c.text} />
</p>
{c.images && c.images.length > 0 ? (
<div className="mt-2 grid grid-cols-3 gap-1.5 sm:gap-2">
{c.images.map((img, idx) => (
<button
key={idx}
type="button"
onClick={() => setPreviewImage(img.url)}
className="block overflow-hidden rounded-md bg-zinc-800/60 cursor-pointer hover:opacity-80 transition-opacity"
title="点击预览大图"
>
{/* 以真实比例显示:利用 width/height 属性提供固有尺寸CSS 设定宽度 100% 高度自适应 */}
<img
src={img.url}
alt="comment-image"
width={img.width}
height={img.height}
className="w-full h-auto"
loading="lazy"
/>
</button>
))}
</div>
) : null}
<div className="mt-2 inline-flex items-center gap-1 text-xs text-white/70">
<ThumbsUp size={14} />
<span>{c.digg_count}</span>
</div>
</div>
</li>
))}
{comments.length === 0 ? <li className="text-sm text-white/60"></li> : null}
</ul>
{/* 图片预览灯箱 */}
{previewImage && (
<div
className="fixed inset-0 z-[100] flex items-center justify-center bg-black/90 backdrop-blur-sm"
onClick={() => setPreviewImage(null)}
>
{/* 关闭按钮 */}
<button
type="button"
className="absolute top-4 right-4 w-10 h-10 rounded-full bg-white/15 text-white border border-white/20 hover:bg-white/25 transition-colors flex items-center justify-center"
onClick={() => setPreviewImage(null)}
aria-label="关闭预览"
>
<X size={20} />
</button>
{/* 大图 */}
<img
src={previewImage}
alt="preview"
className="max-w-[90vw] max-h-[90vh] object-contain"
onClick={(e) => e.stopPropagation()}
/>
</div>
)}
</>
);
}