Ver código fonte

ref: implement UserUtils' ConfigManager

Sven 1 ano atrás
pai
commit
07ffa419cd
5 arquivos alterados com 43 adições e 76 exclusões
  1. 24 56
      src/config.ts
  2. 2 4
      src/features/input.ts
  3. 2 3
      src/features/layout.ts
  4. 7 9
      src/features/menu/menu_old.ts
  5. 8 4
      src/index.ts

+ 24 - 56
src/config.ts

@@ -1,73 +1,41 @@
+import { ConfigManager } from "@sv443-network/userutils";
 import { featInfo } from "./features/index";
 import { FeatureConfig } from "./types";
-import { error, log } from "./utils";
+import { log } from "./utils";
 
-/** If this number is incremented, the features object needs to be migrated (TODO: migration not implemented yet) */
+/** If this number is incremented, the features object data will be migrated to the new format */
 const formatVersion = 1;
 
-export const defaultFeatures = (Object.keys(featInfo) as (keyof typeof featInfo)[])
+export const defaultConfig = (Object.keys(featInfo) as (keyof typeof featInfo)[])
   .reduce<Partial<FeatureConfig>>((acc, key) => {
     acc[key] = featInfo[key].default as unknown as undefined;
     return acc;
   }, {}) as FeatureConfig;
 
