Merge branch 'feature/cloud-sync' into feature/game-achievements

This commit is contained in:
Zamitto 2024-10-20 12:04:12 -03:00
commit 63507f00f6
10 changed files with 119 additions and 118 deletions

View file

@ -11,6 +11,7 @@ const getGameBackupPreview = async (
) => {
const backupPath = path.join(backupsPath, `${shop}-${objectId}`);
console.log("preview invoked>>");
return Ludusavi.getBackupPreview(shop, objectId, backupPath);
};

View file

@ -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);

View 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[] = [];

View file

@ -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({

View file

@ -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" }
);

View file

@ -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(

View file

@ -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>

View file

@ -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";

View file

@ -107,7 +107,7 @@ export function GameOptionsModal({
};
const shouldShowWinePrefixConfiguration =
window.electron.platform === "darwin";
window.electron.platform === "linux";
return (
<>

View file

@ -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({