fix: Fixed romm login, now uses token

feat: Moved romm to internal plugin
fix: Made focusing and navigation more reliable
fix: Loading errors on first time launch
This commit is contained in:
Simeon Radivoev 2026-03-28 17:32:51 +02:00
parent 7c10f4e4c2
commit 816d50ae4d
Signed by: simeonradivoev
GPG key ID: 7611A451D2A5D37A
81 changed files with 1659 additions and 1097 deletions

View file

@ -49,7 +49,11 @@ function RouteComponent ()
id={data.name}
key={data.name}
emulator={data}
onFocus={({ node, details }) => { node.scrollIntoView({ behavior: details.instant ? 'instant' : 'smooth', block: 'center' }); }}
onFocus={({ id, node, details }) =>
{
node.scrollIntoView({ behavior: details.instant ? 'instant' : 'smooth', block: 'center' });
storeContext.prefetchDetails('emulator', 'store', id);
}}
onSelect={(id, focus) => storeContext.showDetails('emulator', 'store', id, focus)}
/>
)) ?? Array.from({ length: 10 }).map((_, i) => <div key={i} className="skeleton rounded-3xl" />)}

View file

@ -1,12 +1,13 @@
import { FocusContext, getCurrentFocusKey, useFocusable } from '@noriginmedia/norigin-spatial-navigation';
import { createFileRoute, useSearch } from '@tanstack/react-router';
import { Gamepad2 } from 'lucide-react';
import { useEffect } from 'react';
import { useContext, useEffect } from 'react';
import { useInfiniteQuery } from '@tanstack/react-query';
import FrontEndGameCard from '@/mainview/components/FrontEndGameCard';
import { GetFocusedElement } from '@/mainview/scripts/spatialNavigation';
import LoadMoreButton from '@/mainview/components/LoadMoreButton';
import { storeGamesInfiniteQuery } from '@queries/store';
import { StoreContext } from '@/mainview/scripts/contexts';
export const Route = createFileRoute('/store/tab/games')({
component: RouteComponent
@ -18,6 +19,7 @@ function RouteComponent ()
const { ref, focusKey, focusSelf } = useFocusable({ focusKey: "main-area", preferredChildFocusKey: focus });
const { data, fetchNextPage, isFetchingNextPage, isFetching } = useInfiniteQuery(storeGamesInfiniteQuery);
const storeContext = useContext(StoreContext);
useEffect(() =>
{
@ -45,7 +47,11 @@ function RouteComponent ()
</div>
<div className="grid grid-cols-[repeat(auto-fill,18rem)] auto-rows-[21rem] py-2 md:px-4 gap-4 justify-center-safe">
{data?.pages.flatMap((page) => (
page.data.map((g, i) => <FrontEndGameCard onFocus={handleFocus} key={g.id.id} game={g} index={i} />))
page.data.map((g, i) => <FrontEndGameCard onFocus={(k, n, d) =>
{
storeContext.prefetchDetails('game', g.id.source, g.id.id);
handleFocus(k, n, d);
}} key={g.id.id} game={g} index={i} />))
) ?? Array.from({ length: 20 }).map((_, i) => <div key={i} className="flex flex-col gap-4">
<div className="skeleton grow w-full"></div>
<div className="skeleton h-4 w-[80%]"></div>

View file

@ -107,9 +107,9 @@ function Main (data: { games?: FrontEndGameTypeDetailed[]; })
export function RouteComponent ()
{
const { focus } = useSearch({ from: '/store/tab' });
const { data: crucialEmulators, isSuccess } = useQuery({ ...autoEmulatorsQuery, select: (data) => data.filter(e => !e.validSource && e.isCritical) });
const { data: crucialEmulators, isSuccess } = useQuery({ ...autoEmulatorsQuery, select: (data) => data.filter(e => !e.validSources.some(s => s.exists) && e.isCritical) });
const { data: featuredGames } = useQuery(storeFeaturedGamesQuery);
const { data: recommendedEmulators } = useQuery(storeEmulatorsRecommendedQuery);
const { data: recommendedEmulators } = useQuery(storeEmulatorsRecommendedQuery());
const { focusKey, ref, focusSelf } = useFocusable({ focusKey: 'main-area', preferredChildFocusKey: focus ?? "recommended-emulators" });
const storeContext = useContext(StoreContext);

View file

@ -3,9 +3,12 @@ import { FilterUI } from '@/mainview/components/Filters';
import { HeaderUI } from '@/mainview/components/Header';
import Shortcuts from '@/mainview/components/Shortcuts';
import { StoreContext } from '@/mainview/scripts/contexts';
import { gameQuery } from '@/mainview/scripts/queries/romm';
import { storeEmulatorDetailsQuery } from '@/mainview/scripts/queries/store';
import { GamePadButtonCode, useShortcutContext, useShortcuts } from '@/mainview/scripts/shortcuts';
import { mobileCheck, useStickyDataAttr } from '@/mainview/scripts/utils';
import { FocusContext, useFocusable } from '@noriginmedia/norigin-spatial-navigation';
import { useQueryClient } from '@tanstack/react-query';
import { useMatchRoute } from '@tanstack/react-router';
import { createFileRoute, Outlet } from '@tanstack/react-router';
import { zodValidator } from '@tanstack/zod-adapter';
@ -78,6 +81,7 @@ function RouteComponent ()
preferredChildFocusKey: 'top-area',
forceFocus: true
});
const queryClient = useQueryClient();
const headerRef = useRef(null);
const sentinelRef = useRef(null);
const filters: Record<string, FilterOption> = {
@ -110,11 +114,23 @@ function RouteComponent ()
};
const handlePrefetch = (type: string, source: string, id: string) =>
{
if (type === 'emulator')
{
queryClient.prefetchQuery(storeEmulatorDetailsQuery(id));
}
else if (type === 'game')
{
queryClient.prefetchQuery(gameQuery(source, id));
}
};
const isMobile = mobileCheck();
useStickyDataAttr(headerRef, sentinelRef, ref);
return <div ref={ref} className='overflow-y-scroll w-screen h-screen' >
<StoreContext value={{ showDetails: handleDetails }} >
<StoreContext value={{ showDetails: handleDetails, prefetchDetails: handlePrefetch }} >
<FocusContext.Provider value={focusKey}>
<div className="relative flex flex-col min-h-screen text-base-content z-10" >
<div ref={sentinelRef} className="h-0" />