-/** In-memory features object to save on a little bit of I/O */
-let featuresCache: FeatureConfig | undefined;
-
-/**
- * Returns the current FeatureConfig in memory or reads it from GM storage if undefined.  
- * Automatically applies defaults for non-existant keys
- * @param forceRead Set to true to force reading the config from GM storage
- */
-export async function getFeatures(forceRead = false) {
-  if(!featuresCache || forceRead)
-    featuresCache = await loadFeatureConf();
-  return featuresCache;
+const cfgMgr = new ConfigManager({
+  id: "bytm-config",
+  formatVersion,
+  defaultConfig,
+});
+
+/** Initializes the ConfigManager instance and loads persistent data into memory */
+export async function initConfig() {
+  const data = await cfgMgr.loadData();
+  log(`Initialized ConfigManager (format version = ${cfgMgr.formatVersion})`);
+  return data;
 }
 
-/** Loads a feature configuration saved persistently, returns an empty object if no feature configuration was saved */
-export async function loadFeatureConf(): Promise<FeatureConfig> {
-  const defConf = { ...defaultFeatures };
-
-  try {
-    const featureConf = await GM.getValue("betterytm-config") as string;
-
-    // empty object length is 2-3, so check for >3 to be sure
-    if(typeof featureConf !== "string" || featureConf.length <= 3) {
-      await setDefaultFeatConf();
-      return featuresCache = defConf;
-    }
-
-    return featuresCache = {
-      ...defConf,
-      ...(featureConf ? JSON.parse(featureConf) as FeatureConfig : {}),
-    };
-  }
-  catch(err) {
-    error("Error loading feature configuration, resetting to default:", err);
-    await setDefaultFeatConf();
-    return featuresCache = defConf;
-  }
+/** Returns the current feature config from the in-memory cache */
+export function getFeatures() {
+  return cfgMgr.getData();
 }
 
-/**
- * Saves the passed feature configuration persistently in GM storage and in the in-memory cache
- * @param featureConf
- */
-export function saveFeatureConf(featureConf: FeatureConfig) {
-  if(!featureConf || typeof featureConf != "object")
-    throw new TypeError("Feature config not provided or invalid");
- 
-  log("Saving new feature config:", featureConf);
-  featuresCache = { ...featureConf };
-
-  GM.setValue("betterytm-config-ver", formatVersion);
-  return GM.setValue("betterytm-config", JSON.stringify(featureConf));
+/** Saves the feature config synchronously to the in-memory cache and asynchronously to the persistent storage */
+export function saveFeatures(featureConf: FeatureConfig) {
+  return cfgMgr.setData(featureConf);
 }
 
-/** Resets the featuresCache synchronously and the persistent features storage asynchronously to their default values */
-export function setDefaultFeatConf() {
-  featuresCache = { ...defaultFeatures };
-  GM.setValue("betterytm-config-ver", formatVersion);
-  return GM.setValue("betterytm-config", JSON.stringify(defaultFeatures));
+/** Saves the default feature config synchronously to the in-memory cache and asynchronously to persistent storage */
+export function setDefaultFeatures() {
+  return cfgMgr.saveDefaultData();
 }

+ 2 - 4
src/features/input.ts

@@ -174,8 +174,6 @@ export function initBeforeUnloadHook() {
     // @ts-ignore
   })(window.__proto__.addEventListener);
 
-  getFeatures().then(feats => {
-    if(feats.disableBeforeUnloadPopup)
-      disableBeforeUnload();
-  });
+  if(getFeatures().disableBeforeUnloadPopup)
+    disableBeforeUnload();
 }

+ 2 - 3
src/features/layout.ts

@@ -2,7 +2,6 @@ import type { Event } from "@billjs/event-emitter";
 import { addGlobalStyle, addParent, autoPlural, fetchAdvanced, insertAfter, onSelector, openInNewTab, pauseFor } from "@sv443-network/userutils";
 import type { FeatureConfig } from "../types";
 import { scriptInfo } from "../constants";
-import { getFeatures } from "../config";
 import { error, getResourceUrl, log } from "../utils";
 import { getEvtData, siteEvents } from "../events";
 import { openMenu } from "./menu/menu_old";
@@ -12,8 +11,8 @@ import { featInfo } from ".";
 
 let features: FeatureConfig;
 
-export async function preInitLayout() {
-  features = await getFeatures();
+export function preInitLayout(feats: FeatureConfig) {
+  features = feats;
 }
 
 //#MARKER BYTM-Config buttons

+ 7 - 9
src/features/menu/menu_old.ts

@@ -1,5 +1,5 @@
 import { debounce } from "@sv443-network/userutils";
-import { defaultFeatures, getFeatures, saveFeatureConf, setDefaultFeatConf } from "../../config";
+import { defaultConfig, getFeatures, saveFeatures, setDefaultFeatures } from "../../config";
 import { scriptInfo } from "../../constants";
 import { featInfo } from "../index";
 import { FeatureConfig } from "../../types";
@@ -99,18 +99,18 @@ export async function addMenu() {
   featuresCont.style.overflowY = "auto";
 
   /** Gets called whenever the feature config is changed */
-  const confChanged = debounce(async (key: keyof typeof defaultFeatures, initialVal: number | boolean | Record<string, unknown>, newVal: number | boolean | Record<string, unknown>) => {
+  const confChanged = debounce(async (key: keyof typeof defaultConfig, initialVal: number | boolean | Record<string, unknown>, newVal: number | boolean | Record<string, unknown>) => {
     const fmt = (val: unknown) => typeof val === "object" ? JSON.stringify(val) : String(val);
     info(`Feature config changed at key '${key}', from value '${fmt(initialVal)}' to '${fmt(newVal)}'`);
 
-    const featConf = { ...await getFeatures() };
+    const featConf = { ...getFeatures() };
 
     featConf[key] = newVal as never;
 
-    await saveFeatureConf(featConf);
+    await saveFeatures(featConf);
   });
 
-  const features = await getFeatures();
+  const features = getFeatures();
 
   for(const key in features) {
     const ftInfo = featInfo[key as keyof typeof features];
@@ -267,10 +267,8 @@ export async function addMenu() {
   resetElem.title = "Click to reset all settings to their default value";
   resetElem.innerText = "Reset";
   resetElem.addEventListener("click", async () => {
-    if(confirm("Do you really want to reset all settings to their default value?\nThe page will automatically reload if you proceed.")) {
-      await setDefaultFeatConf();
-      location.reload();
-    }
+    if(confirm("Do you really want to reset all settings to their default value?\nAfterwards the page will need to be reloaded to apply changes."))
+      await setDefaultFeatures();
   });
 
   footerElem.appendChild(reloadElem);

+ 8 - 4
src/index.ts

@@ -1,5 +1,5 @@
 import { addGlobalStyle, initOnSelector, onSelector } from "@sv443-network/userutils";
-import { loadFeatureConf } from "./config";
+import { getFeatures, initConfig } from "./config";
 import { logLevel, scriptInfo } from "./constants";
 import { error, getDomain, log, setLogLevel } from "./utils";
 import { initSiteEvents } from "./events";
@@ -51,14 +51,18 @@ function preInit() {
 }
 
 async function init() {
-  await preInitLayout();
-
   try {
     document.addEventListener("DOMContentLoaded", onDomLoad);
   }
   catch(err) {
     error("General Error:", err);
   }
+  try {
+    preInitLayout(await initConfig());
+  }
+  catch(err) {
+    error("Error while initializing ConfigManager:", err);
+  }
 
   try {
     void ["TODO(v1.1):", initMenu];
@@ -76,7 +80,7 @@ async function onDomLoad() {
 
   initOnSelector();
 
-  const features = await loadFeatureConf();
+  const features = getFeatures();
 
   log(`Initializing features for domain "${domain}"...`);