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}`);
|
||||
|
||||
console.log("preview invoked>>");
|
||||
return Ludusavi.getBackupPreview(shop, objectId, backupPath);
|
||||
};
|
||||
|
||||
|
|
|
@ -36,11 +36,11 @@ export const watchAchievements = async () => {
|
|||
|
||||
if (!gameAchievementFiles.length) continue;
|
||||
|
||||
console.log(
|
||||
"Achievements files to observe for:",
|
||||
game.title,
|
||||
gameAchievementFiles
|
||||
);
|
||||
// console.log(
|
||||
// "Achievements files to observe for:",
|
||||
// game.title,
|
||||
// gameAchievementFiles
|
||||
// );
|
||||
|
||||
for (const file of gameAchievementFiles) {
|
||||
compareFile(game, file);
|
||||
|
|
|
@ -72,7 +72,7 @@ export const updateLocalUnlockedAchivements = async (game: Game) => {
|
|||
|
||||
gameAchievementFiles.push(...achievementFileInsideDirectory);
|
||||
|
||||
console.log("Achievements files for", game.title, gameAchievementFiles);
|
||||
// console.log("Achievements files for", game.title, gameAchievementFiles);
|
||||
|
||||
const unlockedAchievements: UnlockedAchievement[] = [];
|
||||
|
||||
|
|
|
@ -12,7 +12,7 @@ import {
|
|||
UserNotLoggedInError,
|
||||
UserWithoutCloudSubscriptionError,
|
||||
} from "@shared";
|
||||
import { omit } from "lodash-es";
|
||||
// import { omit } from "lodash-es";
|
||||
import { appVersion } from "@main/constants";
|
||||
|
||||
interface HydraApiOptions {
|
||||
|
@ -109,64 +109,59 @@ export class HydraApi {
|
|||
});
|
||||
|
||||
if (this.ADD_LOG_INTERCEPTOR) {
|
||||
this.instance.interceptors.request.use(
|
||||
(request) => {
|
||||
logger.log(" ---- REQUEST -----");
|
||||
const data = Array.isArray(request.data)
|
||||
? request.data
|
||||
: omit(request.data, ["refreshToken"]);
|
||||
logger.log(request.method, request.url, request.params, data);
|
||||
return request;
|
||||
},
|
||||
(error) => {
|
||||
logger.error("request error", error);
|
||||
return Promise.reject(error);
|
||||
}
|
||||
);
|
||||
|
||||
this.instance.interceptors.response.use(
|
||||
(response) => {
|
||||
logger.log(" ---- RESPONSE -----");
|
||||
const data = Array.isArray(response.data)
|
||||
? response.data
|
||||
: omit(response.data, ["username", "accessToken", "refreshToken"]);
|
||||
logger.log(
|
||||
response.status,
|
||||
response.config.method,
|
||||
response.config.url,
|
||||
data
|
||||
);
|
||||
return response;
|
||||
},
|
||||
(error) => {
|
||||
logger.error(" ---- RESPONSE ERROR -----");
|
||||
|
||||
const { config } = error;
|
||||
|
||||
logger.error(
|
||||
config.method,
|
||||
config.baseURL,
|
||||
config.url,
|
||||
config.headers,
|
||||
config.data
|
||||
);
|
||||
|
||||
if (error.response) {
|
||||
logger.error(
|
||||
"Response",
|
||||
error.response.status,
|
||||
error.response.data
|
||||
);
|
||||
} else if (error.request) {
|
||||
logger.error("Request", error.request);
|
||||
} else {
|
||||
logger.error("Error", error.message);
|
||||
}
|
||||
|
||||
logger.error(" ----- END RESPONSE ERROR -------");
|
||||
return Promise.reject(error);
|
||||
}
|
||||
);
|
||||
// this.instance.interceptors.request.use(
|
||||
// (request) => {
|
||||
// logger.log(" ---- REQUEST -----");
|
||||
// const data = Array.isArray(request.data)
|
||||
// ? request.data
|
||||
// : omit(request.data, ["refreshToken"]);
|
||||
// logger.log(request.method, request.url, request.params, data);
|
||||
// return request;
|
||||
// },
|
||||
// (error) => {
|
||||
// logger.error("request error", error);
|
||||
// return Promise.reject(error);
|
||||
// }
|
||||
// );
|
||||
// this.instance.interceptors.response.use(
|
||||
// (response) => {
|
||||
// logger.log(" ---- RESPONSE -----");
|
||||
// const data = Array.isArray(response.data)
|
||||
// ? response.data
|
||||
// : omit(response.data, ["username", "accessToken", "refreshToken"]);
|
||||
// logger.log(
|
||||
// response.status,
|
||||
// response.config.method,
|
||||
// response.config.url,
|
||||
// data
|
||||
// );
|
||||
// return response;
|
||||
// },
|
||||
// (error) => {
|
||||
// logger.error(" ---- RESPONSE ERROR -----");
|
||||
// const { config } = error;
|
||||
// logger.error(
|
||||
// config.method,
|
||||
// config.baseURL,
|
||||
// config.url,
|
||||
// config.headers,
|
||||
// config.data
|
||||
// );
|
||||
// if (error.response) {
|
||||
// logger.error(
|
||||
// "Response",
|
||||
// error.response.status,
|
||||
// error.response.data
|
||||
// );
|
||||
// } else if (error.request) {
|
||||
// logger.error("Request", error.request);
|
||||
// } else {
|
||||
// logger.error("Error", error.message);
|
||||
// }
|
||||
// logger.error(" ----- END RESPONSE ERROR -------");
|
||||
// return Promise.reject(error);
|
||||
// }
|
||||
// );
|
||||
}
|
||||
|
||||
const userAuth = await userAuthRepository.findOne({
|
||||
|
|
|
@ -7,6 +7,9 @@ import path from "node:path";
|
|||
import YAML from "yaml";
|
||||
|
||||
import ludusaviWorkerPath from "../workers/ludusavi.worker?modulePath";
|
||||
import axios from "axios";
|
||||
|
||||
let a: Record<string, string> | null = null;
|
||||
|
||||
export class Ludusavi {
|
||||
private static ludusaviPath = path.join(app.getPath("appData"), "ludusavi");
|
||||
|
@ -62,15 +65,29 @@ export class Ludusavi {
|
|||
}
|
||||
|
||||
static async getBackupPreview(
|
||||
shop: GameShop,
|
||||
_shop: GameShop,
|
||||
objectId: string,
|
||||
backupPath: string
|
||||
): Promise<LudusaviBackup | null> {
|
||||
const games = await this.findGames(shop, objectId);
|
||||
if (!games.length) return null;
|
||||
if (!a) {
|
||||
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(
|
||||
{ title: games[0], backupPath, preview: true },
|
||||
{ title: game, backupPath, preview: true },
|
||||
{ name: "backupGame" }
|
||||
);
|
||||
|
||||
|
|
|
@ -7,6 +7,7 @@ import React, {
|
|||
useCallback,
|
||||
useEffect,
|
||||
useMemo,
|
||||
useRef,
|
||||
useState,
|
||||
} from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
@ -66,6 +67,8 @@ export function CloudSyncContextProvider({
|
|||
}: CloudSyncContextProviderProps) {
|
||||
const { t } = useTranslation("game_details");
|
||||
|
||||
const backupPreviewLock = useRef("");
|
||||
|
||||
const [artifacts, setArtifacts] = useState<GameArtifact[]>([]);
|
||||
const [showCloudSyncModal, setShowCloudSyncModal] = useState(false);
|
||||
const [backupPreview, setBackupPreview] = useState<LudusaviBackup | null>(
|
||||
|
@ -86,26 +89,32 @@ export function CloudSyncContextProvider({
|
|||
);
|
||||
|
||||
const getGameBackupPreview = useCallback(async () => {
|
||||
await Promise.allSettled([
|
||||
window.electron.getGameArtifacts(objectId, shop).then((results) => {
|
||||
setArtifacts(results);
|
||||
}),
|
||||
window.electron
|
||||
.getGameBackupPreview(objectId, shop)
|
||||
.then((preview) => {
|
||||
if (preview && Object.keys(preview.games).length) {
|
||||
setBackupPreview(preview);
|
||||
}
|
||||
})
|
||||
.catch((err) => {
|
||||
logger.error(
|
||||
"Failed to get game backup preview",
|
||||
objectId,
|
||||
shop,
|
||||
err
|
||||
);
|
||||
const backupPreviewLockKey = `${objectId}-${shop}`;
|
||||
|
||||
if (backupPreviewLock.current !== backupPreviewLockKey) {
|
||||
backupPreviewLock.current = backupPreviewLockKey;
|
||||
await Promise.allSettled([
|
||||
window.electron.getGameArtifacts(objectId, shop).then((results) => {
|
||||
setArtifacts(results);
|
||||
}),
|
||||
]);
|
||||
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]);
|
||||
|
||||
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 { cloudSyncContext } from "@renderer/context";
|
||||
import { useTranslation } from "react-i18next";
|
||||
// import { useTranslation } from "react-i18next";
|
||||
|
||||
export interface CloudSyncFilesModalProps
|
||||
extends Omit<ModalProps, "children" | "title"> {}
|
||||
|
@ -12,7 +12,7 @@ export function CloudSyncFilesModal({
|
|||
}: CloudSyncFilesModalProps) {
|
||||
const { backupPreview } = useContext(cloudSyncContext);
|
||||
|
||||
const { t } = useTranslation("game_details");
|
||||
// const { t } = useTranslation("game_details");
|
||||
|
||||
const files = useMemo(() => {
|
||||
if (!backupPreview) {
|
||||
|
@ -27,24 +27,6 @@ export function CloudSyncFilesModal({
|
|||
});
|
||||
}, [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 (
|
||||
<Modal
|
||||
visible={visible}
|
||||
|
@ -72,7 +54,7 @@ export function CloudSyncFilesModal({
|
|||
))}
|
||||
</div> */}
|
||||
|
||||
<TextField
|
||||
{/* <TextField
|
||||
// value={game.executablePath || ""}
|
||||
readOnly
|
||||
theme="dark"
|
||||
|
@ -87,7 +69,7 @@ export function CloudSyncFilesModal({
|
|||
{t("select_directory")}
|
||||
</Button>
|
||||
}
|
||||
/>
|
||||
/> */}
|
||||
|
||||
<table>
|
||||
<thead>
|
||||
|
|
|
@ -1,9 +1,4 @@
|
|||
import {
|
||||
Button,
|
||||
ConfirmationModal,
|
||||
Modal,
|
||||
ModalProps,
|
||||
} from "@renderer/components";
|
||||
import { Button, Modal, ModalProps } from "@renderer/components";
|
||||
import { useContext, useEffect, useMemo, useState } from "react";
|
||||
import { cloudSyncContext, gameDetailsContext } from "@renderer/context";
|
||||
|
||||
|
|
|
@ -107,7 +107,7 @@ export function GameOptionsModal({
|
|||
};
|
||||
|
||||
const shouldShowWinePrefixConfiguration =
|
||||
window.electron.platform === "darwin";
|
||||
window.electron.platform === "linux";
|
||||
|
||||
return (
|
||||
<>
|
||||
|
|
|
@ -52,6 +52,8 @@ export const profileDisplayName = style({
|
|||
display: "flex",
|
||||
alignItems: "center",
|
||||
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({
|
||||
|
|
Loading…
Add table
Reference in a new issue