Files
gerbeur/src/components/DumpCard.tsx

102 lines
3.1 KiB
TypeScript

import { Link, useNavigate } from "react-router";
import type { Dump } from "../model.ts";
import { relativeTime } from "../utils/relativeTime.ts";
import { isDumpVisited, isRecent, markDumpVisited } from "../utils/visited.ts";
import FilePreview from "./FilePreview.tsx";
import RichContentCard from "./RichContentCard.tsx";
import { VoteButton } from "./VoteButton.tsx";
import { Markdown } from "./Markdown.tsx";
interface DumpCardProps {
dump: Dump;
voteCount: number;
voted: boolean;
canVote: boolean;
castVote: (id: string) => void;
removeVote: (id: string) => void;
className?: string;
isOwner?: boolean;
}
export function DumpCard(
{ dump, voteCount, voted, canVote, castVote, removeVote, className, isOwner }:
DumpCardProps,
) {
const navigate = useNavigate();
const unread = !isOwner && isRecent(dump.createdAt) &&
!isDumpVisited(dump.id);
function handleNavigate() {
markDumpVisited(dump.id);
navigate(`/dumps/${dump.id}`);
}
return (
<li className={`dump-card${className ? ` ${className}` : ""}`}>
<div
className="dump-card-inner"
onClick={handleNavigate}
>
<div
className="dump-card-preview"
onClick={dump.richContent ? (e) => e.stopPropagation() : undefined}
>
{dump.kind === "file"
? <FilePreview dump={dump} compact />
: dump.richContent
? <RichContentCard richContent={dump.richContent} compact />
: <span className="dump-card-preview-icon">🔗</span>}
</div>
<div className="dump-card-body">
<Link
to={`/dumps/${dump.id}`}
className="dump-card-title"
onClick={(e) => {
e.stopPropagation();
markDumpVisited(dump.id);
}}
>
{unread && <span className="unread-dot" aria-hidden="true" />}
{dump.title}
</Link>
{dump.comment && (
<Markdown className="dump-card-comment" inline>
{dump.comment}
</Markdown>
)}
<div className="dump-card-meta">
<time
className="dump-card-date"
dateTime={dump.createdAt.toISOString()}
title={dump.createdAt.toLocaleString()}
>
{relativeTime(dump.createdAt)}
</time>
{dump.commentCount > 0 && (
<span className="dump-card-comment-count">
{dump.commentCount}{" "}
{dump.commentCount === 1 ? "comment" : "comments"}
</span>
)}
{dump.isPrivate && isOwner && (
<span className="dump-card-private-badge">private</span>
)}
</div>
</div>
<div className="dump-card-vote" onClick={(e) => e.stopPropagation()}>
<VoteButton
dumpId={dump.id}
count={voteCount}
voted={voted}
disabled={!canVote}
onCast={castVote}
onRemove={removeVote}
/>
</div>
</div>
</li>
);
}