import Image from "next/image"; import PixelImage from "./PixelImage"; import Link from "next/link"; import { formatCompactNumber } from "@/utils/format"; import { useBaseRoms } from "@/contexts/BaseRomContext"; import { baseRoms } from "@/data/baseRoms"; import { useEffect, useRef, useState } from "react"; import useEmblaCarousel from "embla-carousel-react"; import { usePathname } from "next/navigation"; import { FaRegImages } from "react-icons/fa6"; import { ImDownload } from "react-icons/im"; type CardHack = { slug: string; title: string; author: string; covers: string[]; tags: string[]; downloads: number; baseRomId?: string; version: string; summary?: string; description?: string; }; export default function HackCard({ hack, clickable = true, className = "" }: { hack: CardHack; clickable?: boolean; className?: string }) { const { isLinked, hasPermission, hasCached } = useBaseRoms(); const match = baseRoms.find((r) => r.id === hack.baseRomId); const baseId = match?.id ?? undefined; const baseName = match?.name ?? undefined; const linked = baseId ? isLinked(baseId) : false; const ready = baseId ? hasPermission(baseId) || hasCached(baseId) : false; const images = (hack.covers && hack.covers.length > 0 ? hack.covers : []).filter(Boolean); const isCarousel = images.length > 1; const pathname = usePathname(); const showTitlePlaceholder = (pathname || "").startsWith("/submit") && images.length === 0; const [emblaRef, emblaApi] = useEmblaCarousel({ loop: true }); const [selectedIndex, setSelectedIndex] = useState(0); const dragStartRef = useRef<{ x: number; y: number } | null>(null); const didDragRef = useRef(false); useEffect(() => { if (!emblaApi) return; const onSelect = () => setSelectedIndex(emblaApi.selectedScrollSnap()); emblaApi.on("select", onSelect); onSelect(); return () => { emblaApi.off("select", onSelect); }; }, [emblaApi]); const cardClass = `rounded-[12px] overflow-hidden ${ clickable ? "transition-transform duration-300 hover:-translate-y-0.5 hover:shadow-xl anim-float" : "" } ring-1 ${ready ? "ring-emerald-400/50 bg-emerald-500/10" : "card ring-[var(--border)]"}`; const gradientBgClass = `bg-gradient-to-b ${ready ? 'from-emerald-300/5 to-emerald-400/30 dark:from-emerald-950/10 dark:to-emerald-600/40' : 'from-black/30 to-black/10 dark:from-black/80 dark:to-black/40'}`; const shadowClass = `shadow-xl ${ready ? "shadow-emerald-700/40 dark:shadow-emerald-200/40" : "shadow-slate-500/40 dark:shadow-slate-300/40"}`; const content = (
By {hack.author}
{(() => { const text = (hack as any).summary ?? (hack as any).description ?? ""; return text.length > 120 ? text.slice(0, 120).trimEnd() + "…" : text; })()}