import * as electron from "electron"; import * as path from "path"; import * as fs from "fs-extra"; const APP_DATA = electron.remote.app.getPath("userData"); const SETTINGS_DIR = path.join(APP_DATA, "settings"); let _local_storage: {[key: string]: any} = {}; let _local_storage_save: {[key: string]: boolean} = {}; let _save_timer: NodeJS.Timer; export async function initialize() { await fs.mkdirp(SETTINGS_DIR); const files = await fs.readdir(SETTINGS_DIR); for(const file of files) { const key = decodeURIComponent(file); console.log("Load settings: %s", key); try { const data = await fs.readFile(path.join(SETTINGS_DIR, file)); const decoded = JSON.parse(data.toString() || "{}"); _local_storage[key] = decoded; } catch(error) { const target_file = path.join(SETTINGS_DIR, file + "." + Date.now() + ".broken"); console.error("Failed to load settings for %s: %o. Moving settings so the file does not get overridden. Target file: %s", key, error, target_file); try { await fs.move(path.join(SETTINGS_DIR, file), target_file); } catch (error) { console.warn("Failed to move broken settings file!"); } } } let _new_storage: Storage = {} as any; _new_storage.getItem = key => _local_storage[key] || null; _new_storage.setItem = (key, value) => { _local_storage[key] = value; _local_storage_save[key] = true; (_new_storage as any)["length"] = Object.keys(_local_storage).length; }; _new_storage.clear = () => { _local_storage = {}; _local_storage_save = {}; try { fs.emptyDirSync(SETTINGS_DIR); } catch(error) { console.warn("Failed to empty settings dir"); } (_new_storage as any)["length"] = 0; }; _new_storage.key = index => Object.keys(_local_storage)[index]; _new_storage.removeItem = key => { delete _local_storage[key]; delete_key(key).catch(error => { console.warn("Failed to delete key on fs: %s => %o", key, error); }); (_new_storage as any)["length"] = Object.keys(_local_storage).length; }; Object.assign(window.localStorage, _new_storage); /* try to save everything all 60 seconds */ _save_timer = setInterval(() => { save_all_sync(); }, 60 * 1000); } export function save_all_sync() { for(const key of Object.keys(_local_storage_save)) save_key_sync(key); } function key_path(key: string) { return path.join(SETTINGS_DIR, encodeURIComponent(key)); } export function save_key_sync(key: string) { if(!_local_storage_save[key]) return; delete _local_storage_save[key]; const setting_path = key_path(key); fs.writeJsonSync(setting_path, _local_storage[key], {spaces: 0}); } export async function delete_key(key: string) { delete _local_storage_save[key]; const setting_path = key_path(key); await fs.remove(setting_path); /* could be async because we're not carrying about data */ } window.addEventListener("beforeunload", event => { save_all_sync(); });