Game updates (#187)

* refactor: split umu launcher

* feat: latest version picker + fixes

* feat: frontend latest changes

* feat: game update detection w/ setting

* feat: fixes and refactor for game update

* fix: windows ui

* fix: deps

* feat: update modifications

* feat: missing ui and lock update

* fix: create install dir on init

* fix: clippy

* fix: clippy x2

* feat: add configuration option to toggle updates

* feat: uninstall dropdown on partiallyinstalled
This commit is contained in:
DecDuck
2026-02-25 23:27:30 +11:00
committed by GitHub
parent fe9a88b9b2
commit 4728ea177c
38 changed files with 1193 additions and 573 deletions
+20 -30
View File
@@ -1,50 +1,45 @@
import { invoke } from "@tauri-apps/api/core";
import { listen } from "@tauri-apps/api/event";
import type { Game, GameStatus, GameStatusEnum, GameVersion } from "~/types";
import type {
Game,
GameStatus,
GameStatusEnum,
GameVersion,
RawGameStatus,
} from "~/types";
const gameRegistry: { [key: string]: { game: Game; version?: GameVersion } } =
const gameRegistry: { [key: string]: { game: Game; version: Ref<GameVersion | undefined> } } =
{};
const gameStatusRegistry: { [key: string]: Ref<GameStatus> } = {};
type OptionGameStatus = { [key in GameStatusEnum]: { version_name?: string } };
export type SerializedGameStatus = [
{ type: GameStatusEnum },
OptionGameStatus | null,
];
export const parseStatus = (status: SerializedGameStatus): GameStatus => {
export const parseStatus = (status: RawGameStatus): GameStatus => {
console.log(status[0]);
if (status[0]) {
return {
type: status[0].type,
};
} else if (status[1]) {
const [[gameStatus, options]] = Object.entries(status[1]);
return {
type: gameStatus as GameStatusEnum,
...options,
};
} else {
throw new Error("No game status");
return status[0];
}
if (status[1]) {
return status[1];
}
throw new Error("No game status: " + JSON.stringify(status));
};
export const useGame = async (gameId: string) => {
if (!gameRegistry[gameId]) {
const data: {
game: Game;
status: SerializedGameStatus;
status: RawGameStatus;
version?: GameVersion;
} = await invoke("fetch_game", {
gameId,
});
gameRegistry[gameId] = { game: data.game, version: data.version };
gameRegistry[gameId] = { game: data.game, version: ref(data.version) };
if (!gameStatusRegistry[gameId]) {
gameStatusRegistry[gameId] = ref(parseStatus(data.status));
listen(`update_game/${gameId}`, (event) => {
const payload: {
status: SerializedGameStatus;
status: RawGameStatus;
version?: GameVersion;
} = event.payload as any;
gameStatusRegistry[gameId].value = parseStatus(payload.status);
@@ -57,7 +52,7 @@ export const useGame = async (gameId: string) => {
* on transient state updates.
*/
if (payload.version) {
gameRegistry[gameId].version = payload.version;
gameRegistry[gameId].version.value = payload.version;
}
});
}
@@ -68,11 +63,6 @@ export const useGame = async (gameId: string) => {
return { ...game, status };
};
export type FrontendGameConfiguration = {
launchString: string;
overrideProtonPath?: string;
};
export type LaunchResult =
| { result: "Success" }
| { result: "InstallRequired"; data: [string, string] };
@@ -102,4 +92,4 @@ export type VersionOption = {
export type ProtonPath = {
path: string;
name: string;
};
};
+32
View File
@@ -0,0 +1,32 @@
import { invoke } from "@tauri-apps/api/core";
interface ProtonPaths {
data: Ref<{
autodiscovered: ProtonPath[];
custom: ProtonPath[];
default?: string;
}>;
refresh: () => Promise<void>;
}
const protonPaths = useState<ProtonPaths["data"]["value"]>(
"proton_paths",
undefined,
);
export const useProtonPaths = async (): Promise<ProtonPaths> => {
const refresh = async () => {
protonPaths.value = await invoke("fetch_proton_paths");
};
if (protonPaths.value)
return {
data: protonPaths,
refresh,
};
await refresh();
return {
data: protonPaths,
refresh,
};
};