From d33f708104ca067950855dd535f0a9823abad308 Mon Sep 17 00:00:00 2001 From: schroda <50052685+schroda@users.noreply.github.com> Date: Sat, 4 Oct 2025 14:11:28 +0200 Subject: [PATCH] Add backup flag selection on backup creation --- public/locales/en.json | 15 +++- src/features/backup/Backup.constants.ts | 31 ++++++++ src/features/backup/Backup.types.ts | 18 +++++ .../component/BackupFlagInclusionDialog.tsx | 71 +++++++++++++++++++ src/features/backup/screens/Backup.tsx | 47 ++++++------ 5 files changed, 158 insertions(+), 24 deletions(-) create mode 100644 src/features/backup/Backup.constants.ts create mode 100644 src/features/backup/Backup.types.ts create mode 100644 src/features/backup/component/BackupFlagInclusionDialog.tsx diff --git a/public/locales/en.json b/public/locales/en.json index ec3f7ee1..f531064d 100644 --- a/public/locales/en.json +++ b/public/locales/en.json @@ -1196,8 +1196,8 @@ "failure": "Could not create backup" }, "label": { - "in_progress": "Creating backup…", "description": "Back up library as a Tachiyomi backup", + "in_progress": "Creating backup…", "title": "Create backup" } }, @@ -1251,6 +1251,19 @@ } } }, + "flag": { + "categories": "Categories", + "chapters": "Chapters", + "client_data": "Client data", + "group": { + "library": "Library", + "settings": "Settings" + }, + "history": "History", + "manga": "Manga", + "server_settings": "Server settings", + "tracking": "Tracking" + }, "title": "Backup" }, "clear_cache": { diff --git a/src/features/backup/Backup.constants.ts b/src/features/backup/Backup.constants.ts new file mode 100644 index 00000000..95e22551 --- /dev/null +++ b/src/features/backup/Backup.constants.ts @@ -0,0 +1,31 @@ +/* + * Copyright (C) Contributors to the Suwayomi project + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +import { BackupFlag, BackupFlagGroup } from '@/features/backup/Backup.types.ts'; +import { TranslationKey } from '@/base/Base.types.ts'; + +export const BACKUP_FLAGS_TO_TRANSLATION: Record = { + includeCategories: 'settings.backup.flag.categories', + includeChapters: 'settings.backup.flag.chapters', + includeClientData: 'settings.backup.flag.client_data', + includeHistory: 'settings.backup.flag.history', + includeServerSettings: 'settings.backup.flag.server_settings', + includeTracking: 'settings.backup.flag.tracking', +}; + +export const BACKUP_FLAG_GROUP_TO_TRANSLATION: Record = { + [BackupFlagGroup.LIBRARY]: 'settings.backup.flag.group.library', + [BackupFlagGroup.SETTINGS]: 'settings.backup.flag.group.settings', +}; + +export const BACKUP_FLAGS = Object.keys(BACKUP_FLAGS_TO_TRANSLATION) as readonly BackupFlag[]; + +export const BACKUP_FLAGS_BY_GROUP: Record = { + [BackupFlagGroup.LIBRARY]: ['includeChapters', 'includeTracking', 'includeHistory', 'includeCategories'], + [BackupFlagGroup.SETTINGS]: ['includeClientData', 'includeServerSettings'], +}; diff --git a/src/features/backup/Backup.types.ts b/src/features/backup/Backup.types.ts new file mode 100644 index 00000000..6672d6f1 --- /dev/null +++ b/src/features/backup/Backup.types.ts @@ -0,0 +1,18 @@ +/* + * Copyright (C) Contributors to the Suwayomi project + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +import { CreateBackupInput } from '@/lib/graphql/generated/graphql.ts'; + +export enum BackupFlagGroup { + LIBRARY = 'library', + SETTINGS = 'settings', +} + +export type BackupFlag = keyof Omit; + +export type BackupFlagInclusionState = Record; diff --git a/src/features/backup/component/BackupFlagInclusionDialog.tsx b/src/features/backup/component/BackupFlagInclusionDialog.tsx new file mode 100644 index 00000000..992870c8 --- /dev/null +++ b/src/features/backup/component/BackupFlagInclusionDialog.tsx @@ -0,0 +1,71 @@ +/* + * Copyright (C) Contributors to the Suwayomi project + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +import Dialog from '@mui/material/Dialog'; +import DialogActions from '@mui/material/DialogActions'; +import DialogContent from '@mui/material/DialogContent'; +import DialogTitle from '@mui/material/DialogTitle'; +import FormGroup from '@mui/material/FormGroup'; +import { useTranslation } from 'react-i18next'; +import { useState } from 'react'; +import Button from '@mui/material/Button'; +import Stack from '@mui/material/Stack'; +import Typography from '@mui/material/Typography'; +import { CheckboxInput } from '@/base/components/inputs/CheckboxInput.tsx'; +import { DialogProps } from '@/base/global-dialog/GlobalDialogManager.tsx'; +import { + BACKUP_FLAG_GROUP_TO_TRANSLATION, + BACKUP_FLAGS, + BACKUP_FLAGS_BY_GROUP, + BACKUP_FLAGS_TO_TRANSLATION, +} from '@/features/backup/Backup.constants.ts'; +import { BackupFlagGroup, BackupFlagInclusionState } from '@/features/backup/Backup.types.ts'; + +export const BackupFlagInclusionDialog = ({ onCancel, onConfirm }: DialogProps) => { + const { t } = useTranslation(); + + const [includeStateByFlag, setIncludeStateByFlag] = useState( + Object.fromEntries(BACKUP_FLAGS.map((flag) => [flag, true])) as BackupFlagInclusionState, + ); + + return ( + + {t('settings.backup.action.create.label.title')} + + + {Object.entries(BACKUP_FLAGS_BY_GROUP).map(([group, flags]) => ( + + {t(BACKUP_FLAG_GROUP_TO_TRANSLATION[group as BackupFlagGroup])} + {flags.map((flag) => ( + { + setIncludeStateByFlag({ + ...includeStateByFlag, + [flag]: checked, + }); + }} + /> + ))} + + ))} + + + + + + + + ); +}; diff --git a/src/features/backup/screens/Backup.tsx b/src/features/backup/screens/Backup.tsx index 69f1a8c8..d1d9fb29 100644 --- a/src/features/backup/screens/Backup.tsx +++ b/src/features/backup/screens/Backup.tsx @@ -39,6 +39,8 @@ import { AppRoutes } from '@/base/AppRoute.constants.ts'; import { getErrorMessage } from '@/lib/HelperFunctions.ts'; import { useAppTitle } from '@/features/navigation-bar/hooks/useAppTitle.ts'; import { BrowseTab } from '@/features/browse/Browse.types.ts'; +import { GlobalDialogManager } from '@/base/global-dialog/GlobalDialogManager.tsx'; +import { BackupFlagInclusionDialog } from '@/features/backup/component/BackupFlagInclusionDialog.tsx'; type BackupSettingsType = Pick; @@ -130,33 +132,32 @@ export function Backup() { }; const createBackup = async () => { + const backupFlagInclusionState = await GlobalDialogManager.show(BackupFlagInclusionDialog); + makeToast(t('settings.backup.action.create.label.in_progress'), 'info'); - const backupFileResponse = await requestManager.createBackupFile({ - includeCategories: true, - includeChapters: true, - includeHistory: true, - includeClientData: true, - includeTracking: true, - includeServerSettings: true, - }).response; + try { + const backupFileResponse = await requestManager.createBackupFile(backupFlagInclusionState).response; - const backupFileUrl = backupFileResponse.data?.createBackup.url; - if (!backupFileUrl) { - makeToast( - t('settings.backup.action.create.error.failure'), - 'error', - getErrorMessage(backupFileResponse.errors), - ); - return; + const backupFileUrl = backupFileResponse.data?.createBackup.url; + if (!backupFileUrl) { + makeToast( + t('settings.backup.action.create.error.failure'), + 'error', + getErrorMessage(backupFileResponse.errors), + ); + return; + } + + const link = document.createElement('a'); + link.href = backupFileUrl; + link.download = ''; + document.body.appendChild(link); + link.click(); + document.body.removeChild(link); + } catch (e) { + makeToast(t('settings.backup.action.create.error.failure'), 'error', getErrorMessage(e)); } - - const link = document.createElement('a'); - link.href = backupFileUrl; - link.download = ''; - document.body.appendChild(link); - link.click(); - document.body.removeChild(link); }; const validateBackup = async (file: File) => {