1
0
cvsa/packages/temp_frontend/app/routes/home/MilestoneVideoCard.tsx

120 lines
3.7 KiB
TypeScript

import { Card } from "@/components/ui/card";
import { Progress } from "@/components/ui/progress";
import { formatDateTime } from "@/components/SearchResults";
import { addHoursToNow, formatHours } from "../song/[id]/info";
import { milestoneConfig, type CloseMilestoneInfo, type MilestoneType } from "./Milestone";
import { DAY, HOUR, MINUTE, SECOND } from "@core/lib";
function timeAgo(timeStamp: Date | number, now: Date | number = Date.now()): string {
const pastTime = typeof timeStamp === "number" ? timeStamp : timeStamp.getTime();
const currentTime = typeof now === "number" ? now : now.getTime();
const diffMs = Math.abs(currentTime - pastTime);
if (diffMs < MINUTE) {
const seconds = Math.floor(diffMs / SECOND);
return `${seconds}`;
}
if (diffMs < HOUR) {
const minutes = Math.floor(diffMs / MINUTE);
if (diffMs % MINUTE === 0) {
return `${minutes} 分钟`;
}
return `${minutes} 分钟`;
}
if (diffMs < DAY) {
const hours = Math.floor(diffMs / HOUR);
const minutes = Math.floor((diffMs % HOUR) / MINUTE);
return `${hours}${minutes}`;
}
if (diffMs >= DAY) {
const days = Math.floor(diffMs / DAY);
const hours = Math.floor((diffMs % DAY) / HOUR);
return `${days}${hours}`;
}
return "刚刚";
}
export const MilestoneVideoCard = ({
video,
milestoneType,
}: {
video: NonNullable<CloseMilestoneInfo>[number];
milestoneType: MilestoneType;
}) => {
const config = milestoneConfig[milestoneType];
const remainingViews = config.target - video.eta.currentViews;
const progressPercentage = (video.eta.currentViews / config.target) * 100;
return (
<Card className="px-3 max-md:py-3 md:px-4 my-4 gap-0">
<div className="w-full flex items-start space-x-4 mb-4">
{video.bilibili_metadata.coverUrl && (
<img
src={video.bilibili_metadata.coverUrl}
alt="视频封面"
className="h-25 w-40 rounded-sm object-cover shrink-0"
referrerPolicy="no-referrer"
loading="lazy"
/>
)}
<div className="flex flex-col w-full justify-between">
<h3 className="text-sm sm:text-lg font-medium line-clamp-2 text-wrap mb-2">
<a href={`/song/av${video.bilibili_metadata.aid}/info`} className="hover:underline">
{video.bilibili_metadata.title}
</a>
</h3>
<div className="space-y-2 text-xs text-muted-foreground">
<div className="flex items-center justify-between">
<span>: {video.eta.currentViews.toLocaleString()}</span>
<span>: {config.target.toLocaleString()}</span>
</div>
<Progress value={progressPercentage} />
</div>
</div>
</div>
<div className="grid grid-cols-2 gap-5 text-xs text-muted-foreground mb-2">
<div>
<p>: {remainingViews.toLocaleString()}</p>
<p>: {formatHours(video.eta.eta)}</p>
</div>
<div>
<p>: {Math.round(video.eta.speed)}/</p>
<p>: {addHoursToNow(video.eta.eta)}</p>
</div>
</div>
<div className="flex gap-4 text-xs text-muted-foreground">
{video.bilibili_metadata.publishedAt && (
<span className="stat-num">
{formatDateTime(new Date(video.bilibili_metadata.publishedAt))}
</span>
)}
<span>
{timeAgo(new Date(video.eta.updatedAt))}
</span>
<a
href={`https://www.bilibili.com/video/av${video.bilibili_metadata.aid}`}
target="_blank"
rel="noopener noreferrer"
className="text-pink-400 text-xs hover:underline"
>
</a>
<a
href={`/song/av${video.bilibili_metadata.aid}/info`}
className="text-xs text-secondary-foreground hover:underline"
>
</a>
</div>
</Card>
);
};