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}`); const backupPath = path.join(backupsPath, `${shop}-${objectId}`);
console.log("preview invoked>>");
return Ludusavi.getBackupPreview(shop, objectId, backupPath); return Ludusavi.getBackupPreview(shop, objectId, backupPath);
}; };

View file

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

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

View file

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

View file

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

View file

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

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

View file

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

View file

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

View file

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