MISC: Remove file-saver (#1217)

Also refactor to dedup our own download code
This commit is contained in:
catloversg 2024-04-20 03:38:44 +07:00 committed by GitHub
parent 216500ed32
commit 7b993f3550
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
9 changed files with 20 additions and 62 deletions

6
package-lock.json generated

@ -27,7 +27,6 @@
"clsx": "^1.2.1", "clsx": "^1.2.1",
"date-fns": "^2.30.0", "date-fns": "^2.30.0",
"escodegen": "^2.1.0", "escodegen": "^2.1.0",
"file-saver": "^2.0.5",
"js-sha256": "^0.9.0", "js-sha256": "^0.9.0",
"jszip": "^3.10.1", "jszip": "^3.10.1",
"material-ui-color": "^1.2.0", "material-ui-color": "^1.2.0",
@ -8458,11 +8457,6 @@
"node": "^10.12.0 || >=12.0.0" "node": "^10.12.0 || >=12.0.0"
} }
}, },
"node_modules/file-saver": {
"version": "2.0.5",
"resolved": "https://registry.npmjs.org/file-saver/-/file-saver-2.0.5.tgz",
"integrity": "sha512-P9bmyZ3h/PRG+Nzga+rbdI4OEpNDzAVyy74uVO9ATgzLK6VtAsYybF/+TOCvrc0MO793d6+42lLyZTw7/ArVzA=="
},
"node_modules/filename-reserved-regex": { "node_modules/filename-reserved-regex": {
"version": "2.0.0", "version": "2.0.0",
"resolved": "https://registry.npmjs.org/filename-reserved-regex/-/filename-reserved-regex-2.0.0.tgz", "resolved": "https://registry.npmjs.org/filename-reserved-regex/-/filename-reserved-regex-2.0.0.tgz",

@ -27,7 +27,6 @@
"clsx": "^1.2.1", "clsx": "^1.2.1",
"date-fns": "^2.30.0", "date-fns": "^2.30.0",
"escodegen": "^2.1.0", "escodegen": "^2.1.0",
"file-saver": "^2.0.5",
"js-sha256": "^0.9.0", "js-sha256": "^0.9.0",
"jszip": "^3.10.1", "jszip": "^3.10.1",
"material-ui-color": "^1.2.0", "material-ui-color": "^1.2.0",

@ -7,7 +7,6 @@ export type ContentFilePath = ScriptFilePath | TextFilePath;
export interface ContentFile { export interface ContentFile {
filename: ContentFilePath; filename: ContentFilePath;
content: string; content: string;
download: () => void;
deleteFromServer: (server: BaseServer) => boolean; deleteFromServer: (server: BaseServer) => boolean;
} }
export type ContentFileMap = Map<ContentFilePath, ContentFile>; export type ContentFileMap = Map<ContentFilePath, ContentFile>;

@ -41,6 +41,7 @@ import { getGoSave, loadGo } from "./Go/SaveLoad";
import { SaveData } from "./types"; import { SaveData } from "./types";
import { SaveDataError, canUseBinaryFormat, decodeSaveData, encodeJsonSaveString } from "./utils/SaveDataUtils"; import { SaveDataError, canUseBinaryFormat, decodeSaveData, encodeJsonSaveString } from "./utils/SaveDataUtils";
import { isBinaryFormat } from "../electron/saveDataBinaryFormat"; import { isBinaryFormat } from "../electron/saveDataBinaryFormat";
import { downloadContentAsFile } from "./utils/FileUtils";
/* SaveObject.js /* SaveObject.js
* Defines the object used to save/load games * Defines the object used to save/load games
@ -158,7 +159,7 @@ class BitburnerSaveObject {
async exportGame(): Promise<void> { async exportGame(): Promise<void> {
const saveData = await this.getSaveData(); const saveData = await this.getSaveData();
const filename = this.getSaveFileName(); const filename = this.getSaveFileName();
download(filename, saveData); downloadContentAsFile(saveData, filename);
} }
async importGame(saveData: SaveData, reload = true): Promise<void> { async importGame(saveData: SaveData, reload = true): Promise<void> {
@ -853,23 +854,8 @@ function createBetaUpdateText() {
); );
} }
function download(filename: string, content: SaveData): void {
const file = new Blob([content], { type: "text/plain" });
const a = document.createElement("a"),
url = URL.createObjectURL(file);
a.href = url;
a.download = filename;
document.body.appendChild(a);
a.click();
setTimeout(function () {
document.body.removeChild(a);
window.URL.revokeObjectURL(url);
}, 0);
}
constructorsForReviver.BitburnerSaveObject = BitburnerSaveObject; constructorsForReviver.BitburnerSaveObject = BitburnerSaveObject;
export { saveObject, loadGame, download }; export { saveObject, loadGame };
const saveObject = new BitburnerSaveObject(); const saveObject = new BitburnerSaveObject();

@ -45,22 +45,6 @@ export class Script implements ContentFile {
this.server = server; // hostname of server this script is on this.server = server; // hostname of server this script is on
} }
/** Download the script as a file */
download(): void {
const filename = this.filename;
const file = new Blob([this.code], { type: "text/plain" });
const a = document.createElement("a"),
url = URL.createObjectURL(file);
a.href = url;
a.download = filename;
document.body.appendChild(a);
a.click();
setTimeout(function () {
document.body.removeChild(a);
window.URL.revokeObjectURL(url);
}, 0);
}
/** Invalidates the current script module and related data, e.g. when modifying the file. */ /** Invalidates the current script module and related data, e.g. when modifying the file. */
invalidateModule(): void { invalidateModule(): void {
// Always clear ram usage // Always clear ram usage

@ -1,11 +1,11 @@
import { Terminal } from "../../Terminal"; import { Terminal } from "../../Terminal";
import { BaseServer } from "../../Server/BaseServer"; import { BaseServer } from "../../Server/BaseServer";
import FileSaver from "file-saver";
import JSZip from "jszip"; import JSZip from "jszip";
import { root } from "../../Paths/Directory"; import { root } from "../../Paths/Directory";
import { hasScriptExtension } from "../../Paths/ScriptFilePath"; import { hasScriptExtension } from "../../Paths/ScriptFilePath";
import { hasTextExtension } from "../../Paths/TextFilePath"; import { hasTextExtension } from "../../Paths/TextFilePath";
import { getGlobbedFileMap } from "../../Paths/GlobbedFiles"; import { getGlobbedFileMap } from "../../Paths/GlobbedFiles";
import { downloadContentAsFile } from "../../utils/FileUtils";
// Basic globbing implementation only supporting * and ?. Can be broken out somewhere else later. // Basic globbing implementation only supporting * and ?. Can be broken out somewhere else later.
export function exportScripts(pattern: string, server: BaseServer, currDir = root): void { export function exportScripts(pattern: string, server: BaseServer, currDir = root): void {
@ -17,10 +17,10 @@ export function exportScripts(pattern: string, server: BaseServer, currDir = roo
// Return an error if no files matched, rather than an empty zip folder // Return an error if no files matched, rather than an empty zip folder
if (Object.keys(zip.files).length == 0) throw new Error(`No files match the pattern ${pattern}`); if (Object.keys(zip.files).length == 0) throw new Error(`No files match the pattern ${pattern}`);
const zipFn = `bitburner${ const filename = `bitburner${
hasScriptExtension(pattern) ? "Scripts" : pattern.endsWith(".txt") ? "Texts" : "Files" hasScriptExtension(pattern) ? "Scripts" : pattern.endsWith(".txt") ? "Texts" : "Files"
}.zip`; }.zip`;
zip.generateAsync({ type: "blob" }).then((content: Blob) => FileSaver.saveAs(content, zipFn)); zip.generateAsync({ type: "blob" }).then((content: Blob) => downloadContentAsFile(content, filename));
} }
export function download(args: (string | number | boolean)[], server: BaseServer): void { export function download(args: (string | number | boolean)[], server: BaseServer): void {
@ -45,5 +45,5 @@ export function download(args: (string | number | boolean)[], server: BaseServer
} }
const file = server.getContentFile(path); const file = server.getContentFile(path);
if (!file) return Terminal.error(`File not found: ${path}`); if (!file) return Terminal.error(`File not found: ${path}`);
return file.download(); return downloadContentAsFile(file.content, file.filename);
} }

@ -24,21 +24,6 @@ export class TextFile implements ContentFile {
this.text = txt; this.text = txt;
} }
/** Serves the file to the user as a downloadable resource through the browser. */
download(): void {
const file: Blob = new Blob([this.text], { type: "text/plain" });
const a: HTMLAnchorElement = document.createElement("a");
const url: string = URL.createObjectURL(file);
a.href = url;
a.download = this.filename;
document.body.appendChild(a);
a.click();
setTimeout(() => {
document.body.removeChild(a);
URL.revokeObjectURL(url);
}, 0);
}
/** Serialize the current file to a JSON save state. */ /** Serialize the current file to a JSON save state. */
toJSON(): IReviverValue { toJSON(): IReviverValue {
return Generic_toJSON("TextFile", this); return Generic_toJSON("TextFile", this);

@ -5,7 +5,6 @@ import { Settings } from "../../Settings/Settings";
import { load } from "../../db"; import { load } from "../../db";
import { Router } from "../GameRoot"; import { Router } from "../GameRoot";
import { Page } from "../Router"; import { Page } from "../Router";
import { download } from "../../SaveObject";
import { IErrorData, newIssueUrl } from "../../utils/ErrorHelper"; import { IErrorData, newIssueUrl } from "../../utils/ErrorHelper";
import { DeleteGameButton } from "./DeleteGameButton"; import { DeleteGameButton } from "./DeleteGameButton";
import { SoftResetButton } from "./SoftResetButton"; import { SoftResetButton } from "./SoftResetButton";
@ -14,6 +13,7 @@ import DirectionsRunIcon from "@mui/icons-material/DirectionsRun";
import GitHubIcon from "@mui/icons-material/GitHub"; import GitHubIcon from "@mui/icons-material/GitHub";
import { isBinaryFormat } from "../../../electron/saveDataBinaryFormat"; import { isBinaryFormat } from "../../../electron/saveDataBinaryFormat";
import { InvalidSaveData, UnsupportedSaveData } from "../../utils/SaveDataUtils"; import { InvalidSaveData, UnsupportedSaveData } from "../../utils/SaveDataUtils";
import { downloadContentAsFile } from "../../utils/FileUtils";
export let RecoveryMode = false; export let RecoveryMode = false;
let sourceError: unknown; let sourceError: unknown;
@ -44,7 +44,7 @@ export function RecoveryRoot({ softReset, errorData, resetError }: IProps): Reac
const epochTime = Math.round(Date.now() / 1000); const epochTime = Math.round(Date.now() / 1000);
const extension = isBinaryFormat(content) ? "json.gz" : "json"; const extension = isBinaryFormat(content) ? "json.gz" : "json";
const filename = `RECOVERY_BITBURNER_${epochTime}.${extension}`; const filename = `RECOVERY_BITBURNER_${epochTime}.${extension}`;
download(filename, content); downloadContentAsFile(content, filename);
}) })
.catch((err) => console.error(err)); .catch((err) => console.error(err));
}, []); }, []);

11
src/utils/FileUtils.ts Normal file

@ -0,0 +1,11 @@
export function downloadContentAsFile(content: BlobPart, filename: string): void {
const blob = new Blob([content]);
const anchorElement = document.createElement("a");
const url = URL.createObjectURL(blob);
anchorElement.href = url;
anchorElement.download = filename;
anchorElement.click();
setTimeout(function () {
URL.revokeObjectURL(url);
}, 0);
}