fix: Issues with launching and installation on the steam deck
This commit is contained in:
parent
dc0f2d150a
commit
ccc5a05ed7
19 changed files with 247 additions and 80 deletions
|
|
@ -7,17 +7,22 @@ import { GamePadButtonCode, Shortcut, useShortcuts } from "../scripts/shortcuts"
|
|||
import { ContextDialogContext } from "../scripts/contexts";
|
||||
import { FOCUS_KEYS } from "../scripts/types";
|
||||
|
||||
export function ContextList (data: { options?: DialogEntry[]; className?: string; showCloseButton?: boolean; })
|
||||
export function ContextList (data: {
|
||||
options?: DialogEntry[];
|
||||
className?: string;
|
||||
showCloseButton?: boolean;
|
||||
disableCloseButton?: boolean;
|
||||
})
|
||||
{
|
||||
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} />)}
|
||||
<div className="divider m-0 "></div>
|
||||
{data.showCloseButton !== false && <OptionElement className="list-row" type='accent' icon={<X />} action={() => context.close()} id="close-context-dialog" content="Close" />}
|
||||
{data.showCloseButton !== false && <OptionElement disabled={data.disableCloseButton} className="list-row" type='accent' icon={<X />} action={() => context.close()} id="close-context-dialog" content="Close" />}
|
||||
</ul>;
|
||||
}
|
||||
|
||||
export function OptionElement (data: DialogEntry & { onFocus?: () => void; className?: string; })
|
||||
export function OptionElement (data: DialogEntry & { onFocus?: () => void; className?: string; disabled?: boolean; })
|
||||
{
|
||||
const context = useContext(ContextDialogContext);
|
||||
const handleFocus = () =>
|
||||
|
|
@ -25,7 +30,11 @@ export function OptionElement (data: DialogEntry & { onFocus?: () => void; class
|
|||
(ref.current as HTMLElement).scrollIntoView({ block: 'nearest', behavior: 'smooth' });
|
||||
data.onFocus?.();
|
||||
};
|
||||
const handleAction = data.action ? () => data.action?.({ close: context.close, focus: focusSelf }) : undefined;
|
||||
const handleAction = () =>
|
||||
{
|
||||
if (data.disabled === true) return;
|
||||
data.action?.({ close: context.close, focus: focusSelf });
|
||||
};
|
||||
const { ref, focusSelf, focusKey } = useFocusable({
|
||||
focusKey: FOCUS_KEYS.CONTEXT_DIALOG_OPTION(context.id, data.id),
|
||||
onEnterPress: data.shortcuts ? undefined : handleAction,
|
||||
|
|
@ -47,6 +56,7 @@ export function OptionElement (data: DialogEntry & { onFocus?: () => void; class
|
|||
return <li ref={ref}
|
||||
onClick={handleAction}
|
||||
data-selected={data.selected}
|
||||
aria-disabled={data.disabled}
|
||||
className={
|
||||
twMerge("flex cursor-pointer sm:text-sm md:text-base group-focusable scroll-m-4")}>
|
||||
<FocusContext value={focusKey}>
|
||||
|
|
@ -72,12 +82,13 @@ export interface DialogEntry
|
|||
shortcuts?: Shortcut[];
|
||||
}
|
||||
|
||||
export function useContextDialog (id: string, data: { content?: JSX.Element; className?: string; preferredChildFocusKey?: string; onClose?: () => void; })
|
||||
export function useContextDialog (id: string, data: { content?: JSX.Element; className?: string; preferredChildFocusKey?: string; onClose?: () => void; canClose?: boolean; })
|
||||
{
|
||||
const [open, setOpen] = useState(false);
|
||||
const [sourceFocusKey, setSourceFocusKey] = useState<string | undefined>(undefined);
|
||||
const handleClose = (value: boolean, newSourceFocusKey?: string) =>
|
||||
{
|
||||
if (data.canClose === false) return;
|
||||
if (value === open) return;
|
||||
if (value)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { deleteGameMutation } from "@/mainview/scripts/queries/romm";
|
||||
import { deleteGameMutation, gameInvalidationQuery } from "@/mainview/scripts/queries/romm";
|
||||
import { FocusContext, setFocus, useFocusable } from "@noriginmedia/norigin-spatial-navigation";
|
||||
import { useMutation } from "@tanstack/react-query";
|
||||
import { ContextList, DialogEntry, useContextDialog } from "../ContextDialog";
|
||||
|
|
@ -9,6 +9,8 @@ import MainActions from "./MainActions";
|
|||
import ActionButton from "./ActionButton";
|
||||
import { useLocalStorage } from "usehooks-ts";
|
||||
import FocusTooltip from "../FocusTooltip";
|
||||
import { Router } from "@/mainview";
|
||||
import { useBlocker } from "@tanstack/react-router";
|
||||
|
||||
function AchievementsInfo (data: { game: FrontEndGameTypeDetailed; } & InteractParams)
|
||||
{
|
||||
|
|
@ -35,10 +37,9 @@ export default function ActionButtons (data: { game?: FrontEndGameTypeDetailed,
|
|||
const { ref, focusKey, hasFocusedChild } = useFocusable({ focusKey: 'actions', forceFocus: true, trackChildren: true, preferredChildFocusKey: 'mainAction' });
|
||||
const deleteMutation = useMutation({
|
||||
...deleteGameMutation({ id: data.id, source: data.source }),
|
||||
onSuccess: () =>
|
||||
onSuccess: (d, v, r, ctx) =>
|
||||
{
|
||||
location.reload();
|
||||
console.log("Deleted");
|
||||
ctx.client.invalidateQueries(gameInvalidationQuery(data.id, data.source)).then(() => Router.history.back());
|
||||
},
|
||||
onError (error)
|
||||
{
|
||||
|
|
@ -46,22 +47,35 @@ export default function ActionButtons (data: { game?: FrontEndGameTypeDetailed,
|
|||
}
|
||||
});
|
||||
|
||||
useBlocker({ shouldBlockFn: () => deleteMutation.isPending });
|
||||
|
||||
const contextOptions: DialogEntry[] = [];
|
||||
if (data.game?.local)
|
||||
{
|
||||
contextOptions.push({
|
||||
id: 'delete',
|
||||
action: () =>
|
||||
{
|
||||
deleteMutation.mutate();
|
||||
},
|
||||
icon: <Trash />,
|
||||
content: "Delete",
|
||||
type: 'error'
|
||||
});
|
||||
if (deleteMutation.isPending)
|
||||
{
|
||||
contextOptions.push({
|
||||
id: 'delete',
|
||||
icon: <span className="loading loading-spinner loading-lg"></span>,
|
||||
content: "Deleting",
|
||||
type: 'error'
|
||||
});
|
||||
} else
|
||||
{
|
||||
contextOptions.push({
|
||||
id: 'delete',
|
||||
action: () =>
|
||||
{
|
||||
deleteMutation.mutate();
|
||||
},
|
||||
icon: <Trash />,
|
||||
content: "Delete",
|
||||
type: 'error'
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
const { setOpen, dialog: settingsDialog } = useContextDialog("settings-context", { content: <ContextList options={contextOptions} /> });
|
||||
const { setOpen, dialog: settingsDialog } = useContextDialog("settings-context", { content: <ContextList disableCloseButton={deleteMutation.isPending} options={contextOptions} />, canClose: !deleteMutation.isPending });
|
||||
|
||||
return <div ref={ref} className="flex sm:gap-2 md:gap-4 sm:h-16 md:h-32 overflow-hidden p-2 items-center shrink-0">
|
||||
<FocusContext value={focusKey}>
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ 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 { installMutation, playMutation } from "@/mainview/scripts/queries/romm";
|
||||
import { gameInvalidationQuery, installMutation, playMutation } from "@/mainview/scripts/queries/romm";
|
||||
import ActionButton from "./ActionButton";
|
||||
|
||||
export default function MainActions (data: { game?: FrontEndGameTypeDetailed, source: string, id: string; })
|
||||
|
|
@ -53,8 +53,17 @@ export default function MainActions (data: { game?: FrontEndGameTypeDetailed, so
|
|||
|
||||
if (e.data.status === 'refresh')
|
||||
{
|
||||
queryClient.invalidateQueries({ queryKey: ['game', data.id] });
|
||||
Router.navigate({ to: '/game/$source/$id', params: { id: data.id, source: data.source }, replace: true });
|
||||
const localId = e.data.localId;
|
||||
queryClient.refetchQueries(gameInvalidationQuery(localId ? 'local' : data.source, localId ? String(localId) : data.id)).then(() =>
|
||||
{
|
||||
if (localId)
|
||||
{
|
||||
Router.navigate({ to: '/game/$source/$id', params: { id: String(localId), source: 'local' }, replace: true });
|
||||
} else
|
||||
{
|
||||
Router.navigate({ to: '/game/$source/$id', params: { id: data.id, source: data.source }, replace: true });
|
||||
}
|
||||
});
|
||||
} else if (e.data.status === 'error')
|
||||
{
|
||||
const errorMessage = getErrorMessage(e.data.error);
|
||||
|
|
@ -171,12 +180,13 @@ export default function MainActions (data: { game?: FrontEndGameTypeDetailed, so
|
|||
}
|
||||
|
||||
const { dialog: allCommandDialog, setOpen: showAllCommands } = useContextDialog('all-commands-dialog', {
|
||||
content: <ContextList options={validCommands.map(c =>
|
||||
content: <ContextList options={validCommands.map((c, i) =>
|
||||
{
|
||||
const commands: DialogEntry = {
|
||||
id: String(c.id),
|
||||
content: c.label ?? "",
|
||||
type: 'primary',
|
||||
selected: preferredCommand !== undefined ? preferredCommand === c.id : i === 0,
|
||||
action (ctx)
|
||||
{
|
||||
setPreferredCommand(c.id);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue