Browse Source

refactor: deduplicate/simplify hookSetting() usage

tophf 5 years ago
parent
commit
2837622bbf

+ 16 - 5
src/common/hook-setting.js

@@ -13,12 +13,23 @@ options.hook((data) => {
   });
 });
 
-export default function hook(key, update) {
-  let list = hooks[key];
-  if (!list) {
-    list = [];
-    hooks[key] = list;
+/**
+ * When an option is updated elsewhere (or when a yet unresolved options.ready will be fired),
+ * calls the specified `update` function or assigns the specified `prop` in `target` object.
+ * Also, when the latter mode is used, option.get() is called explicitly right away,
+ * but only if options.ready is resolved or `transform` function is specified.
+ * @param {string} key - option name
+ * @param {function(value) | { target, prop, transform }} update - either a function or the config object
+ * @return {function}
+ */
+export default function hookSetting(key, update) {
+  const { target } = update;
+  if (target) {
+    const { prop, transform } = update;
+    update = value => { target[prop] = transform ? transform(value) : value; };
+    if (transform || options.ready.indeed) update(options.get(key));
   }
+  const list = hooks[key] || (hooks[key] = []);
   list.push(update);
   return () => {
     const i = list.indexOf(update);

+ 1 - 0
src/common/options.js

@@ -6,6 +6,7 @@ let options = {};
 const hooks = initHooks();
 const ready = sendCmd('GetAllOptions', null, { retry: true })
 .then((data) => {
+  ready.indeed = true; // a workaround for inability to query native Promise state
   options = data;
   if (data) hooks.fire(data);
 });

+ 4 - 8
src/common/ui/setting-check.vue

@@ -34,14 +34,10 @@ export default {
       this.$emit('change', value);
     },
   },
-  created() {
-    options.ready.then(() => {
-      this.value = options.get(this.name);
-      this.revoke = hookSetting(this.name, (value) => {
-        this.value = value;
-      });
-      this.$watch('value', this.onChange);
-    });
+  async created() {
+    await options.ready;
+    this.revoke = hookSetting(this.name, { target: this, prop: 'value' });
+    this.$watch('value', this.onChange);
   },
   beforeDestroy() {
     if (this.revoke) this.revoke();

+ 2 - 5
src/common/ui/setting-text.vue

@@ -32,14 +32,11 @@ export default {
     };
   },
   created() {
-    const handle = this.json
+    const transform = this.json
       ? (value => JSON.stringify(value, null, '  '))
       // XXX compatible with old data format
       : (value => (Array.isArray(value) ? value.join('\n') : value || ''));
-    this.value = handle(options.get(this.name));
-    this.revoke = hookSetting(this.name, (value) => {
-      this.value = handle(value);
-    });
+    this.revoke = hookSetting(this.name, { target: this, prop: 'value', transform });
     if (this.json) this.$watch('value', this.parseJson);
   },
   beforeDestroy() {

+ 17 - 25
src/options/views/tab-installed.vue

@@ -152,21 +152,24 @@ const filterOptions = {
     },
   },
 };
+const filtersSort = {
+  value: null,
+  title: null,
+};
 const filters = {
   searchScope: null,
   showEnabledFirst: null,
-  sort: {
-    value: null,
-    title: null,
-    set(value) {
-      const option = filterOptions.sort[value];
-      if (option) {
-        filters.sort.value = value;
-        filters.sort.title = option.title;
-      } else {
-        filters.sort.set(Object.keys(filterOptions.sort)[0]);
-      }
-    },
+  get sort() {
+    return filtersSort;
+  },
+  set sort(value) {
+    const option = filterOptions.sort[value];
+    if (option) {
+      filtersSort.value = value;
+      filtersSort.title = option.title;
+    } else {
+      filters.sort = Object.keys(filterOptions.sort)[0];
+    }
   },
 };
 const combinedCompare = cmpFunc => (
@@ -174,19 +177,8 @@ const combinedCompare = cmpFunc => (
     ? ((a, b) => b.config.enabled - a.config.enabled || cmpFunc(a, b))
     : cmpFunc
 );
-hookSetting('filters.searchScope', (value) => {
-  filters.searchScope = value;
-});
-hookSetting('filters.showEnabledFirst', (value) => {
-  filters.showEnabledFirst = value;
-});
-hookSetting('filters.sort', (value) => {
-  filters.sort.set(value);
-});
-options.ready.then(() => {
-  filters.searchScope = options.get('filters.searchScope');
-  filters.sort.set(options.get('filters.sort'));
-  filters.showEnabledFirst = options.get('filters.showEnabledFirst');
+Object.keys(filters).forEach(prop => {
+  hookSetting(`filters.${prop}`, { target: filters, prop });
 });
 
 const MAX_BATCH_DURATION = 100;

+ 6 - 10
src/options/views/tab-settings/index.vue

@@ -168,17 +168,13 @@ export default {
       };
     },
   },
-  created() {
+  async created() {
     this.revokers = [];
-    options.ready.then(() => {
-      items.forEach((item) => {
-        const { name, normalize } = item;
-        settings[name] = normalize(options.get(name));
-        this.revokers.push(hookSetting(name, (value) => {
-          settings[name] = value;
-        }));
-        this.$watch(() => settings[name], debounce(this.getUpdater(item), 300));
-      });
+    await options.ready;
+    items.forEach((item) => {
+      const { name, normalize: transform } = item;
+      this.revokers.push(hookSetting(name, { target: settings, prop: name, transform }));
+      this.$watch(() => settings[name], debounce(this.getUpdater(item), 300));
     });
   },
   beforeDestroy() {