fix: Fixed a bunch of issues on linux

fix: Removed archive when unzipping with stream zip fallback
This commit is contained in:
Simeon Radivoev 2026-04-20 02:14:37 +03:00
parent 7065e64722
commit 6aacec2c0d
No known key found for this signature in database
GPG key ID: 6D1F209D0C277D60
22 changed files with 236 additions and 83 deletions

View file

@ -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}

View file

@ -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>;

View file

@ -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>;

View file

@ -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' })}