feat: Added way to update the local games from romm when IDs change based on IGDB or Retro Achievement ID
Fixes #2
This commit is contained in:
parent
7948bd24fa
commit
4806f3487a
9 changed files with 143 additions and 52 deletions
|
|
@ -1,10 +1,10 @@
|
|||
import { deleteGameMutation, gameInvalidationQuery } from "@/mainview/scripts/queries/romm";
|
||||
import { deleteGameMutation, fixSourceMutation, gameInvalidationQuery, validateSourceQuery } from "@/mainview/scripts/queries/romm";
|
||||
import { FocusContext, setFocus, useFocusable } from "@noriginmedia/norigin-spatial-navigation";
|
||||
import { useMutation } from "@tanstack/react-query";
|
||||
import { useMutation, useQuery } from "@tanstack/react-query";
|
||||
import { ContextList, DialogEntry, useContextDialog } from "../ContextDialog";
|
||||
import { getErrorMessage } from "react-error-boundary";
|
||||
import toast from "react-hot-toast";
|
||||
import { Settings, Trash, Trophy } from "lucide-react";
|
||||
import { Hammer, Settings, Trash, Trophy } from "lucide-react";
|
||||
import MainActions from "./MainActions";
|
||||
import ActionButton from "./ActionButton";
|
||||
import { useLocalStorage } from "usehooks-ts";
|
||||
|
|
@ -33,6 +33,18 @@ export default function ActionButtons (data: { game?: FrontEndGameTypeDetailed,
|
|||
{
|
||||
const [, setDetailsSection] = useLocalStorage('details-section', 'screenshots');
|
||||
|
||||
const fixMutation = useMutation({
|
||||
...fixSourceMutation, onSuccess (data, variables, onMutateResult, context)
|
||||
{
|
||||
if (onMutateResult) toast.success("Updated Source");
|
||||
context.client.invalidateQueries(gameInvalidationQuery(variables.id, variables.source)).then(() => router.history.back());
|
||||
},
|
||||
onError (error)
|
||||
{
|
||||
toast.error(getErrorMessage(error) ?? "Error While Trying To Fix");
|
||||
}
|
||||
});
|
||||
const { data: validation } = useQuery(validateSourceQuery(data.source, data.id));
|
||||
const { ref, focusKey, hasFocusedChild } = useFocusable({ focusKey: 'actions', forceFocus: true, trackChildren: true, preferredChildFocusKey: 'mainAction' });
|
||||
const router = useRouter();
|
||||
const deleteMutation = useMutation({
|
||||
|
|
@ -47,32 +59,41 @@ export default function ActionButtons (data: { game?: FrontEndGameTypeDetailed,
|
|||
}
|
||||
});
|
||||
|
||||
useBlocker({ shouldBlockFn: () => deleteMutation.isPending });
|
||||
useBlocker({
|
||||
shouldBlockFn: () =>
|
||||
{
|
||||
return deleteMutation.isPending || fixMutation.isPending;
|
||||
}
|
||||
});
|
||||
|
||||
const contextOptions: DialogEntry[] = [];
|
||||
if (data.game?.local)
|
||||
{
|
||||
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'
|
||||
});
|
||||
}
|
||||
contextOptions.push({
|
||||
id: 'delete',
|
||||
action: () =>
|
||||
{
|
||||
deleteMutation.mutate();
|
||||
},
|
||||
icon: deleteMutation.isPending ? <span className="loading loading-spinner loading-lg"></span> : <Trash />,
|
||||
content: deleteMutation.isPending ? "Deleting" : "Delete",
|
||||
type: 'error'
|
||||
});
|
||||
}
|
||||
|
||||
if (!validation?.valid)
|
||||
{
|
||||
contextOptions.push({
|
||||
id: "fix_source",
|
||||
action (ctx)
|
||||
{
|
||||
if (data.game)
|
||||
fixMutation.mutate({ source: data.game.id.source, id: data.game.id.id });
|
||||
},
|
||||
icon: fixMutation.isPending ? <span className="loading loading-spinner loading-lg"></span> : <Hammer />,
|
||||
content: "Try Fix Source",
|
||||
type: "warning"
|
||||
});
|
||||
}
|
||||
|
||||
const { setOpen, dialog: settingsDialog } = useContextDialog("settings-context", { content: <ContextList disableCloseButton={deleteMutation.isPending} options={contextOptions} />, canClose: !deleteMutation.isPending });
|
||||
|
|
|
|||
|
|
@ -2,11 +2,13 @@ import { scrollIntoViewHandler } from "@/mainview/scripts/utils";
|
|||
import { RPC_URL } from "@/shared/constants";
|
||||
import { FocusContext, useFocusable } from "@noriginmedia/norigin-spatial-navigation";
|
||||
import classNames from "classnames";
|
||||
import { Clock, CloudBackup, CloudDownload, CloudUpload, HardDrive, Store, TriangleAlert } from "lucide-react";
|
||||
import { Clock, CloudBackup, CloudDownload, CloudUpload, Gamepad2, HardDrive, Store, TriangleAlert } from "lucide-react";
|
||||
import prettyBytes from "pretty-bytes";
|
||||
import { JSX } from "react";
|
||||
import ActionButtons from "./ActionButtons";
|
||||
import prettyMilliseconds from 'pretty-ms';
|
||||
import { useQuery } from "@tanstack/react-query";
|
||||
import { validateSourceQuery } from "@/mainview/scripts/queries/romm";
|
||||
|
||||
export function DetailElement (data: { icon: JSX.Element; tooltip?: string | null, children?: any | any[]; })
|
||||
{
|
||||
|
|
@ -18,6 +20,12 @@ export function DetailElement (data: { icon: JSX.Element; tooltip?: string | nul
|
|||
);
|
||||
}
|
||||
|
||||
const sourceIconMap: Record<string, any> = {
|
||||
store: <Store />,
|
||||
local: <HardDrive />,
|
||||
romm: <Gamepad2 />
|
||||
};
|
||||
|
||||
export default function Details (data: {
|
||||
game?: FrontEndGameTypeDetailed,
|
||||
source: string,
|
||||
|
|
@ -32,6 +40,8 @@ export default function Details (data: {
|
|||
forceFocus: true
|
||||
});
|
||||
|
||||
const { data: validation } = useQuery(validateSourceQuery(data.source, data.id));
|
||||
|
||||
const platformCoverImg = data.game?.path_platform_cover ? new URL(`${RPC_URL(__HOST__)}${data.game?.path_platform_cover}`) : undefined;
|
||||
if (platformCoverImg)
|
||||
platformCoverImg.searchParams.set("width", "64");
|
||||
|
|
@ -70,8 +80,8 @@ export default function Details (data: {
|
|||
</div>}
|
||||
<DetailElement icon={platformCoverImg ? <img className="size-6" src={platformCoverImg.href}></img> : <div className="skeleton size-6 rounded-full shrink-0"></div>} >{data.game?.platform_display_name ?? <div className="skeleton h-4 w-32"></div>}</DetailElement>
|
||||
{data.game?.emulators?.some(e => e.integrations.some(i => i.capabilities?.includes('saves'))) && <DetailElement tooltip={"Save Backup"} icon={<CloudUpload />} />}
|
||||
<DetailElement icon={
|
||||
<Store />
|
||||
<DetailElement tooltip={validation?.reason} icon={
|
||||
validation ? validation.valid ? sourceIconMap[data.game?.source ?? ''] : <TriangleAlert className="text-error" /> : <span className="loading loading-spinner loading-lg"></span>
|
||||
} >
|
||||
{data.game?.source ?? data.game?.id.source}
|
||||
{data.game?.local && <small className="text-base-content/60 font-semibold">local</small>}</DetailElement>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue