feat: Implemented launching and downloading of roms
This is just an initial implementation lots of kings to iron out
This commit is contained in:
parent
ef08fa6114
commit
f15bf9a1e0
117 changed files with 37776 additions and 1073 deletions
|
|
@ -1,15 +1,15 @@
|
|||
import { keepPreviousData, useQuery, useSuspenseQuery } from "@tanstack/react-query";
|
||||
import { getRomsApiRomsGetOptions } from "../../clients/romm/@tanstack/react-query.gen";
|
||||
import { useSuspenseQuery } from "@tanstack/react-query";
|
||||
import { GameMetaExtra, CardList } from "./CardList";
|
||||
import { DefaultRommStaleTime, RPC_URL } from "../../shared/constants";
|
||||
import { FrontEndId, RPC_URL } from "../../shared/constants";
|
||||
import { useLocation, useNavigate } from "@tanstack/react-router";
|
||||
import { Suspense, useEffect } from "react";
|
||||
import { SaveSource } from "../scripts/spatialNavigation";
|
||||
import { gamesQueryOptions } from "../query-options";
|
||||
import { rommApi } from "../scripts/clientApi";
|
||||
import { HardDrive } from "lucide-react";
|
||||
import { JSX } from "react";
|
||||
|
||||
export interface GameListFilter
|
||||
{
|
||||
platformIds?: number[];
|
||||
platformId?: number;
|
||||
collectionId?: number;
|
||||
}
|
||||
|
||||
|
|
@ -19,30 +19,39 @@ export interface GameListParams
|
|||
filters?: GameListFilter,
|
||||
grid?: boolean,
|
||||
setBackground?: (url: string) => void;
|
||||
onGameSelect?: (id: number) => void;
|
||||
onGameSelect?: (id: FrontEndId) => void;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
export function GameList (data: GameListParams)
|
||||
{
|
||||
const games = useSuspenseQuery(gamesQueryOptions(data.filters));
|
||||
const games = useSuspenseQuery({
|
||||
queryKey: ['games', data.filters ?? 'all'],
|
||||
queryFn: () => rommApi.api.romm.games.get({
|
||||
query: {
|
||||
platform_id: data.filters?.platformId,
|
||||
collection_id: data.filters?.collectionId
|
||||
}
|
||||
}).then(d => d.data)
|
||||
});
|
||||
const navigator = useNavigate();
|
||||
const location = useLocation();
|
||||
|
||||
const handleFocus = (id: number) =>
|
||||
const handleFocus = (id: FrontEndId) =>
|
||||
{
|
||||
const game = games.data?.items.find((g) => g.id === id);
|
||||
const game = games.data?.games.find((g) => g.id === id);
|
||||
if (game)
|
||||
{
|
||||
data.setBackground?.(
|
||||
`${RPC_URL(__HOST__)}/api/romm${game.path_cover_small}`,
|
||||
`${RPC_URL(__HOST__)}${game.path_cover}`,
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
function handleDefaultSelect (id: number)
|
||||
function handleDefaultSelect (id: FrontEndId, source: string | null, sourceId: number | null)
|
||||
{
|
||||
SaveSource('details', location.pathname);
|
||||
navigator({ to: '/game/$id', params: { id: String(id) }, viewTransition: { types: ['zoom-in'] } });
|
||||
SaveSource('details');
|
||||
navigator({ to: '/game/$source/$id', params: { id: String(sourceId ?? id.id), source: source ?? id.source }, viewTransition: { types: ['zoom-in'] } });
|
||||
};
|
||||
|
||||
return (
|
||||
|
|
@ -51,23 +60,34 @@ export function GameList (data: GameListParams)
|
|||
id={data.id}
|
||||
type="game"
|
||||
grid={data.grid}
|
||||
games={games.data.items.sort(
|
||||
(a, b) =>
|
||||
Date.parse(b.rom_user.last_played ?? b.updated_at) -
|
||||
Date.parse(a.rom_user.last_played ?? a.updated_at),
|
||||
)
|
||||
className={data.className}
|
||||
games={games.data?.games
|
||||
.map(
|
||||
(g) =>
|
||||
({
|
||||
id: g.id,
|
||||
{
|
||||
const badges: JSX.Element[] = [];
|
||||
if (g.id.source === 'local')
|
||||
{
|
||||
badges.push(<HardDrive className="size-8 m-1" />);
|
||||
}
|
||||
|
||||
return {
|
||||
id: `game-${g.id.source}-${g.id.id}`,
|
||||
focusKey: g.slug ?? `game-${g.id}`,
|
||||
title: g.name ?? "",
|
||||
subtitle: g.platform_display_name ?? "",
|
||||
previewUrl: `${RPC_URL(__HOST__)}/api/romm${g.path_cover_large}`,
|
||||
}) satisfies GameMetaExtra,
|
||||
)}
|
||||
onGameFocus={handleFocus}
|
||||
onSelectGame={id => data.onGameSelect ? data.onGameSelect(id) : handleDefaultSelect(id)}
|
||||
subtitle: (
|
||||
<div className="flex gap-1 items-center">
|
||||
{!!g.path_platform_cover && <img className="size-4" src={`${RPC_URL(__HOST__)}${g.path_platform_cover}`} />}
|
||||
<p className="opacity-80">{g.platform_display_name}</p>
|
||||
</div>
|
||||
),
|
||||
previewUrl: `${RPC_URL(__HOST__)}${g.path_cover}`,
|
||||
badges: badges,
|
||||
onSelect: () => data.onGameSelect ? data.onGameSelect(g.id) : handleDefaultSelect(g.id, g.source, g.source_id),
|
||||
onFocus: () => handleFocus(g.id)
|
||||
} satisfies GameMetaExtra;
|
||||
},
|
||||
) ?? []}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue