feat: adding motion

This commit is contained in:
Chubby Granny Chaser
2025-05-30 14:07:59 +01:00
parent 97a414e77f
commit 4d950b30fb
6 changed files with 132 additions and 97 deletions

View File

@@ -57,6 +57,7 @@
"electron-log": "^5.2.4",
"electron-updater": "^6.6.2",
"file-type": "^20.5.0",
"framer-motion": "^12.15.0",
"i18next": "^23.11.2",
"i18next-browser-languagedetector": "^7.2.1",
"jsdom": "^24.0.0",

View File

@@ -217,7 +217,9 @@
"freeze_backup": "Pin it so it's not overwritten by automatic backups",
"unfreeze_backup": "Unpin it",
"backup_frozen": "Backup pinned",
"backup_unfrozen": "Backup unpinned"
"backup_unfrozen": "Backup unpinned",
"backup_freeze_failed": "Failed to freeze backup",
"backup_freeze_failed_description": "You must leave at least one free slot for automatic backups"
},
"activation": {
"title": "Activate Hydra",

View File

@@ -204,7 +204,9 @@
"freeze_backup": "Fixar para não ser apagado por backups automáticos",
"unfreeze_backup": "Remover dos fixados",
"backup_frozen": "Backup fixado",
"backup_unfrozen": "Backup removido dos fixados"
"backup_unfrozen": "Backup removido dos fixados",
"backup_freeze_failed": "Falha ao fixar backup",
"backup_freeze_failed_description": "Você deve deixar pelo menos um espaço livre para backups automáticos"
},
"activation": {
"title": "Ativação",

View File

@@ -135,6 +135,7 @@ export function CloudSyncContextProvider({
getGameArtifacts();
} catch (err) {
logger.error("Failed to toggle artifact freeze", objectId, shop, err);
throw err;
} finally {
setFreezingArtifact(false);
}

View File

@@ -1,7 +1,6 @@
import { Button, Modal, ModalProps } from "@renderer/components";
import { useContext, useEffect, useMemo, useState } from "react";
import { cloudSyncContext, gameDetailsContext } from "@renderer/context";
import "./cloud-sync-modal.scss";
import { formatBytes } from "@shared";
import {
@@ -22,6 +21,8 @@ import { AxiosProgressEvent } from "axios";
import { formatDownloadProgress } from "@renderer/helpers";
import { CloudSyncRenameArtifactModal } from "../cloud-sync-rename-artifact-modal/cloud-sync-rename-artifact-modal";
import { GameArtifact } from "@types";
import { motion, AnimatePresence } from "framer-motion";
import { orderBy } from "lodash-es";
export interface CloudSyncModalProps
extends Omit<ModalProps, "children" | "title"> {}
@@ -35,7 +36,6 @@ export function CloudSyncModal({ visible, onClose }: CloudSyncModalProps) {
);
const { t } = useTranslation("game_details");
const { formatDate, formatDateTime } = useDate();
const {
@@ -60,10 +60,8 @@ export function CloudSyncModal({ visible, onClose }: CloudSyncModalProps) {
const handleDeleteArtifactClick = async (gameArtifactId: string) => {
setDeletingArtifact(true);
try {
await deleteGameArtifact(gameArtifactId);
showSuccessToast(t("backup_deleted"));
} catch (err) {
showErrorToast("backup_deletion_failed");
@@ -81,7 +79,6 @@ export function CloudSyncModal({ visible, onClose }: CloudSyncModalProps) {
setBackupDownloadProgress(progressEvent);
}
);
return () => {
removeBackupDownloadProgressListener();
};
@@ -96,12 +93,14 @@ export function CloudSyncModal({ visible, onClose }: CloudSyncModalProps) {
artifactId: string,
isFrozen: boolean
) => {
await toggleArtifactFreeze(artifactId, isFrozen);
if (isFrozen) {
showSuccessToast(t("backup_frozen"));
} else {
showSuccessToast(t("backup_unfrozen"));
try {
await toggleArtifactFreeze(artifactId, isFrozen);
showSuccessToast(isFrozen ? t("backup_frozen") : t("backup_unfrozen"));
} catch (err) {
showErrorToast(
t("backup_freeze_failed"),
t("backup_freeze_failed_description")
);
}
};
@@ -123,7 +122,6 @@ export function CloudSyncModal({ visible, onClose }: CloudSyncModalProps) {
</span>
);
}
if (restoringBackup) {
return (
<span className="cloud-sync-modal__backup-state-label">
@@ -136,7 +134,6 @@ export function CloudSyncModal({ visible, onClose }: CloudSyncModalProps) {
</span>
);
}
if (loadingPreview) {
return (
<span className="cloud-sync-modal__backup-state-label">
@@ -145,19 +142,15 @@ export function CloudSyncModal({ visible, onClose }: CloudSyncModalProps) {
</span>
);
}
if (artifacts.length >= backupsPerGameLimit) {
return t("max_number_of_artifacts_reached");
}
if (!backupPreview) {
return t("no_backup_preview");
}
if (artifacts.length === 0) {
return t("no_backups");
}
return "";
}, [
uploadingBackup,
@@ -194,7 +187,6 @@ export function CloudSyncModal({ visible, onClose }: CloudSyncModalProps) {
<div className="cloud-sync-modal__title-container">
<h2>{gameTitle}</h2>
<p>{backupStateLabel}</p>
<button
type="button"
className="cloud-sync-modal__manage-files-button"
@@ -235,83 +227,99 @@ export function CloudSyncModal({ visible, onClose }: CloudSyncModalProps) {
{artifacts.length > 0 ? (
<ul className="cloud-sync-modal__artifacts">
{artifacts.map((artifact) => (
<li key={artifact.id} className="cloud-sync-modal__artifact">
<div className="cloud-sync-modal__artifact-info">
<div className="cloud-sync-modal__artifact-header">
<button
type="button"
className="cloud-sync-modal__artifact-label"
onClick={() => setArtifactToRename(artifact)}
>
{artifact.label ??
t("backup_from", {
date: formatDate(artifact.createdAt),
})}
<PencilIcon />
</button>
<small>{formatBytes(artifact.artifactLengthInBytes)}</small>
</div>
<span className="cloud-sync-modal__artifact-meta">
<DeviceDesktopIcon size={14} />
{artifact.hostname}
</span>
<span className="cloud-sync-modal__artifact-meta">
<InfoIcon size={14} />
{artifact.downloadOptionTitle ??
t("no_download_option_info")}
</span>
<span className="cloud-sync-modal__artifact-meta">
<ClockIcon size={14} />
{formatDateTime(artifact.createdAt)}
</span>
</div>
<div className="cloud-sync-modal__artifact-actions">
<Button
type="button"
tooltip={
artifact.isFrozen
? t("unfreeze_backup")
: t("freeze_backup")
}
theme={artifact.isFrozen ? "primary" : "outline"}
onClick={() =>
handleFreezeArtifactClick(artifact.id, !artifact.isFrozen)
}
disabled={disableActions}
<AnimatePresence>
{orderBy(artifacts, [(a) => !a.isFrozen], ["asc"]).map(
(artifact) => (
<motion.li
key={artifact.id}
className="cloud-sync-modal__artifact"
layout
initial={{ opacity: 0, y: 10 }}
animate={{ opacity: 1, y: 0 }}
exit={{ opacity: 0, y: -10 }}
transition={{ duration: 0.2 }}
>
{artifact.isFrozen ? <PinSlashIcon /> : <PinIcon />}
</Button>
<Button
type="button"
onClick={() => handleBackupInstallClick(artifact.id)}
disabled={disableActions}
theme="outline"
>
{restoringBackup ? (
<SyncIcon className="cloud-sync-modal__sync-icon" />
) : (
<HistoryIcon />
)}
{t("install_backup")}
</Button>
<Button
type="button"
onClick={() => handleDeleteArtifactClick(artifact.id)}
disabled={disableActions || artifact.isFrozen}
theme="outline"
tooltip={t("delete_backup")}
>
<TrashIcon />
</Button>
</div>
</li>
))}
<div className="cloud-sync-modal__artifact-info">
<div className="cloud-sync-modal__artifact-header">
<button
type="button"
className="cloud-sync-modal__artifact-label"
onClick={() => setArtifactToRename(artifact)}
>
{artifact.label ??
t("backup_from", {
date: formatDate(artifact.createdAt),
})}
<PencilIcon />
</button>
<small>
{formatBytes(artifact.artifactLengthInBytes)}
</small>
</div>
<span className="cloud-sync-modal__artifact-meta">
<DeviceDesktopIcon size={14} />
{artifact.hostname}
</span>
<span className="cloud-sync-modal__artifact-meta">
<InfoIcon size={14} />
{artifact.downloadOptionTitle ??
t("no_download_option_info")}
</span>
<span className="cloud-sync-modal__artifact-meta">
<ClockIcon size={14} />
{formatDateTime(artifact.createdAt)}
</span>
</div>
<div className="cloud-sync-modal__artifact-actions">
<Button
type="button"
tooltip={
artifact.isFrozen
? t("unfreeze_backup")
: t("freeze_backup")
}
theme={artifact.isFrozen ? "primary" : "outline"}
onClick={() =>
handleFreezeArtifactClick(
artifact.id,
!artifact.isFrozen
)
}
disabled={disableActions}
>
{artifact.isFrozen ? <PinSlashIcon /> : <PinIcon />}
</Button>
<Button
type="button"
onClick={() => handleBackupInstallClick(artifact.id)}
disabled={disableActions}
theme="outline"
>
{restoringBackup ? (
<SyncIcon className="cloud-sync-modal__sync-icon" />
) : (
<HistoryIcon />
)}
{t("install_backup")}
</Button>
<Button
type="button"
onClick={() => handleDeleteArtifactClick(artifact.id)}
disabled={disableActions || artifact.isFrozen}
theme="outline"
tooltip={t("delete_backup")}
>
<TrashIcon />
</Button>
</div>
</motion.li>
)
)}
</AnimatePresence>
</ul>
) : (
<p>{t("no_backups_created")}</p>

View File

@@ -5646,6 +5646,15 @@ formdata-polyfill@^4.0.10:
dependencies:
fetch-blob "^3.1.2"
framer-motion@^12.15.0:
version "12.15.0"
resolved "https://registry.yarnpkg.com/framer-motion/-/framer-motion-12.15.0.tgz#6892283fc7967b071f537d6d160ab49e3d5e73ae"
integrity sha512-XKg/LnKExdLGugZrDILV7jZjI599785lDIJZLxMiiIFidCsy0a4R2ZEf+Izm67zyOuJgQYTHOmodi7igQsw3vg==
dependencies:
motion-dom "^12.15.0"
motion-utils "^12.12.1"
tslib "^2.4.0"
fs-extra@^10.0.0, fs-extra@^10.1.0:
version "10.1.0"
resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-10.1.0.tgz#02873cfbc4084dde127eaa5f9905eef2325d1abf"
@@ -7304,6 +7313,18 @@ module-error@^1.0.1:
resolved "https://registry.yarnpkg.com/module-error/-/module-error-1.0.2.tgz#8d1a48897ca883f47a45816d4fb3e3c6ba404d86"
integrity sha512-0yuvsqSCv8LbaOKhnsQ/T5JhyFlCYLPXK3U2sgV10zoKQwzs/MyfuQUOZQ1V/6OCOJsK/TRgNVrPuPDqtdMFtA==
motion-dom@^12.15.0:
version "12.15.0"
resolved "https://registry.yarnpkg.com/motion-dom/-/motion-dom-12.15.0.tgz#eca7c9d8c28976b8c920f175f92d5288f5a17785"
integrity sha512-D2ldJgor+2vdcrDtKJw48k3OddXiZN1dDLLWrS8kiHzQdYVruh0IoTwbJBslrnTXIPgFED7PBN2Zbwl7rNqnhA==
dependencies:
motion-utils "^12.12.1"
motion-utils@^12.12.1:
version "12.12.1"
resolved "https://registry.yarnpkg.com/motion-utils/-/motion-utils-12.12.1.tgz#63e28751325cb9d1cd684f3c273a570022b0010e"
integrity sha512-f9qiqUHm7hWSLlNW8gS9pisnsN7CRFRD58vNjptKdsqFLpkVnX00TNeD6Q0d27V9KzT7ySFyK1TZ/DShfVOv6w==
ms@^2.0.0, ms@^2.1.1, ms@^2.1.3:
version "2.1.3"
resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2"
@@ -9041,7 +9062,7 @@ ts-node@^10.9.2:
v8-compile-cache-lib "^3.0.1"
yn "3.1.1"
tslib@^2.0.0, tslib@^2.1.0:
tslib@^2.0.0, tslib@^2.1.0, tslib@^2.4.0:
version "2.8.1"
resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.8.1.tgz#612efe4ed235d567e8aba5f2a5fab70280ade83f"
integrity sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==