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

120 lines
3.8 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 { 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-rows-2 gap-1 text-xs text-muted-foreground mb-1">
<div className="flex gap-4">
<p>: {remainingViews.toLocaleString()}</p>
<p>: {Math.round(video.eta.speed)}/</p>
</div>
<div>
<p>: {addHoursToNow(video.eta.eta)}{formatHours(video.eta.eta)}</p>
</div>
</div>
<div className="max-sm:grid-rows-2 max-sm:grid flex gap-1 sm:gap-4 text-xs text-muted-foreground">
{video.bilibili_metadata.publishedAt && (
<span className="stat-num">
: {formatDateTime(new Date(video.bilibili_metadata.publishedAt))}
</span>
)}
<div className="flex gap-4">
<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>
</div>
</Card>
);
};