diff --git a/package.json b/package.json index b16e792f..2850454d 100644 --- a/package.json +++ b/package.json @@ -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", diff --git a/src/locales/en/translation.json b/src/locales/en/translation.json index 4b5f7768..b4427eae 100644 --- a/src/locales/en/translation.json +++ b/src/locales/en/translation.json @@ -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", diff --git a/src/locales/pt-BR/translation.json b/src/locales/pt-BR/translation.json index 2bd54dac..f6e153c9 100644 --- a/src/locales/pt-BR/translation.json +++ b/src/locales/pt-BR/translation.json @@ -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", diff --git a/src/renderer/src/context/cloud-sync/cloud-sync.context.tsx b/src/renderer/src/context/cloud-sync/cloud-sync.context.tsx index 51b7d332..f9287a11 100644 --- a/src/renderer/src/context/cloud-sync/cloud-sync.context.tsx +++ b/src/renderer/src/context/cloud-sync/cloud-sync.context.tsx @@ -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); } diff --git a/src/renderer/src/pages/game-details/cloud-sync-modal/cloud-sync-modal.tsx b/src/renderer/src/pages/game-details/cloud-sync-modal/cloud-sync-modal.tsx index 743bf5b2..e30b244c 100644 --- a/src/renderer/src/pages/game-details/cloud-sync-modal/cloud-sync-modal.tsx +++ b/src/renderer/src/pages/game-details/cloud-sync-modal/cloud-sync-modal.tsx @@ -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 {} @@ -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) { ); } - if (restoringBackup) { return ( @@ -136,7 +134,6 @@ export function CloudSyncModal({ visible, onClose }: CloudSyncModalProps) { ); } - if (loadingPreview) { return ( @@ -145,19 +142,15 @@ export function CloudSyncModal({ visible, onClose }: CloudSyncModalProps) { ); } - 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) {

{gameTitle}

{backupStateLabel}

- - {formatBytes(artifact.artifactLengthInBytes)} -
- - - - {artifact.hostname} - - - - - {artifact.downloadOptionTitle ?? - t("no_download_option_info")} - - - - - {formatDateTime(artifact.createdAt)} - - - -
- - - -
- - ))} +
+
+ + + {formatBytes(artifact.artifactLengthInBytes)} + +
+ + + + {artifact.hostname} + + + + + {artifact.downloadOptionTitle ?? + t("no_download_option_info")} + + + + + {formatDateTime(artifact.createdAt)} + +
+ +
+ + + +
+ + ) + )} + ) : (

{t("no_backups_created")}

diff --git a/yarn.lock b/yarn.lock index a745b484..d34598e9 100644 --- a/yarn.lock +++ b/yarn.lock @@ -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==