import { createFileRoute, useNavigate, useRouter } from "@tanstack/react-router"; import { getRomApiRomsIdGetOptions } from "../../../clients/romm/@tanstack/react-query.gen"; import { DefaultRommStaleTime, RPC_URL } from "../../../shared/constants"; import { twJoin, twMerge } from "tailwind-merge"; import { JSX, Ref, RefObject, useEffect, useMemo, useRef, useState } from "react"; import { FocusContext, getCurrentFocusKey, setFocus, useFocusable } from "@noriginmedia/norigin-spatial-navigation"; import classNames from "classnames"; import { Clock, HardDrive, Image, Play, Settings, Trophy } from "lucide-react"; import ShortcutPrompt from "../../components/ShortcutPrompt"; import { HeaderUI } from "../../components/Header"; import prettyBytes from 'pretty-bytes'; import { DetailedRomSchema } from "../../../clients/romm"; import { useEventListener } from "usehooks-ts"; import { PopSource } from "../../scripts/spatialNavigation"; import { gameQueryOptions } from "../../query-options"; import { AnimatedBackground } from "../../components/AnimatedBackground"; export const Route = createFileRoute("/game/$id")({ loader: ({ params, context }) => context.queryClient.fetchQuery(gameQueryOptions(Number(params.id))), component: GameDetailsUI, pendingComponent: GameDetailsUIPending }); function GameDetailsUIPending () { return
; } export function GameDetailsUI () { // In a component! const { id } = Route.useParams(); const data = Route.useLoaderData(); const { ref, focusKey, focusSelf } = useFocusable({ focusKey: "game-details", preferredChildFocusKey: "main-details" }); const backgroundImage = `${RPC_URL(__HOST__)}/api/romm${data.path_cover_small}`; const mainAreaRef = useRef(null); useEffect(() => { focusSelf(); }, []); return (
Screenshots
); } function Details (data: { mainAreaRef: RefObject, game: DetailedRomSchema; }) { const { ref, focusKey } = useFocusable({ focusKey: 'main-details', onFocus: () => { data.mainAreaRef.current?.scrollIntoView({ block: 'start', behavior: 'smooth' }); }, preferredChildFocusKey: "play-btn", saveLastFocusedChild: false }); const navigate = useNavigate(); const platformCoverImg = `${RPC_URL(__HOST__)}/api/romm/assets/platforms/${data.game.platform_slug}.svg`; const gameCoverImg = `${RPC_URL(__HOST__)}/api/romm${data.game.path_cover_large}`; useEventListener("cancel", () => { navigate({ to: PopSource('details') ?? '/', viewTransition: { types: ['zoom-out'] } }); }); return
} >{data.game.rom_user.last_played ? new Date(data.game.rom_user.last_played).toDateString() : "Never"} } >{prettyBytes(data.game.fs_size_bytes)} } >{data.game.platform_display_name}

{data.game.summary}

; } function Screenshot (data: { url: string; index: number; setFocused?: (index: number) => void; }) { const { ref, focused, focusSelf } = useFocusable({ focusKey: `screenshot-${data.index}`, onFocus: () => { (ref.current as HTMLElement).scrollIntoView({ inline: 'center', behavior: 'smooth' }); data.setFocused?.(data.index); } }); return ; } function Screenshots (data: { screenshots: string[]; }) { const [focusedScreenshot, setFocusedScreenshot] = useState(-1); const { ref, focusKey } = useFocusable({ focusKey: 'screenshot-list', onFocus: () => (ref.current as HTMLElement).scrollIntoView({ block: 'center', behavior: 'smooth' }), onBlur: () => setFocusedScreenshot(-1) }); return
{data.screenshots.map((s, i) => )}
{data.screenshots.map((s, i) => { const focused = i === focusedScreenshot; return ; })}
; } function PlayButton () { const { focused, ref } = useFocusable({ focusKey: "play-btn" }); return (

Play

); } // function ActionButtons (data: { game: DetailedRomSchema; }) { const [hoverText, setHoverText] = useState(undefined); const { ref, focusKey } = useFocusable({ focusKey: 'actions', onBlur: () => setHoverText(undefined) }); return
setHoverText("")} type='primary' id="play">
{!!data.game.merged_ra_metadata?.achievements && setHoverText("Achievements")} type="base" id="achievements" >
{`${data.game.merged_ra_metadata.achievements.filter(a => a.type).length}/${data.game.merged_ra_metadata.achievements.length}`}
} setHoverText("Settings")} type="base" id="settings" icon={} /> {!!hoverText &&

{hoverText}

}
; } function Shortcuts () { const { ref, focusKey } = useFocusable({ focusKey: "action-buttons" }); return
; } function Detail (data: { icon: JSX.Element; children?: any | any[]; }) { return (
{data.icon} {data.children}
); } function ActionButton (data: { id: string, icon?: JSX.Element, children?: any | any[]; className?: string; type: "primary" | 'base' | "accent"; onFocus?: () => void; }) { const { ref, focused } = useFocusable({ focusKey: data.id, onFocus: data.onFocus }); const styles = { primary: twMerge("bg-primary text-primary-content rounded-full size-21 hover:bg-base-content hover:text-base-300 hover:ring-7 hover:ring-primary", classNames({ "bg-base-content text-base-300 ring-7 ring-primary": focused })), base: twMerge(" text-base-content border-dashed border-base-content/20 border-2", classNames({ "bg-base-content text-base-300 ring-7 ring-primary": focused })), accent: twMerge("bg-primary text-primary-content ", classNames({ "bg-base-content text-base-300 ring-7 ring-primary": focused })) }; return ( ); }