mirror of
https://github.com/bitburner-official/bitburner-src.git
synced 2024-11-29 19:13:49 +01:00
Add validation & confirm prompt for save import
This commit is contained in:
parent
b578e09986
commit
2b7464ebb7
@ -28,6 +28,7 @@ import { FileDiagnosticModal } from "../../Diagnostic/FileDiagnosticModal";
|
||||
import { dialogBoxCreate } from "./DialogBox";
|
||||
import { ConfirmationModal } from "./ConfirmationModal";
|
||||
import { ThemeEditorModal } from "./ThemeEditorModal";
|
||||
import { SnackbarEvents } from "./Snackbar";
|
||||
|
||||
import { Settings } from "../../Settings/Settings";
|
||||
import { save, deleteGame } from "../../db";
|
||||
@ -51,6 +52,12 @@ interface IProps {
|
||||
softReset: () => void;
|
||||
}
|
||||
|
||||
interface ImportData {
|
||||
base64: string;
|
||||
parsed: any;
|
||||
exportDate?: Date;
|
||||
}
|
||||
|
||||
export function GameOptionsRoot(props: IProps): React.ReactElement {
|
||||
const classes = useStyles();
|
||||
const importInput = useRef<HTMLInputElement>(null);
|
||||
@ -84,6 +91,8 @@ export function GameOptionsRoot(props: IProps): React.ReactElement {
|
||||
const [deleteGameOpen, setDeleteOpen] = useState(false);
|
||||
const [themeEditorOpen, setThemeEditorOpen] = useState(false);
|
||||
const [softResetOpen, setSoftResetOpen] = useState(false);
|
||||
const [importSaveOpen, setImportSaveOpen] = useState(false);
|
||||
const [importData, setImportData] = useState<ImportData | null>(null);
|
||||
|
||||
function handleExecTimeChange(event: any, newValue: number | number[]): void {
|
||||
setExecTime(newValue as number);
|
||||
@ -206,11 +215,67 @@ export function GameOptionsRoot(props: IProps): React.ReactElement {
|
||||
return;
|
||||
}
|
||||
const contents = result;
|
||||
save(contents).then(() => setTimeout(() => location.reload(), 1000));
|
||||
|
||||
// https://stackoverflow.com/a/35002237
|
||||
const base64regex = /^([0-9a-zA-Z+/]{4})*(([0-9a-zA-Z+/]{2}==)|([0-9a-zA-Z+/]{3}=))?$/;
|
||||
if (!base64regex.test(contents)) {
|
||||
SnackbarEvents.emit("Save game was not a base64 string", "error", 5000);
|
||||
return;
|
||||
}
|
||||
|
||||
let newSave;
|
||||
try {
|
||||
newSave = window.atob(contents);
|
||||
newSave = newSave.trim();
|
||||
} catch (error) {
|
||||
console.log(error); // We'll handle below
|
||||
}
|
||||
|
||||
if (!newSave || newSave === '') {
|
||||
SnackbarEvents.emit("Save game had not content", "error", 5000);
|
||||
return;
|
||||
}
|
||||
|
||||
let parsedSave;
|
||||
try {
|
||||
parsedSave = JSON.parse(newSave);
|
||||
} catch (error) {
|
||||
console.log(error); // We'll handle below
|
||||
}
|
||||
|
||||
if (!parsedSave || parsedSave.ctor !== 'BitburnerSaveObject' || !parsedSave.data) {
|
||||
SnackbarEvents.emit("Save game did not seem valid", "error", 5000);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
const data: ImportData = {
|
||||
base64: contents,
|
||||
parsed: parsedSave,
|
||||
}
|
||||
|
||||
// We don't always seem to have this value in the save file. Exporting from the option menu does not set the bonus I think.
|
||||
const exportTimestamp = parsedSave.data.LastExportBonus;
|
||||
if (exportTimestamp && exportTimestamp !== '0') {
|
||||
data.exportDate = new Date(parseInt(exportTimestamp, 10))
|
||||
}
|
||||
|
||||
setImportData(data)
|
||||
setImportSaveOpen(true);
|
||||
};
|
||||
reader.readAsText(file);
|
||||
}
|
||||
|
||||
function confirmedImportGame(): void {
|
||||
if (!importData) return;
|
||||
|
||||
setImportSaveOpen(false);
|
||||
save(importData.base64).then(() => {
|
||||
setImportData(null);
|
||||
setTimeout(() => location.reload(), 1000)
|
||||
});
|
||||
}
|
||||
|
||||
function doSoftReset(): void {
|
||||
if (!Settings.SuppressBuyAugmentationConfirmation) {
|
||||
setSoftResetOpen(true);
|
||||
@ -618,19 +683,41 @@ export function GameOptionsRoot(props: IProps): React.ReactElement {
|
||||
<Button onClick={() => setDeleteOpen(true)}>Delete Game</Button>
|
||||
</Box>
|
||||
<Box>
|
||||
<Tooltip title={<Typography>export</Typography>}>
|
||||
<Tooltip title={<Typography>Export your game to a text file.</Typography>}>
|
||||
<Button onClick={() => props.export()}>
|
||||
<DownloadIcon color="primary" />
|
||||
Export
|
||||
Export Game
|
||||
</Button>
|
||||
</Tooltip>
|
||||
<Tooltip title={<Typography>import</Typography>}>
|
||||
<Tooltip title={<Typography>Import your game from a text file.<br/>This will <strong>overwrite</strong> your current game. Back it up first!</Typography>}>
|
||||
<Button onClick={startImport}>
|
||||
<UploadIcon color="primary" />
|
||||
Import
|
||||
Import Game
|
||||
<input ref={importInput} id="import-game-file-selector" type="file" hidden onChange={onImport} />
|
||||
</Button>
|
||||
</Tooltip>
|
||||
<ConfirmationModal
|
||||
open={importSaveOpen}
|
||||
onClose={() => setImportSaveOpen(false)}
|
||||
onConfirm={() => confirmedImportGame()}
|
||||
confirmationText={
|
||||
<>
|
||||
Importing a new game will <strong>completely wipe</strong> the current data!
|
||||
<br />
|
||||
<br />
|
||||
Make sure to have a backup of your current save file before importing.
|
||||
<br />
|
||||
The file you are attempting to import seems valid.
|
||||
<br />
|
||||
<br />
|
||||
{importData?.exportDate && (<>
|
||||
The export date of the save file is <strong>{importData?.exportDate.toString()}</strong>
|
||||
<br />
|
||||
<br />
|
||||
</>)}
|
||||
</>
|
||||
}
|
||||
/>
|
||||
</Box>
|
||||
<Box>
|
||||
<Tooltip
|
||||
|
Loading…
Reference in New Issue
Block a user