import Image from "next/image"; import Link from "next/link"; import { formatCompactNumber } from "@/utils/format"; import { useBaseRoms } from "@/contexts/BaseRomContext"; import type { Hack } from "@/data/hacks"; import { useEffect, useState } from "react"; import useEmblaCarousel from "embla-carousel-react"; import { usePathname } from "next/navigation"; // Using shared Hack type from data export default function HackCard({ hack, clickable = true, className = "" }: { hack: Hack; clickable?: boolean; className?: string }) { const { isLinked, hasPermission, hasCached } = useBaseRoms(); const linked = isLinked(hack.baseRom); const ready = hasPermission(hack.baseRom) || hasCached(hack.baseRom); 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); 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 content = (
By {hack.author}
{(() => { const text = (hack as any).summary ?? (hack as any).description ?? ""; return text.length > 120 ? text.slice(0, 120).trimEnd() + "…" : text; })()}