import { rommApi } from "@/mainview/scripts/clientApi"; import { useMutation, useQueryClient } from "@tanstack/react-query"; import { JSX, useEffect, useRef, useState } from "react"; 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 { gameInvalidationQuery, installMutation, playMutation } from "@/mainview/scripts/queries/romm"; import ActionButton from "./ActionButton"; import { useRouter } from "@tanstack/react-router"; export default function MainActions (data: { game?: FrontEndGameTypeDetailed, source: string, id: string; }) { const installMut = useMutation(installMutation(data.source, data.id)); const router = useRouter(); const playMut = useMutation({ ...playMutation, onError (error) { toast.error(error.message); }, onSuccess (data, { source, id }, onMutateResult, context) { router.navigate({ to: '/launcher/$source/$id', params: { source: source, id: id }, replace: true }); }, }); const ws = useRef<{ send: (data: string) => void; }>(undefined); const [progress, setProgress] = useState(undefined); const [status, setStatus] = useState(undefined); const [error, setError] = useState(undefined); const [details, setDetails] = useState(undefined); const [commands, setCommands] = useState(undefined); const [preferredCommand, setPreferredCommand] = useLocalStorage(`${data.game?.source ?? data.game?.id.source}-${data.game?.source_id ?? data.game?.id.id}-preferred-command`, undefined); const queryClient = useQueryClient(); const validCommands = commands ? commands.filter(c => c.valid) : []; const validDefaultCommand = commands?.find(c => { if (!c.valid) return false; if (preferredCommand && c.id !== preferredCommand) return false; return true; }); useEffect(() => { const sub = rommApi.api.romm.status({ source: data.source })({ id: data.id }).subscribe(); ws.current = sub.ws; sub.subscribe((e) => { setStatus(e.data.status); setProgress((e.data as any).progress); setDetails((e.data as any).details); setCommands((e.data as any).commands); if (e.data.status === 'refresh') { 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); if (!errorMessage) return; toast.error(errorMessage); setError(errorMessage); } }); return () => { sub.close(); ws.current = undefined; }; }, [data.source, data.id, router]); let progressIcon: JSX.Element | undefined = undefined; switch (status) { case 'download': progressIcon = ; break; case 'queued': progressIcon = ; break; case 'extract': progressIcon = ; break; } const showProgress = progress !== null && !!progressIcon; useEffect(() => { if (showProgress) return; showInstallOptions(false); }, [showProgress]); const handlePlay = (cmd?: CommandEntry) => { if (!cmd) return; if (cmd.emulator === 'EMULATORJS') { const params = new URLSearchParams(cmd.command); router.navigate({ to: '/embedded/$source/$id', params: { source: data.source, id: data.id }, search: Object.fromEntries(params.entries()), replace: true }); } else { playMut.mutate({ source: data.source, id: data.id, command_id: cmd.id }); } }; let mainButton: any | undefined = undefined; if (status === 'installed') { mainButton =
handlePlay(validDefaultCommand)} tooltip={validDefaultCommand?.label ?? details} key="primary" type='primary' id="mainAction" > {validCommands.length > 1 && showAllCommands(true, 'allActionsBtn')}> }
; } else if (error) { mainButton = { if (status === 'missing-emulator') { router.navigate({ to: '/settings/directories' }); } }} id="mainAction"> ; } else { let icon = ; if (status === 'install') { icon = ; } else if (status === 'present') { icon = ; } mainButton = { if (installMut.isPending) return; switch (status) { case 'present': case 'install': installMut.mutate(); break; } }} tooltip={details ?? status} type='primary' id="mainAction"> {icon} ; } const { dialog: allCommandDialog, setOpen: showAllCommands } = useContextDialog('all-commands-dialog', { content: { 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); handlePlay(c); }, }; return commands; })} />, preferredChildFocusKey: String(preferredCommand) }); const { dialog: installOptionsDialog, setOpen: showInstallOptions } = useContextDialog('install-options-dialog', { content: }); return
{mainButton}
{showProgress && showInstallOptions(true, "progress")} key="progress" square tooltip={details} type="base" id="progress" >
{progressIcon}
} {installOptionsDialog} {allCommandDialog}
; }