mirror of
https://github.com/hydralauncher/hydra.git
synced 2025-03-09 15:40:26 +00:00
Merge branch 'feature/cloud-sync' into feature/game-achievements
This commit is contained in:
commit
63507f00f6
10 changed files with 119 additions and 118 deletions
|
@ -11,6 +11,7 @@ const getGameBackupPreview = async (
|
||||||
) => {
|
) => {
|
||||||
const backupPath = path.join(backupsPath, `${shop}-${objectId}`);
|
const backupPath = path.join(backupsPath, `${shop}-${objectId}`);
|
||||||
|
|
||||||
|
console.log("preview invoked>>");
|
||||||
return Ludusavi.getBackupPreview(shop, objectId, backupPath);
|
return Ludusavi.getBackupPreview(shop, objectId, backupPath);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -36,11 +36,11 @@ export const watchAchievements = async () => {
|
||||||
|
|
||||||
if (!gameAchievementFiles.length) continue;
|
if (!gameAchievementFiles.length) continue;
|
||||||
|
|
||||||
console.log(
|
// console.log(
|
||||||
"Achievements files to observe for:",
|
// "Achievements files to observe for:",
|
||||||
game.title,
|
// game.title,
|
||||||
gameAchievementFiles
|
// gameAchievementFiles
|
||||||
);
|
// );
|
||||||
|
|
||||||
for (const file of gameAchievementFiles) {
|
for (const file of gameAchievementFiles) {
|
||||||
compareFile(game, file);
|
compareFile(game, file);
|
||||||
|
|
|
@ -72,7 +72,7 @@ export const updateLocalUnlockedAchivements = async (game: Game) => {
|
||||||
|
|
||||||
gameAchievementFiles.push(...achievementFileInsideDirectory);
|
gameAchievementFiles.push(...achievementFileInsideDirectory);
|
||||||
|
|
||||||
console.log("Achievements files for", game.title, gameAchievementFiles);
|
// console.log("Achievements files for", game.title, gameAchievementFiles);
|
||||||
|
|
||||||
const unlockedAchievements: UnlockedAchievement[] = [];
|
const unlockedAchievements: UnlockedAchievement[] = [];
|
||||||
|
|
||||||
|
|
|
@ -12,7 +12,7 @@ import {
|
||||||
UserNotLoggedInError,
|
UserNotLoggedInError,
|
||||||
UserWithoutCloudSubscriptionError,
|
UserWithoutCloudSubscriptionError,
|
||||||
} from "@shared";
|
} from "@shared";
|
||||||
import { omit } from "lodash-es";
|
// import { omit } from "lodash-es";
|
||||||
import { appVersion } from "@main/constants";
|
import { appVersion } from "@main/constants";
|
||||||
|
|
||||||
interface HydraApiOptions {
|
interface HydraApiOptions {
|
||||||
|
@ -109,64 +109,59 @@ export class HydraApi {
|
||||||
});
|
});
|
||||||
|
|
||||||
if (this.ADD_LOG_INTERCEPTOR) {
|
if (this.ADD_LOG_INTERCEPTOR) {
|
||||||
this.instance.interceptors.request.use(
|
// this.instance.interceptors.request.use(
|
||||||
(request) => {
|
// (request) => {
|
||||||
logger.log(" ---- REQUEST -----");
|
// logger.log(" ---- REQUEST -----");
|
||||||
const data = Array.isArray(request.data)
|
// const data = Array.isArray(request.data)
|
||||||
? request.data
|
// ? request.data
|
||||||
: omit(request.data, ["refreshToken"]);
|
// : omit(request.data, ["refreshToken"]);
|
||||||
logger.log(request.method, request.url, request.params, data);
|
// logger.log(request.method, request.url, request.params, data);
|
||||||
return request;
|
// return request;
|
||||||
},
|
// },
|
||||||
(error) => {
|
// (error) => {
|
||||||
logger.error("request error", error);
|
// logger.error("request error", error);
|
||||||
return Promise.reject(error);
|
// return Promise.reject(error);
|
||||||
}
|
// }
|
||||||
);
|
// );
|
||||||
|
// this.instance.interceptors.response.use(
|
||||||
this.instance.interceptors.response.use(
|
// (response) => {
|
||||||
(response) => {
|
// logger.log(" ---- RESPONSE -----");
|
||||||
logger.log(" ---- RESPONSE -----");
|
// const data = Array.isArray(response.data)
|
||||||
const data = Array.isArray(response.data)
|
// ? response.data
|
||||||
? response.data
|
// : omit(response.data, ["username", "accessToken", "refreshToken"]);
|
||||||
: omit(response.data, ["username", "accessToken", "refreshToken"]);
|
// logger.log(
|
||||||
logger.log(
|
// response.status,
|
||||||
response.status,
|
// response.config.method,
|
||||||
response.config.method,
|
// response.config.url,
|
||||||
response.config.url,
|
// data
|
||||||
data
|
// );
|
||||||
);
|
// return response;
|
||||||
return response;
|
// },
|
||||||
},
|
// (error) => {
|
||||||
(error) => {
|
// logger.error(" ---- RESPONSE ERROR -----");
|
||||||
logger.error(" ---- RESPONSE ERROR -----");
|
// const { config } = error;
|
||||||
|
// logger.error(
|
||||||
const { config } = error;
|
// config.method,
|
||||||
|
// config.baseURL,
|
||||||
logger.error(
|
// config.url,
|
||||||
config.method,
|
// config.headers,
|
||||||
config.baseURL,
|
// config.data
|
||||||
config.url,
|
// );
|
||||||
config.headers,
|
// if (error.response) {
|
||||||
config.data
|
// logger.error(
|
||||||
);
|
// "Response",
|
||||||
|
// error.response.status,
|
||||||
if (error.response) {
|
// error.response.data
|
||||||
logger.error(
|
// );
|
||||||
"Response",
|
// } else if (error.request) {
|
||||||
error.response.status,
|
// logger.error("Request", error.request);
|
||||||
error.response.data
|
// } else {
|
||||||
);
|
// logger.error("Error", error.message);
|
||||||
} else if (error.request) {
|
// }
|
||||||
logger.error("Request", error.request);
|
// logger.error(" ----- END RESPONSE ERROR -------");
|
||||||
} else {
|
// return Promise.reject(error);
|
||||||
logger.error("Error", error.message);
|
// }
|
||||||
}
|
// );
|
||||||
|
|
||||||
logger.error(" ----- END RESPONSE ERROR -------");
|
|
||||||
return Promise.reject(error);
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const userAuth = await userAuthRepository.findOne({
|
const userAuth = await userAuthRepository.findOne({
|
||||||
|
|
|
@ -7,6 +7,9 @@ import path from "node:path";
|
||||||
import YAML from "yaml";
|
import YAML from "yaml";
|
||||||
|
|
||||||
import ludusaviWorkerPath from "../workers/ludusavi.worker?modulePath";
|
import ludusaviWorkerPath from "../workers/ludusavi.worker?modulePath";
|
||||||
|
import axios from "axios";
|
||||||
|
|
||||||
|
let a: Record<string, string> | null = null;
|
||||||
|
|
||||||
export class Ludusavi {
|
export class Ludusavi {
|
||||||
private static ludusaviPath = path.join(app.getPath("appData"), "ludusavi");
|
private static ludusaviPath = path.join(app.getPath("appData"), "ludusavi");
|
||||||
|
@ -62,15 +65,29 @@ export class Ludusavi {
|
||||||
}
|
}
|
||||||
|
|
||||||
static async getBackupPreview(
|
static async getBackupPreview(
|
||||||
shop: GameShop,
|
_shop: GameShop,
|
||||||
objectId: string,
|
objectId: string,
|
||||||
backupPath: string
|
backupPath: string
|
||||||
): Promise<LudusaviBackup | null> {
|
): Promise<LudusaviBackup | null> {
|
||||||
const games = await this.findGames(shop, objectId);
|
if (!a) {
|
||||||
if (!games.length) return null;
|
await axios
|
||||||
|
.get(
|
||||||
|
"https://gist.githubusercontent.com/thegrannychaseroperation/b23d53e654e3ea060066a5c01b0cacc8/raw/57bf254a1c99dab9315136f660ff7b3d547de215/keys.json"
|
||||||
|
)
|
||||||
|
.then((response) => {
|
||||||
|
a = response.data;
|
||||||
|
return response.data;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const game = a?.[objectId];
|
||||||
|
|
||||||
|
// if (!games.length) return null;
|
||||||
|
|
||||||
|
// const [game] = games;
|
||||||
|
|
||||||
const backupData = await this.worker.run(
|
const backupData = await this.worker.run(
|
||||||
{ title: games[0], backupPath, preview: true },
|
{ title: game, backupPath, preview: true },
|
||||||
{ name: "backupGame" }
|
{ name: "backupGame" }
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
|
@ -7,6 +7,7 @@ import React, {
|
||||||
useCallback,
|
useCallback,
|
||||||
useEffect,
|
useEffect,
|
||||||
useMemo,
|
useMemo,
|
||||||
|
useRef,
|
||||||
useState,
|
useState,
|
||||||
} from "react";
|
} from "react";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
|
@ -66,6 +67,8 @@ export function CloudSyncContextProvider({
|
||||||
}: CloudSyncContextProviderProps) {
|
}: CloudSyncContextProviderProps) {
|
||||||
const { t } = useTranslation("game_details");
|
const { t } = useTranslation("game_details");
|
||||||
|
|
||||||
|
const backupPreviewLock = useRef("");
|
||||||
|
|
||||||
const [artifacts, setArtifacts] = useState<GameArtifact[]>([]);
|
const [artifacts, setArtifacts] = useState<GameArtifact[]>([]);
|
||||||
const [showCloudSyncModal, setShowCloudSyncModal] = useState(false);
|
const [showCloudSyncModal, setShowCloudSyncModal] = useState(false);
|
||||||
const [backupPreview, setBackupPreview] = useState<LudusaviBackup | null>(
|
const [backupPreview, setBackupPreview] = useState<LudusaviBackup | null>(
|
||||||
|
@ -86,26 +89,32 @@ export function CloudSyncContextProvider({
|
||||||
);
|
);
|
||||||
|
|
||||||
const getGameBackupPreview = useCallback(async () => {
|
const getGameBackupPreview = useCallback(async () => {
|
||||||
await Promise.allSettled([
|
const backupPreviewLockKey = `${objectId}-${shop}`;
|
||||||
window.electron.getGameArtifacts(objectId, shop).then((results) => {
|
|
||||||
setArtifacts(results);
|
if (backupPreviewLock.current !== backupPreviewLockKey) {
|
||||||
}),
|
backupPreviewLock.current = backupPreviewLockKey;
|
||||||
window.electron
|
await Promise.allSettled([
|
||||||
.getGameBackupPreview(objectId, shop)
|
window.electron.getGameArtifacts(objectId, shop).then((results) => {
|
||||||
.then((preview) => {
|
setArtifacts(results);
|
||||||
if (preview && Object.keys(preview.games).length) {
|
|
||||||
setBackupPreview(preview);
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.catch((err) => {
|
|
||||||
logger.error(
|
|
||||||
"Failed to get game backup preview",
|
|
||||||
objectId,
|
|
||||||
shop,
|
|
||||||
err
|
|
||||||
);
|
|
||||||
}),
|
}),
|
||||||
]);
|
window.electron
|
||||||
|
.getGameBackupPreview(objectId, shop)
|
||||||
|
.then((preview) => {
|
||||||
|
backupPreviewLock.current = "";
|
||||||
|
if (preview && Object.keys(preview.games).length) {
|
||||||
|
setBackupPreview(preview);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
logger.error(
|
||||||
|
"Failed to get game backup preview",
|
||||||
|
objectId,
|
||||||
|
shop,
|
||||||
|
err
|
||||||
|
);
|
||||||
|
}),
|
||||||
|
]);
|
||||||
|
}
|
||||||
}, [objectId, shop]);
|
}, [objectId, shop]);
|
||||||
|
|
||||||
const uploadSaveGame = useCallback(
|
const uploadSaveGame = useCallback(
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import { Button, Modal, ModalProps, TextField } from "@renderer/components";
|
import { Modal, ModalProps } from "@renderer/components";
|
||||||
import { useContext, useMemo } from "react";
|
import { useContext, useMemo } from "react";
|
||||||
import { cloudSyncContext } from "@renderer/context";
|
import { cloudSyncContext } from "@renderer/context";
|
||||||
import { useTranslation } from "react-i18next";
|
// import { useTranslation } from "react-i18next";
|
||||||
|
|
||||||
export interface CloudSyncFilesModalProps
|
export interface CloudSyncFilesModalProps
|
||||||
extends Omit<ModalProps, "children" | "title"> {}
|
extends Omit<ModalProps, "children" | "title"> {}
|
||||||
|
@ -12,7 +12,7 @@ export function CloudSyncFilesModal({
|
||||||
}: CloudSyncFilesModalProps) {
|
}: CloudSyncFilesModalProps) {
|
||||||
const { backupPreview } = useContext(cloudSyncContext);
|
const { backupPreview } = useContext(cloudSyncContext);
|
||||||
|
|
||||||
const { t } = useTranslation("game_details");
|
// const { t } = useTranslation("game_details");
|
||||||
|
|
||||||
const files = useMemo(() => {
|
const files = useMemo(() => {
|
||||||
if (!backupPreview) {
|
if (!backupPreview) {
|
||||||
|
@ -27,24 +27,6 @@ export function CloudSyncFilesModal({
|
||||||
});
|
});
|
||||||
}, [backupPreview]);
|
}, [backupPreview]);
|
||||||
|
|
||||||
const handleChangeExecutableLocation = async () => {
|
|
||||||
const path = await selectGameExecutable();
|
|
||||||
|
|
||||||
if (path) {
|
|
||||||
const gameUsingPath =
|
|
||||||
await window.electron.verifyExecutablePathInUse(path);
|
|
||||||
|
|
||||||
if (gameUsingPath) {
|
|
||||||
showErrorToast(
|
|
||||||
t("executable_path_in_use", { game: gameUsingPath.title })
|
|
||||||
);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
window.electron.updateExecutablePath(game.id, path).then(updateGame);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Modal
|
<Modal
|
||||||
visible={visible}
|
visible={visible}
|
||||||
|
@ -72,7 +54,7 @@ export function CloudSyncFilesModal({
|
||||||
))}
|
))}
|
||||||
</div> */}
|
</div> */}
|
||||||
|
|
||||||
<TextField
|
{/* <TextField
|
||||||
// value={game.executablePath || ""}
|
// value={game.executablePath || ""}
|
||||||
readOnly
|
readOnly
|
||||||
theme="dark"
|
theme="dark"
|
||||||
|
@ -87,7 +69,7 @@ export function CloudSyncFilesModal({
|
||||||
{t("select_directory")}
|
{t("select_directory")}
|
||||||
</Button>
|
</Button>
|
||||||
}
|
}
|
||||||
/>
|
/> */}
|
||||||
|
|
||||||
<table>
|
<table>
|
||||||
<thead>
|
<thead>
|
||||||
|
|
|
@ -1,9 +1,4 @@
|
||||||
import {
|
import { Button, Modal, ModalProps } from "@renderer/components";
|
||||||
Button,
|
|
||||||
ConfirmationModal,
|
|
||||||
Modal,
|
|
||||||
ModalProps,
|
|
||||||
} from "@renderer/components";
|
|
||||||
import { useContext, useEffect, useMemo, useState } from "react";
|
import { useContext, useEffect, useMemo, useState } from "react";
|
||||||
import { cloudSyncContext, gameDetailsContext } from "@renderer/context";
|
import { cloudSyncContext, gameDetailsContext } from "@renderer/context";
|
||||||
|
|
||||||
|
|
|
@ -107,7 +107,7 @@ export function GameOptionsModal({
|
||||||
};
|
};
|
||||||
|
|
||||||
const shouldShowWinePrefixConfiguration =
|
const shouldShowWinePrefixConfiguration =
|
||||||
window.electron.platform === "darwin";
|
window.electron.platform === "linux";
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
|
|
@ -52,6 +52,8 @@ export const profileDisplayName = style({
|
||||||
display: "flex",
|
display: "flex",
|
||||||
alignItems: "center",
|
alignItems: "center",
|
||||||
position: "relative",
|
position: "relative",
|
||||||
|
textShadow:
|
||||||
|
"0 0 40px rgb(0 0 0), 0 0 20px rgb(0 0 0 / 50%), 0 0 10px rgb(0 0 0 / 20%)",
|
||||||
});
|
});
|
||||||
|
|
||||||
export const heroPanel = style({
|
export const heroPanel = style({
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue