fix: Fixed a bunch of issues on linux
fix: Removed archive when unzipping with stream zip fallback
This commit is contained in:
parent
7065e64722
commit
6aacec2c0d
22 changed files with 236 additions and 83 deletions
|
|
@ -45,7 +45,7 @@ function LocalCardElement (data: { game: GameMetaExtra, i: number; } & FocusPara
|
|||
onFocus={(focusKey, node, details) =>
|
||||
{
|
||||
data.game.onFocus?.(focusKey, node, details);
|
||||
data.onFocus?.(focusKey, node, details);
|
||||
data.onFocus?.(focusKey, node, { ...details, id: data.game.id });
|
||||
}}
|
||||
onAction={handleAction}
|
||||
preview={preview}
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ export function ContextList (data: {
|
|||
{
|
||||
const context = useContext(ContextDialogContext);
|
||||
return <ul className={twMerge("list gap-1", data.className)}>
|
||||
{data.options?.map(o => <OptionElement className="list-row" key={o.id} {...o} />)}
|
||||
{data.options?.map((o, i) => <OptionElement className="list-row" key={i} {...o} />)}
|
||||
{data.showCloseButton !== false && <div className="divider m-0 "></div>}
|
||||
{data.showCloseButton !== false && <OptionElement disabled={data.disableCloseButton} className="list-row" type='accent' icon={<X />} action={() => context.close()} id="close-context-dialog" content="Close" />}
|
||||
</ul>;
|
||||
|
|
|
|||
|
|
@ -5,10 +5,11 @@ import { getErrorMessage } from "react-error-boundary";
|
|||
import toast from "react-hot-toast";
|
||||
import { useLocalStorage } from "usehooks-ts";
|
||||
import { ContextList, DialogEntry, useContextDialog } from "../ContextDialog";
|
||||
import { Clock, Download, EllipsisVertical, Import, PackageOpen, Play, TriangleAlert } from "lucide-react";
|
||||
import { Clock, Crosshair, Download, EllipsisVertical, Import, PackageOpen, Play, TriangleAlert } from "lucide-react";
|
||||
import { gameInvalidationQuery, installMutation, playMutation } from "@/mainview/scripts/queries/romm";
|
||||
import ActionButton from "./ActionButton";
|
||||
import { useRouter } from "@tanstack/react-router";
|
||||
import { DownloadSourceType } from "@/shared/constants";
|
||||
|
||||
export default function MainActions (data: { game?: FrontEndGameTypeDetailed, source: string, id: string; })
|
||||
{
|
||||
|
|
@ -29,6 +30,7 @@ export default function MainActions (data: { game?: FrontEndGameTypeDetailed, so
|
|||
const [status, setStatus] = useState<string | undefined>(undefined);
|
||||
const [error, setError] = useState<string | undefined>(undefined);
|
||||
const [details, setDetails] = useState<string | undefined>(undefined);
|
||||
const [installSources, setInstallSources] = useState<DownloadSourceType[] | undefined>(undefined);
|
||||
const [commands, setCommands] = useState<CommandEntry[] | undefined>(undefined);
|
||||
const [preferredCommand, setPreferredCommand] = useLocalStorage<string | number | undefined>(`${data.game?.source ?? data.game?.id.source}-${data.game?.source_id ?? data.game?.id.id}-preferred-command`, undefined);
|
||||
const queryClient = useQueryClient();
|
||||
|
|
@ -51,6 +53,7 @@ export default function MainActions (data: { game?: FrontEndGameTypeDetailed, so
|
|||
setProgress((e.data as any).progress);
|
||||
setDetails((e.data as any).details);
|
||||
setCommands((e.data as any).commands);
|
||||
setInstallSources((e.data as any).sources);
|
||||
|
||||
if (e.data.status === 'refresh')
|
||||
{
|
||||
|
|
@ -154,7 +157,11 @@ export default function MainActions (data: { game?: FrontEndGameTypeDetailed, so
|
|||
let icon = <span className="loading loading-spinner loading-lg"></span>;
|
||||
if (status === 'install')
|
||||
{
|
||||
icon = <Download />;
|
||||
if (installSources && installSources.length > 1)
|
||||
icon = <Crosshair />;
|
||||
else
|
||||
icon = <Download />;
|
||||
|
||||
} else if (status === 'present')
|
||||
{
|
||||
icon = <Import />;
|
||||
|
|
@ -168,7 +175,14 @@ export default function MainActions (data: { game?: FrontEndGameTypeDetailed, so
|
|||
{
|
||||
case 'present':
|
||||
case 'install':
|
||||
installMut.mutate({});
|
||||
if (installSources && installSources.length > 1)
|
||||
{
|
||||
showInstallSource(true, 'mainAction');
|
||||
} else
|
||||
{
|
||||
installMut.mutate({});
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}}
|
||||
|
|
@ -211,6 +225,19 @@ export default function MainActions (data: { game?: FrontEndGameTypeDetailed, so
|
|||
}]} />
|
||||
});
|
||||
|
||||
const { dialog: installSourcesDialog, setOpen: showInstallSource } = useContextDialog('install-source-dialog', {
|
||||
content: <ContextList options={installSources?.map(s => ({
|
||||
content: s.name,
|
||||
action (ctx)
|
||||
{
|
||||
installMut.mutate({ downloadId: s.id });
|
||||
ctx.close();
|
||||
},
|
||||
type: 'primary',
|
||||
id: s.id
|
||||
} satisfies DialogEntry)) ?? []} />
|
||||
});
|
||||
|
||||
return <div className="flex gap-2">
|
||||
{mainButton}
|
||||
<div className="divider divider-horizontal m-0"></div>
|
||||
|
|
@ -222,6 +249,7 @@ export default function MainActions (data: { game?: FrontEndGameTypeDetailed, so
|
|||
<progress className="progress progress-secondary w-full" value={progress} max="100"></progress>
|
||||
</div>
|
||||
</ActionButton>}
|
||||
{installSourcesDialog}
|
||||
{installOptionsDialog}
|
||||
{allCommandDialog}
|
||||
</div>;
|
||||
|
|
|
|||
|
|
@ -44,7 +44,7 @@ export function GamesSection (data: {
|
|||
<Carousel controlsClassName="z-20" scrollRef={containerRef} className="flex *:w-[18rem] *:min-w-[18rem] *:h-[21rem] overflow-y-hidden overflow-x-auto hide-scrollbar p-4 gap-4 justify-center-safe">
|
||||
{data.games?.map((g, i) => <FrontEndGameCard
|
||||
showSource={data.showSources}
|
||||
key={g.id.id}
|
||||
key={i}
|
||||
game={g}
|
||||
onAction={() => data.onSelect?.(g.id, FOCUS_KEYS.GAME_CARD(g.id))}
|
||||
onFocus={(key, node, details) => scrollIntoNearestParent(node, { behavior: details.instant ? 'instant' : 'smooth' })}
|
||||
|
|
|
|||
|
|
@ -155,7 +155,7 @@ function RouteComponent ()
|
|||
|
||||
useOnNavigateBack((s) => s.sound = 'returnDetails');
|
||||
|
||||
const recommendedEmulators = data?.emulators?.filter(e => e.validSources.some(em => em.exists));
|
||||
const recommendedEmulators = data?.emulators?.filter(e => e.validSources.some(em => em.exists) || e.source === 'store');
|
||||
|
||||
const { ref: intersct } = useIntersectionObserver({
|
||||
onChange: (isIntersecting, entry) =>
|
||||
|
|
|
|||
|
|
@ -156,7 +156,7 @@ function HomeList (data: {
|
|||
saveChildFocus="session"
|
||||
onFocus={(l, n, d) =>
|
||||
{
|
||||
const [source, id] = l.split('@', 1);
|
||||
const [source, id] = d.id?.split('@', 2);
|
||||
queryClient.prefetchQuery(gameQuery(source, id));
|
||||
handleNodeFocus(l, n, d);
|
||||
}}
|
||||
|
|
|
|||
|
|
@ -114,7 +114,7 @@ function Settings ()
|
|||
return "settings";
|
||||
})).map(([cat, data]) =>
|
||||
{
|
||||
return <div className='flex flex-col gap-1'>
|
||||
return <div key={cat} className='flex flex-col gap-1'>
|
||||
<div className="divider">{cat !== "settings" ? cat : <><Settings2 className='size-14' /> Settings</>}</div>
|
||||
{data?.map(([key, prop]) =>
|
||||
{
|
||||
|
|
|
|||
|
|
@ -357,6 +357,41 @@ export function RouteComponent ()
|
|||
|
||||
const stats: StatEntry[] = [];
|
||||
|
||||
if (emulator)
|
||||
{
|
||||
if (emulator.keywords)
|
||||
stats.push({ label: "Tags", content: emulator.keywords });
|
||||
if (emulator.storeDownloadInfo)
|
||||
stats.push({ label: "Version", content: `${emulator.storeDownloadInfo.version ?? "Unknown"} (${emulator.storeDownloadInfo.type})` });
|
||||
stats.push({ label: "Systems", content: emulator.systems.map(s => s.name) });
|
||||
stats.push(...emulator.validSources.flatMap(s => [{
|
||||
label: "Source", content: <div className="flex flex-col grow">
|
||||
<div className="flex grow flex-wrap justify-between gap-1">
|
||||
<div className="flex gap-1">{emulatorStatusIcons[s.type]}{s.type}</div>
|
||||
<div className="text-base-content/40">{s.binPath}</div>
|
||||
</div>
|
||||
{emulator.integrations.some(i => i.source?.type === s.type) && <div className="divider m-0"></div>}
|
||||
{emulator.integrations.filter(i => i.source?.type === s.type).map(i =>
|
||||
{
|
||||
return <div key={i.id} className="flex flex-wrap justify-between gap-1">
|
||||
<div className="flex gap-2">
|
||||
<Puzzle />
|
||||
<div>{i.id}</div>
|
||||
</div>
|
||||
<div className="flex flex-wrap text-base-content/40">
|
||||
{i.capabilities?.map(c => <><div className="divider divider-horizontal"></div><div className="flex gap-1">{capabilityIconMap[c]}{c}</div></>)}
|
||||
</div>
|
||||
</div>;
|
||||
})}
|
||||
</div>
|
||||
}]));
|
||||
if (emulator.bios)
|
||||
stats.push({
|
||||
label: "Bios", content: emulator.bios && emulator.bios.length > 0 ? emulator.bios : <div className="text-warning font-semibold">Missing</div>
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
return (
|
||||
<AnimatedBackground ref={ref} className="" scrolling>
|
||||
<AutoFocus focus={focusSelf} />
|
||||
|
|
|
|||
|
|
@ -109,7 +109,7 @@ export const installMutation = (source: string, id: string) => mutationOptions({
|
|||
mutationKey: ['install', source, id],
|
||||
mutationFn: async (init: { downloadId?: string; }) =>
|
||||
{
|
||||
const { data, error } = await rommApi.api.romm.game({ source })({ id }).install.post({ query: { downloadId: init.downloadId } });
|
||||
const { data, error } = await rommApi.api.romm.game({ source })({ id }).install.post({ downloadId: init.downloadId });
|
||||
if (error) throw error;
|
||||
return data;
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue