115 lines
4.2 KiB
TypeScript
115 lines
4.2 KiB
TypeScript
"use client";
|
||
|
||
import { useState, useEffect, useRef } from "react";
|
||
import type { ChangelogRelease } from "@/lib/changelog";
|
||
import { ChevronDown, Tag } from "lucide-react";
|
||
|
||
const PAGE_SIZE = 5;
|
||
|
||
const sectionLabels: Record<string, Record<string, string>> = {
|
||
added: { en: "New Features", cn: "新功能" },
|
||
improved: { en: "Improvements", cn: "改进" },
|
||
fixed: { en: "Bug Fixes", cn: "问题修复" },
|
||
changed: { en: "Changes", cn: "变更" },
|
||
removed: { en: "Removed", cn: "移除" },
|
||
};
|
||
|
||
function formatDate(dateStr: string, lang: string) {
|
||
const d = new Date(dateStr);
|
||
if (lang === "cn") {
|
||
return `${d.getFullYear()}年${d.getMonth() + 1}月${d.getDate()}日`;
|
||
}
|
||
return d.toLocaleDateString("en-US", { year: "numeric", month: "long", day: "numeric" });
|
||
}
|
||
|
||
function ReleaseCard({ release, lang }: { release: ChangelogRelease; lang: string }) {
|
||
const t = lang === "cn" ? { publishedOn: "发布于", download: "下载", seeGitHub: "查看 GitHub Release 获取详情" } : { publishedOn: "Published on", download: "Download", seeGitHub: "See GitHub Release for details" };
|
||
|
||
return (
|
||
<div className="py-12 border-t border-[rgba(155,176,205,0.18)]">
|
||
{/* Header */}
|
||
<div className="flex items-center justify-between gap-4 mb-8">
|
||
<div className="flex items-center gap-4">
|
||
<span className="flex items-center gap-1.5 px-3.5 py-1.5 rounded-full border border-[rgba(155,176,205,0.25)] text-sm font-semibold text-[#e2e8f0]">
|
||
<Tag size={13} className="text-[#6ea8ff]" />
|
||
{release.tag.replace("v", "")}
|
||
</span>
|
||
<span className="text-[15px] text-[#64748b]">
|
||
{t.publishedOn} {formatDate(release.date, lang)}
|
||
</span>
|
||
</div>
|
||
<a href={`https://github.com/t8y2/dbx/releases/tag/${release.tag}`} target="_blank" rel="noopener noreferrer" className="flex items-center gap-1.5 px-4 py-2 rounded-full border border-[rgba(155,176,205,0.25)] text-sm text-[#e2e8f0] hover:border-[rgba(155,176,205,0.4)] transition-colors">
|
||
{t.download}
|
||
<ChevronDown size={14} />
|
||
</a>
|
||
</div>
|
||
|
||
{/* Title */}
|
||
<h2 className="text-[28px] font-[720] text-[#f7fbff] mb-10">Release {release.tag}</h2>
|
||
|
||
{/* Sections */}
|
||
{release.sections.map((section, si) => (
|
||
<div key={si} className={si > 0 ? "mt-10" : ""}>
|
||
<h3 className="text-xl font-bold text-[#f7fbff] mb-5">{sectionLabels[section.type]?.[lang] || section.title}</h3>
|
||
<ul className="space-y-3">
|
||
{section.items.map((item, ii) => (
|
||
<li key={ii} className="flex gap-3 text-[15px] leading-relaxed text-[#b8c5d6]">
|
||
<span className="mt-2 w-1.5 h-1.5 rounded-full bg-[#475569] shrink-0" />
|
||
<span>
|
||
{item.desc ? (
|
||
<>
|
||
{item.title},{item.desc}
|
||
</>
|
||
) : (
|
||
item.title
|
||
)}
|
||
</span>
|
||
</li>
|
||
))}
|
||
</ul>
|
||
</div>
|
||
))}
|
||
|
||
{release.sections.length === 0 && <p className="text-[15px] text-[#64748b] italic">{t.seeGitHub}</p>}
|
||
</div>
|
||
);
|
||
}
|
||
|
||
export function ChangelogList({ releases, lang }: { releases: ChangelogRelease[]; lang: string }) {
|
||
const [count, setCount] = useState(PAGE_SIZE);
|
||
const sentinelRef = useRef<HTMLDivElement>(null);
|
||
|
||
useEffect(() => {
|
||
const node = sentinelRef.current;
|
||
if (!node) return;
|
||
|
||
const observer = new IntersectionObserver(
|
||
([entry]) => {
|
||
if (entry.isIntersecting) {
|
||
setCount((c) => Math.min(c + PAGE_SIZE, releases.length));
|
||
}
|
||
},
|
||
{ rootMargin: "200px" },
|
||
);
|
||
|
||
observer.observe(node);
|
||
return () => observer.disconnect();
|
||
}, [releases.length]);
|
||
|
||
const visible = releases.slice(0, count);
|
||
const hasMore = count < releases.length;
|
||
|
||
return (
|
||
<>
|
||
{visible.map((release) => (
|
||
<ReleaseCard key={release.tag} release={release} lang={lang} />
|
||
))}
|
||
{hasMore && (
|
||
<div ref={sentinelRef} className="flex justify-center py-12 text-[#64748b] text-sm">
|
||
{lang === "cn" ? "加载更多版本…" : "Loading more versions…"}
|
||
</div>
|
||
)}
|
||
</>
|
||
);
|
||
}
|