feat: adding freezing backups logic

This commit is contained in:
Chubby Granny Chaser
2025-05-30 04:25:35 +01:00
parent 7a196e4315
commit 7c425eeccc
7 changed files with 85 additions and 7 deletions

View File

@@ -0,0 +1,16 @@
import { registerEvent } from "../register-event";
import { HydraApi } from "@main/services";
const toggleArtifactFreeze = async (
_event: Electron.IpcMainInvokeEvent,
gameArtifactId: string,
freeze: boolean
) => {
if (freeze) {
await HydraApi.put(`/profile/games/artifacts/${gameArtifactId}/freeze`);
} else {
await HydraApi.put(`/profile/games/artifacts/${gameArtifactId}/unfreeze`);
}
};
registerEvent("toggleArtifactFreeze", toggleArtifactFreeze);

View File

@@ -86,6 +86,7 @@ import "./cloud-save/get-game-backup-preview";
import "./cloud-save/upload-save-game";
import "./cloud-save/delete-game-artifact";
import "./cloud-save/select-game-backup-path";
import "./cloud-save/toggle-artifact-freeze";
import "./notifications/publish-new-repacks-notification";
import "./themes/add-custom-theme";
import "./themes/delete-custom-theme";

View File

@@ -238,6 +238,8 @@ contextBridge.exposeInMainWorld("electron", {
downloadOptionTitle: string | null
) =>
ipcRenderer.invoke("uploadSaveGame", objectId, shop, downloadOptionTitle),
toggleArtifactFreeze: (gameArtifactId: string, freeze: boolean) =>
ipcRenderer.invoke("toggleArtifactFreeze", gameArtifactId, freeze),
downloadGameArtifact: (
objectId: string,
shop: GameShop,

View File

@@ -30,9 +30,14 @@ export interface CloudSyncContext {
setShowCloudSyncFilesModal: React.Dispatch<React.SetStateAction<boolean>>;
getGameBackupPreview: () => Promise<void>;
getGameArtifacts: () => Promise<void>;
toggleArtifactFreeze: (
gameArtifactId: string,
freeze: boolean
) => Promise<void>;
restoringBackup: boolean;
uploadingBackup: boolean;
loadingPreview: boolean;
freezingArtifact: boolean;
}
export const cloudSyncContext = createContext<CloudSyncContext>({
@@ -47,10 +52,12 @@ export const cloudSyncContext = createContext<CloudSyncContext>({
showCloudSyncFilesModal: false,
setShowCloudSyncFilesModal: () => {},
getGameBackupPreview: async () => {},
toggleArtifactFreeze: async () => {},
getGameArtifacts: async () => {},
restoringBackup: false,
uploadingBackup: false,
loadingPreview: false,
freezingArtifact: false,
});
const { Provider } = cloudSyncContext;
@@ -78,6 +85,7 @@ export function CloudSyncContextProvider({
const [uploadingBackup, setUploadingBackup] = useState(false);
const [showCloudSyncFilesModal, setShowCloudSyncFilesModal] = useState(false);
const [loadingPreview, setLoadingPreview] = useState(false);
const [freezingArtifact, setFreezingArtifact] = useState(false);
const { showSuccessToast } = useToast();
@@ -119,6 +127,21 @@ export function CloudSyncContextProvider({
[objectId, shop]
);
const toggleArtifactFreeze = useCallback(
async (gameArtifactId: string, freeze: boolean) => {
setFreezingArtifact(true);
try {
await window.electron.toggleArtifactFreeze(gameArtifactId, freeze);
getGameArtifacts();
} catch (err) {
logger.error("Failed to toggle artifact freeze", objectId, shop, err);
} finally {
setFreezingArtifact(false);
}
},
[objectId, shop, getGameArtifacts]
);
useEffect(() => {
const removeUploadCompleteListener = window.electron.onUploadComplete(
objectId,
@@ -192,6 +215,7 @@ export function CloudSyncContextProvider({
uploadingBackup,
showCloudSyncFilesModal,
loadingPreview,
freezingArtifact,
setShowCloudSyncModal,
uploadSaveGame,
downloadGameArtifact,
@@ -199,6 +223,7 @@ export function CloudSyncContextProvider({
setShowCloudSyncFilesModal,
getGameBackupPreview,
getGameArtifacts,
toggleArtifactFreeze,
}}
>
{children}

View File

@@ -202,6 +202,10 @@ declare global {
shop: GameShop,
downloadOptionTitle: string | null
) => Promise<void>;
toggleArtifactFreeze: (
gameArtifactId: string,
freeze: boolean
) => Promise<void>;
downloadGameArtifact: (
objectId: string,
shop: GameShop,

View File

@@ -7,10 +7,13 @@ import { formatBytes } from "@shared";
import {
ClockIcon,
DeviceDesktopIcon,
HistoryIcon,
DownloadIcon,
InfoIcon,
LockIcon,
PencilIcon,
SyncIcon,
TrashIcon,
UnlockIcon,
UploadIcon,
} from "@primer/octicons-react";
import { useAppSelector, useDate, useToast } from "@renderer/hooks";
@@ -36,9 +39,11 @@ export function CloudSyncModal({ visible, onClose }: CloudSyncModalProps) {
uploadingBackup,
restoringBackup,
loadingPreview,
freezingArtifact,
uploadSaveGame,
downloadGameArtifact,
deleteGameArtifact,
toggleArtifactFreeze,
setShowCloudSyncFilesModal,
getGameBackupPreview,
} = useContext(cloudSyncContext);
@@ -82,6 +87,13 @@ export function CloudSyncModal({ visible, onClose }: CloudSyncModalProps) {
downloadGameArtifact(artifactId);
};
const handleFreezeArtifactClick = async (artifactId: string) => {
await toggleArtifactFreeze(
artifactId,
!artifacts.find((artifact) => artifact.id === artifactId)?.isFrozen
);
};
useEffect(() => {
if (visible) {
getGameBackupPreview();
@@ -147,7 +159,8 @@ export function CloudSyncModal({ visible, onClose }: CloudSyncModalProps) {
t,
]);
const disableActions = uploadingBackup || restoringBackup || deletingArtifact;
const disableActions =
uploadingBackup || restoringBackup || deletingArtifact || freezingArtifact;
const isMissingWinePrefix =
window.electron.platform === "linux" && !game?.winePrefixPath;
@@ -208,12 +221,14 @@ export function CloudSyncModal({ visible, onClose }: CloudSyncModalProps) {
<li key={artifact.id} className="cloud-sync-modal__artifact">
<div className="cloud-sync-modal__artifact-info">
<div className="cloud-sync-modal__artifact-header">
<h3>
<button type="button">
{artifact.label ??
t("backup_from", {
date: formatDate(artifact.createdAt),
})}
</h3>
<PencilIcon />
</button>
<small>{formatBytes(artifact.artifactLengthInBytes)}</small>
</div>
@@ -234,23 +249,37 @@ export function CloudSyncModal({ visible, onClose }: CloudSyncModalProps) {
</div>
<div className="cloud-sync-modal__artifact-actions">
<Button
type="button"
tooltip={
artifact.isFrozen
? t("unfreeze_backup")
: t("freeze_backup")
}
theme={artifact.isFrozen ? "danger" : "outline"}
onClick={() => handleFreezeArtifactClick(artifact.id)}
disabled={disableActions}
>
{artifact.isFrozen ? <UnlockIcon /> : <LockIcon />}
</Button>
<Button
type="button"
onClick={() => handleBackupInstallClick(artifact.id)}
disabled={disableActions}
tooltip={t("install_backup")}
theme="outline"
>
{restoringBackup ? (
<SyncIcon className="cloud-sync-modal__sync-icon" />
) : (
<HistoryIcon />
<DownloadIcon />
)}
{t("install_backup")}
</Button>
<Button
type="button"
onClick={() => handleDeleteArtifactClick(artifact.id)}
theme="danger"
disabled={disableActions}
disabled={disableActions || artifact.isFrozen}
>
<TrashIcon />
{t("delete_backup")}

View File

@@ -272,6 +272,7 @@ export interface GameArtifact {
hostname: string;
downloadCount: number;
label?: string;
isFrozen: boolean;
}
export interface ComparedAchievements {