diff --git a/src/bridges/utils.ts b/src/bridges/utils.ts index 9be7c91f..eef0c929 100644 --- a/src/bridges/utils.ts +++ b/src/bridges/utils.ts @@ -64,6 +64,14 @@ const utilsBridge = { )) as string; }, + autosaveBackupData: (writeContent: string): Promise => { + return ipcRenderer.invoke("write-autosave", writeContent); + }, + + getLastAutosaveTimestamp: (): Promise => { + return ipcRenderer.invoke("get-last-autosave-timestamp"); + }, + getCacheSize: async (): Promise => { return await ipcRenderer.invoke("get-cache"); }, diff --git a/src/index.tsx b/src/index.tsx index 5175036e..090302f8 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -11,6 +11,7 @@ import { initApp, openTextMenu } from "./scripts/models/app"; import { startPool } from "./scripts/fetch-pool"; import { rootStore } from "./scripts/reducer"; import { initThemeUpdateListener } from "./scripts/settings"; +import { renewAutosave } from "./scripts/autosave"; startPool(); // Start global fetch pool. initThemeUpdateListener(); @@ -22,6 +23,8 @@ initializeIcons("icons/"); rootStore.dispatch(initApp()); +renewAutosave(); + window.utils.addMainContextListener((pos, text) => { rootStore.dispatch(openTextMenu(pos, text)); }); diff --git a/src/main/utils.ts b/src/main/utils.ts index f0878b1b..63f3d745 100644 --- a/src/main/utils.ts +++ b/src/main/utils.ts @@ -7,13 +7,20 @@ import { clipboard, systemPreferences, } from "electron"; +import path from "path"; +import { getFonts } from "font-list"; +import { promises, writeFile } from "fs"; + import pkg from "../../package.json"; import { WindowManager } from "./window"; import { trimContent, trimString } from "./pure-utils"; -import { promises, writeFile } from "fs"; import { ImageCallbackTypes, TouchBarTexts } from "../schema-types"; import { initMainTouchBar } from "./touchbar"; -import { getFonts } from "font-list"; + +function getAutosavePath() { + const userDataPath = app.getPath("userData"); + return `${userDataPath}/autosaves/ffr_autosave.frdata`; +} export function setUtilsListeners(manager: WindowManager) { async function openExternal(url: string, background = false) { @@ -159,6 +166,37 @@ export function setUtilsListeners(manager: WindowManager) { }, ); + ipcMain.handle("write-autosave", async (_, writeContent: string) => { + const autosavePath = getAutosavePath(); + try { + const dirStat = await promises.stat(path.dirname(autosavePath), { + throwIfNoEntry: false, + }); + if (dirStat == undefined) { + await promises.mkdir(path.dirname(autosavePath)); + } + await promises.writeFile(getAutosavePath(), writeContent); + } catch (err: any) { + console.error("Failed to write autosave", err); + } + }); + + ipcMain.handle( + "get-last-autosave-timestamp", + async (): Promise => { + const autosavePath = getAutosavePath(); + try { + const result = await promises.stat(autosavePath, { + throwIfNoEntry: false, + }); + return result?.mtimeMs; + } catch (err: any) { + console.error(`Failed to stat ${autosavePath}`, err); + return undefined; + } + }, + ); + ipcMain.handle("get-cache", async () => { return await session.defaultSession.getCacheSize(); }); diff --git a/src/scripts/autosave.tsx b/src/scripts/autosave.tsx new file mode 100644 index 00000000..f6939e47 --- /dev/null +++ b/src/scripts/autosave.tsx @@ -0,0 +1,37 @@ +import * as db from "./db"; + +/** + * Renew the autosave file in userData if it's been long enough. + */ +export async function renewAutosave() { + const oneDay = 24 * 60 * 60 * 1000; + const autosaveMTimeMs = await window.utils.getLastAutosaveTimestamp(); + const autosaveStartTime = Date.now(); + if (autosaveStartTime < autosaveMTimeMs + oneDay) { + console.debug("Autosave still current, not rewriting."); + return; + } + let sources: db.SourceEntry[]; + let items: db.ItemEntry[]; + const transaction = db.fluentDB.transaction( + "r", + db.fluentDB.sources, + db.fluentDB.items, + async () => { + sources = await db.fluentDB.sources.toArray(); + items = await db.fluentDB.items.toArray(); + }, + ); + const output = window.settings.getAll(); + await transaction; + output["database"] = { + sources: sources, + items: items, + }; + await window.utils.autosaveBackupData(JSON.stringify(output)); + const autosaveEndTime = Date.now(); + console.debug( + "Completed autosave in " + + `${Math.round(autosaveEndTime - autosaveStartTime)}ms.`, + ); +} diff --git a/src/scripts/db.ts b/src/scripts/db.ts index a7ff3dcd..82d55676 100644 --- a/src/scripts/db.ts +++ b/src/scripts/db.ts @@ -294,7 +294,11 @@ function byteLength(str: string) { } export async function init() { - await migrateLovefieldSourcesDB("sourcesDB", 3); + try { + await migrateLovefieldSourcesDB("sourcesDB", 3); + } catch (e: any) { + console.error("Error migrating sources DB", e); + } try { await migrateLovefieldItemsDB("itemsDB", 1); } catch (e) {