mirror of
https://github.com/clash-verge-rev/clash-verge-rev
synced 2025-05-05 01:13:44 +08:00
140 lines
3.8 KiB
TypeScript
140 lines
3.8 KiB
TypeScript
import {
|
|
forwardRef,
|
|
useImperativeHandle,
|
|
useState,
|
|
useCallback,
|
|
useEffect,
|
|
} from "react";
|
|
import { useTranslation } from "react-i18next";
|
|
import { BaseDialog, DialogRef } from "@/components/base";
|
|
import getSystem from "@/utils/get-system";
|
|
import { BaseLoadingOverlay } from "@/components/base";
|
|
import dayjs from "dayjs";
|
|
import customParseFormat from "dayjs/plugin/customParseFormat";
|
|
import {
|
|
BackupTableViewer,
|
|
BackupFile,
|
|
DEFAULT_ROWS_PER_PAGE,
|
|
} from "./backup-table-viewer";
|
|
import { BackupConfigViewer } from "./backup-config-viewer";
|
|
import { Box, Paper, Divider } from "@mui/material";
|
|
import { listWebDavBackup } from "@/services/cmds";
|
|
dayjs.extend(customParseFormat);
|
|
|
|
const DATE_FORMAT = "YYYY-MM-DD_HH-mm-ss";
|
|
const FILENAME_PATTERN = /\d{4}-\d{2}-\d{2}_\d{2}-\d{2}-\d{2}/;
|
|
|
|
export const BackupViewer = forwardRef<DialogRef>((props, ref) => {
|
|
const { t } = useTranslation();
|
|
const [open, setOpen] = useState(false);
|
|
|
|
const [isLoading, setIsLoading] = useState(false);
|
|
const [backupFiles, setBackupFiles] = useState<BackupFile[]>([]);
|
|
const [dataSource, setDataSource] = useState<BackupFile[]>([]);
|
|
const [total, setTotal] = useState(0);
|
|
const [page, setPage] = useState(0);
|
|
|
|
const OS = getSystem();
|
|
|
|
useImperativeHandle(ref, () => ({
|
|
open: () => {
|
|
setOpen(true);
|
|
},
|
|
close: () => setOpen(false),
|
|
}));
|
|
|
|
// Handle page change
|
|
const handleChangePage = useCallback(
|
|
(_: React.MouseEvent<HTMLButtonElement> | null, page: number) => {
|
|
setPage(page);
|
|
},
|
|
[]
|
|
);
|
|
|
|
const fetchAndSetBackupFiles = async () => {
|
|
try {
|
|
setIsLoading(true);
|
|
const files = await getAllBackupFiles();
|
|
setBackupFiles(files);
|
|
setTotal(files.length);
|
|
} catch (error) {
|
|
console.error(error);
|
|
// Notice.error(t("Failed to fetch backup files"));
|
|
} finally {
|
|
setIsLoading(false);
|
|
}
|
|
};
|
|
|
|
const getAllBackupFiles = async () => {
|
|
const files = await listWebDavBackup();
|
|
return files
|
|
.map((file) => {
|
|
const platform = file.filename.split("-")[0];
|
|
const fileBackupTimeStr = file.filename.match(FILENAME_PATTERN)!;
|
|
|
|
if (fileBackupTimeStr === null) {
|
|
return null;
|
|
}
|
|
|
|
const backupTime = dayjs(fileBackupTimeStr[0], DATE_FORMAT);
|
|
const allowApply = OS === platform;
|
|
return {
|
|
...file,
|
|
platform,
|
|
backup_time: backupTime,
|
|
allow_apply: allowApply,
|
|
} as BackupFile;
|
|
})
|
|
.filter((item) => item !== null)
|
|
.sort((a, b) => (a.backup_time.isAfter(b.backup_time) ? -1 : 1));
|
|
};
|
|
|
|
useEffect(() => {
|
|
setDataSource(
|
|
backupFiles.slice(
|
|
page * DEFAULT_ROWS_PER_PAGE,
|
|
page * DEFAULT_ROWS_PER_PAGE + DEFAULT_ROWS_PER_PAGE
|
|
)
|
|
);
|
|
}, [page, backupFiles]);
|
|
|
|
return (
|
|
<BaseDialog
|
|
open={open}
|
|
title={t("Backup Setting")}
|
|
contentSx={{ width: 600, maxHeight: 800 }}
|
|
okBtn={t("")}
|
|
cancelBtn={t("Close")}
|
|
onClose={() => setOpen(false)}
|
|
onCancel={() => setOpen(false)}
|
|
disableOk
|
|
>
|
|
<Box sx={{ maxWidth: 800 }}>
|
|
<BaseLoadingOverlay isLoading={isLoading} />
|
|
<Paper elevation={2} sx={{ padding: 2 }}>
|
|
<BackupConfigViewer
|
|
setLoading={setIsLoading}
|
|
onBackupSuccess={async () => {
|
|
fetchAndSetBackupFiles();
|
|
}}
|
|
onSaveSuccess={async () => {
|
|
fetchAndSetBackupFiles();
|
|
}}
|
|
onInit={async () => {
|
|
fetchAndSetBackupFiles();
|
|
}}
|
|
/>
|
|
<Divider sx={{ marginY: 2 }} />
|
|
<BackupTableViewer
|
|
datasource={dataSource}
|
|
page={page}
|
|
onPageChange={handleChangePage}
|
|
total={total}
|
|
onRefresh={fetchAndSetBackupFiles}
|
|
/>
|
|
</Paper>
|
|
</Box>
|
|
</BaseDialog>
|
|
);
|
|
});
|