2021-04-10 00:44:13 +04:30
|
|
|
/*
|
|
|
|
|
* 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
|
2023-05-18 13:17:41 +02:00
|
|
|
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
|
|
|
|
*/
|
2021-04-10 00:44:13 +04:30
|
|
|
|
2023-10-29 01:49:27 +02:00
|
|
|
import { useContext, useEffect, useRef, useState } from 'react';
|
2021-09-09 17:51:22 +04:30
|
|
|
import List from '@mui/material/List';
|
|
|
|
|
import ListItemText from '@mui/material/ListItemText';
|
2021-04-10 00:44:13 +04:30
|
|
|
import { fromEvent } from 'file-selector';
|
2023-03-23 13:59:32 +01:00
|
|
|
import { useTranslation } from 'react-i18next';
|
2023-06-04 21:13:07 +02:00
|
|
|
import { ListItemButton } from '@mui/material';
|
2023-10-29 01:49:27 +02:00
|
|
|
import ListItemIcon from '@mui/material/ListItemIcon';
|
2023-10-28 00:32:02 +02:00
|
|
|
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';
|
2023-10-29 01:49:27 +02:00
|
|
|
import { BackupRestoreState, GetRestoreStatusQuery } from '@/lib/graphql/generated/graphql.ts';
|
|
|
|
|
import { Progress } from '@/components/util/Progress.tsx';
|
2021-04-10 00:44:13 +04:30
|
|
|
|
2023-10-28 00:32:02 +02:00
|
|
|
export function Backup() {
|
2023-03-23 13:59:32 +01:00
|
|
|
const { t } = useTranslation();
|
2023-10-28 00:32:02 +02:00
|
|
|
const { setTitle, setAction } = useContext(NavBarContext);
|
2023-02-06 10:06:33 +01:00
|
|
|
useEffect(() => {
|
2023-03-23 13:59:32 +01:00
|
|
|
setTitle(t('settings.backup.title'));
|
2023-03-11 18:05:58 +01:00
|
|
|
setAction(null);
|
2023-03-28 23:14:03 +02:00
|
|
|
}, [t]);
|
2021-04-10 00:44:13 +04:30
|
|
|
|
2023-06-26 20:40:35 +02:00
|
|
|
useSetDefaultBackTo('settings');
|
|
|
|
|
|
2023-10-29 01:49:27 +02:00
|
|
|
const [isRestoring, setIsRestoring] = useState<boolean | null>(null);
|
|
|
|
|
const { data } = requestManager.useGetBackupRestoreStatus({
|
|
|
|
|
skip: isRestoring !== null ? !isRestoring : false,
|
|
|
|
|
pollInterval: 1000,
|
|
|
|
|
});
|
|
|
|
|
const prevRestoreStatusRef = useRef<GetRestoreStatusQuery['restoreStatus'] | null>(null);
|
|
|
|
|
|
|
|
|
|
const restoreProgress = (() => {
|
|
|
|
|
if (!isRestoring || !data) {
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const progress = 100 * (data.restoreStatus.mangaProgress / data.restoreStatus.totalManga);
|
|
|
|
|
return Number.isNaN(progress) ? 0 : progress;
|
|
|
|
|
})();
|
|
|
|
|
|
|
|
|
|
useEffect(() => {
|
|
|
|
|
if (!data || isRestoring !== null) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const isRestoreInProgress = data?.restoreStatus.state !== BackupRestoreState.Idle;
|
|
|
|
|
setIsRestoring(isRestoreInProgress);
|
|
|
|
|
}, [data?.restoreStatus.state]);
|
|
|
|
|
|
|
|
|
|
useEffect(() => {
|
|
|
|
|
if (!data) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const isRestoreFinished =
|
|
|
|
|
isRestoring &&
|
|
|
|
|
data.restoreStatus.mangaProgress === data.restoreStatus.totalManga &&
|
|
|
|
|
prevRestoreStatusRef.current?.state !== BackupRestoreState.Idle;
|
|
|
|
|
if (isRestoreFinished) {
|
|
|
|
|
setIsRestoring(false);
|
|
|
|
|
makeToast(t('settings.backup.label.restored_backup'), 'success');
|
|
|
|
|
}
|
|
|
|
|
prevRestoreStatusRef.current = data.restoreStatus;
|
|
|
|
|
}, [data?.restoreStatus.state]);
|
|
|
|
|
|
|
|
|
|
const submitBackup = async (file: File) => {
|
2023-10-29 01:45:08 +02:00
|
|
|
if (file.name.toLowerCase().match(/proto\.gz$|tachibk$/g)) {
|
2023-03-23 13:59:32 +01:00
|
|
|
makeToast(t('settings.backup.label.restoring_backup'), 'info');
|
2023-10-29 01:49:27 +02:00
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
await requestManager.restoreBackupFile(file).response;
|
|
|
|
|
setIsRestoring(true);
|
|
|
|
|
} catch (e) {
|
|
|
|
|
makeToast(t('settings.backup.label.backup_restore_failed'), 'error');
|
|
|
|
|
}
|
2021-08-19 20:42:28 +04:30
|
|
|
} else if (file.name.toLowerCase().endsWith('json')) {
|
2023-03-23 13:59:32 +01:00
|
|
|
makeToast(t('settings.backup.label.legacy_backup_unsupported'), 'error');
|
2021-08-19 20:42:28 +04:30
|
|
|
} else {
|
2023-03-23 13:59:32 +01:00
|
|
|
makeToast(t('global.error.label.invalid_file_type'), 'error');
|
2021-08-19 20:42:28 +04:30
|
|
|
}
|
2021-04-10 00:44:13 +04:30
|
|
|
};
|
|
|
|
|
|
|
|
|
|
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);
|
|
|
|
|
|
2023-10-29 01:49:27 +02:00
|
|
|
const handleFileSelection = async (event: Event) => {
|
|
|
|
|
const files = await fromEvent(event);
|
2021-04-10 00:44:13 +04:30
|
|
|
submitBackup(files[0] as File);
|
2023-10-29 01:49:27 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const input = document.getElementById('backup-file');
|
|
|
|
|
input?.addEventListener('change', handleFileSelection);
|
2021-04-10 00:44:13 +04:30
|
|
|
|
|
|
|
|
return () => {
|
|
|
|
|
document.removeEventListener('drop', dropHandler);
|
|
|
|
|
document.removeEventListener('dragover', dragOverHandler);
|
2023-10-29 01:49:27 +02:00
|
|
|
input?.removeEventListener('change', handleFileSelection);
|
2021-04-10 00:44:13 +04:30
|
|
|
};
|
|
|
|
|
}, []);
|
|
|
|
|
|
|
|
|
|
return (
|
2021-08-19 20:42:28 +04:30
|
|
|
<>
|
2021-11-15 18:55:50 +03:30
|
|
|
<List sx={{ padding: 0 }}>
|
2023-05-18 13:25:19 +02:00
|
|
|
<ListItemLink to={requestManager.getExportBackupUrl()} directLink>
|
2023-03-23 13:59:32 +01:00
|
|
|
<ListItemText
|
|
|
|
|
primary={t('settings.backup.label.create_backup')}
|
|
|
|
|
secondary={t('settings.backup.label.create_backup_info')}
|
|
|
|
|
/>
|
2021-08-19 20:42:28 +04:30
|
|
|
</ListItemLink>
|
2023-10-29 01:49:27 +02:00
|
|
|
<ListItemButton
|
|
|
|
|
onClick={() => document.getElementById('backup-file')?.click()}
|
|
|
|
|
disabled={!!isRestoring}
|
|
|
|
|
>
|
2021-08-19 20:42:28 +04:30
|
|
|
<ListItemText
|
2023-03-23 13:59:32 +01:00
|
|
|
primary={t('settings.backup.label.restore_backup')}
|
|
|
|
|
secondary={t('settings.backup.label.restore_backup_info')}
|
2021-08-19 20:42:28 +04:30
|
|
|
/>
|
2023-10-29 01:49:27 +02:00
|
|
|
{isRestoring ? (
|
|
|
|
|
<ListItemIcon>
|
|
|
|
|
<Progress progress={restoreProgress} />
|
|
|
|
|
</ListItemIcon>
|
|
|
|
|
) : null}
|
2023-06-04 21:13:07 +02:00
|
|
|
</ListItemButton>
|
2021-08-19 20:42:28 +04:30
|
|
|
</List>
|
2023-02-06 10:06:33 +01:00
|
|
|
<input type="file" id="backup-file" style={{ display: 'none' }} />
|
2021-08-19 20:42:28 +04:30
|
|
|
</>
|
2021-04-10 00:44:13 +04:30
|
|
|
);
|
|
|
|
|
}
|