/* * 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 { useContext, useEffect, useState } from 'react'; import List from '@mui/material/List'; import ListItemText from '@mui/material/ListItemText'; import { fromEvent } from 'file-selector'; import { useTranslation } from 'react-i18next'; import { ListItemButton } from '@mui/material'; import ListItemIcon from '@mui/material/ListItemIcon'; import { requestManager } from '@/lib/requests/RequestManager.ts'; import { makeToast } from '@/components/util/Toast'; import { ListItemLink } from '@/components/util/ListItemLink'; import { NavBarContext, useSetDefaultBackTo } from '@/components/context/NavbarContext'; import { BackupRestoreState } from '@/lib/graphql/generated/graphql.ts'; import { Progress } from '@/components/util/Progress.tsx'; let backupRestoreId: string | undefined; export function Backup() { const { t } = useTranslation(); const { setTitle, setAction } = useContext(NavBarContext); useEffect(() => { setTitle(t('settings.backup.title')); setAction(null); }, [t]); useSetDefaultBackTo('settings'); const { data } = requestManager.useGetBackupRestoreStatus(backupRestoreId ?? '', { skip: !backupRestoreId, pollInterval: 1000, }); const [, setTriggerReRender] = useState(0); const restoreProgress = (() => { if (!data?.restoreStatus) { return 0; } const progress = 100 * (data.restoreStatus.mangaProgress / data.restoreStatus.totalManga); return Number.isNaN(progress) ? 0 : progress; })(); useEffect(() => { if (!data?.restoreStatus) { return; } const isSuccess = data.restoreStatus.state === BackupRestoreState.Success; const isFailure = data.restoreStatus.state === BackupRestoreState.Failure; const isRestoreFinished = isSuccess || isFailure; if (isRestoreFinished) { if (isSuccess) { makeToast(t('settings.backup.label.restored_backup'), 'success'); } if (isFailure) { makeToast(t('settings.backup.label.backup_restore_failed'), 'error'); } backupRestoreId = undefined; setTriggerReRender(Date.now()); } }, [data?.restoreStatus?.state]); const submitBackup = async (file: File) => { if (file.name.toLowerCase().match(/proto\.gz$|tachibk$/g)) { makeToast(t('settings.backup.label.restoring_backup'), 'info'); try { const response = await requestManager.restoreBackupFile(file).response; backupRestoreId = response.data?.restoreBackup.id; setTriggerReRender(Date.now()); } catch (e) { makeToast(t('settings.backup.label.backup_restore_failed'), 'error'); } } else if (file.name.toLowerCase().endsWith('json')) { makeToast(t('settings.backup.label.legacy_backup_unsupported'), 'error'); } else { makeToast(t('global.error.label.invalid_file_type'), 'error'); } }; const dropHandler = async (e: Event) => { e.preventDefault(); const files = await fromEvent(e); submitBackup(files[0] as File); }; const dragOverHandler = (e: Event) => { e.preventDefault(); }; useEffect(() => { document.addEventListener('drop', dropHandler); document.addEventListener('dragover', dragOverHandler); const handleFileSelection = async (event: Event) => { const files = await fromEvent(event); submitBackup(files[0] as File); }; const input = document.getElementById('backup-file'); input?.addEventListener('change', handleFileSelection); return () => { document.removeEventListener('drop', dropHandler); document.removeEventListener('dragover', dragOverHandler); input?.removeEventListener('change', handleFileSelection); }; }, []); return ( <> document.getElementById('backup-file')?.click()} disabled={!!backupRestoreId} > {backupRestoreId ? ( ) : null} ); }