From 67d3b57eb13eb8b4abe8fc0e3325927c12572d6f Mon Sep 17 00:00:00 2001 From: KoloMl Date: Wed, 7 Aug 2024 03:26:06 +0400 Subject: [PATCH] Implemented base class for cached settings storages --- src/lib/components/MaintenancePopup.js | 6 +- src/lib/extension/base/CacheableSettings.js | 79 +++++++++++++++++++ .../extension/settings/MaintenanceSettings.js | 56 ++++--------- src/stores/maintenance-profiles-store.js | 12 +-- 4 files changed, 103 insertions(+), 50 deletions(-) create mode 100644 src/lib/extension/base/CacheableSettings.js diff --git a/src/lib/components/MaintenancePopup.js b/src/lib/components/MaintenancePopup.js index 1820366..bb967e5 100644 --- a/src/lib/components/MaintenancePopup.js +++ b/src/lib/components/MaintenancePopup.js @@ -272,12 +272,12 @@ export class MaintenancePopup extends BaseComponent { } }); - const unsubscribeFromMaintenanceSettings = MaintenanceSettings.subscribe(settings => { - if (settings.activeProfileId === lastActiveProfileId) { + const unsubscribeFromMaintenanceSettings = this.#maintenanceSettings.subscribe(settings => { + if (settings.activeProfile === lastActiveProfileId) { return; } - lastActiveProfileId = settings.activeProfileId; + lastActiveProfileId = settings.activeProfile; this.#maintenanceSettings .resolveActiveProfileAsObject() diff --git a/src/lib/extension/base/CacheableSettings.js b/src/lib/extension/base/CacheableSettings.js new file mode 100644 index 0000000..6627297 --- /dev/null +++ b/src/lib/extension/base/CacheableSettings.js @@ -0,0 +1,79 @@ +import ConfigurationController from "$lib/extension/ConfigurationController.js"; + +export default class CacheableSettings { + /** @type {ConfigurationController} */ + #controller; + /** @type {Map} */ + #cachedValues = new Map(); + /** @type {function[]} */ + #disposables = []; + + constructor(settingsNamespace) { + this.#controller = new ConfigurationController(settingsNamespace); + + this.#disposables.push( + this.#controller.subscribeToChanges(settings => { + for (const key of Object.keys(settings)) { + this.#cachedValues.set(key, settings[key]); + } + }) + ); + } + + /** + * @template SettingType + * @param {string} settingName + * @param {SettingType} defaultValue + * @return {Promise} + * @protected + */ + async _resolveSetting(settingName, defaultValue) { + if (this.#cachedValues.has(settingName)) { + return this.#cachedValues.get(settingName); + } + + const settingValue = await this.#controller.readSetting(settingName, defaultValue); + + this.#cachedValues.set(settingName, settingValue); + + return settingValue; + } + + /** + * @param {string} settingName Name of the setting to write. + * @param {*} value Value to pass. + * @param {boolean} [force=false] Ignore the cache and force the update. + * @return {Promise} + * @protected + */ + async _writeSetting(settingName, value, force = false) { + if ( + !force + && this.#cachedValues.has(settingName) + && this.#cachedValues.get(settingName) === value + ) { + return; + } + + return this.#controller.writeSetting(settingName, value); + } + + /** + * Subscribe to the changes made to the storage. + * @param {function(Object): void} callback Callback which will receive list of settings. + * @return {function(): void} Unsubscribe function. + */ + subscribe(callback) { + const unsubscribeCallback = this.#controller.subscribeToChanges(callback); + + this.#disposables.push(unsubscribeCallback); + + return unsubscribeCallback; + } + + dispose() { + for (let disposeCallback of this.#disposables) { + disposeCallback(); + } + } +} diff --git a/src/lib/extension/settings/MaintenanceSettings.js b/src/lib/extension/settings/MaintenanceSettings.js index a7f0c5c..adee2ef 100644 --- a/src/lib/extension/settings/MaintenanceSettings.js +++ b/src/lib/extension/settings/MaintenanceSettings.js @@ -1,21 +1,10 @@ import ConfigurationController from "$lib/extension/ConfigurationController.js"; import MaintenanceProfile from "$lib/extension/entities/MaintenanceProfile.js"; +import CacheableSettings from "$lib/extension/base/CacheableSettings.js"; -export default class MaintenanceSettings { - #isInitialized = false; - #activeProfileId = null; - +export default class MaintenanceSettings extends CacheableSettings { constructor() { - void this.#initializeSettings(); - } - - async #initializeSettings() { - MaintenanceSettings.#controller.subscribeToChanges(settings => { - this.#activeProfileId = settings.activeProfile || null; - }); - - this.#activeProfileId = await MaintenanceSettings.#controller.readSetting("activeProfile", null); - this.#isInitialized = true; + super("maintenance"); } /** @@ -24,18 +13,7 @@ export default class MaintenanceSettings { * @return {Promise} */ async resolveActiveProfileId() { - if (!this.#isInitialized && !this.#activeProfileId) { - this.#activeProfileId = await MaintenanceSettings.#controller.readSetting( - "activeProfile", - null - ); - } - - if (!this.#activeProfileId) { - return null; - } - - return this.#activeProfileId; + return this._resolveSetting("activeProfile", null); } /** @@ -59,31 +37,27 @@ export default class MaintenanceSettings { * @return {Promise} */ async setActiveProfileId(profileId) { - this.#activeProfileId = profileId; - - await MaintenanceSettings.#controller.writeSetting("activeProfile", profileId); + await this._writeSetting("activeProfile", profileId); } - /** - * Controller for interaction with the settings stored in the extension's storage. - * - * @type {ConfigurationController} - */ - static #controller = new ConfigurationController("maintenance"); - /** * Subscribe to the changes in the maintenance-related settings. * - * @param {function({activeProfileId: string|null}): void} callback Callback to call when the settings change. The new settings are - * passed as an argument. + * @param {function(MaintenanceSettingsObject): void} callback Callback to call when the settings change. The new + * settings are passed as an argument. * * @return {function(): void} Unsubscribe function. */ - static subscribe(callback) { - return MaintenanceSettings.#controller.subscribeToChanges(settings => { + subscribe(callback) { + return super.subscribe(settings => { callback({ - activeProfileId: settings.activeProfile || null, + activeProfile: settings.activeProfile || null, }); }); } } + +/** + * @typedef {Object} MaintenanceSettingsObject + * @property {string|null} activeProfile + */ diff --git a/src/stores/maintenance-profiles-store.js b/src/stores/maintenance-profiles-store.js index 5b00d04..ffc0db1 100644 --- a/src/stores/maintenance-profiles-store.js +++ b/src/stores/maintenance-profiles-store.js @@ -30,17 +30,17 @@ maintenanceSettings.resolveActiveProfileId().then(activeProfileId => { activeProfileStore.set(activeProfileId); }); -MaintenanceSettings.subscribe(settings => { - activeProfileStore.set(settings.activeProfileId || null); +maintenanceSettings.subscribe(settings => { + activeProfileStore.set(settings.activeProfile || null); }); +/** + * Active profile ID stored locally. Used to reset active profile once the existing profile was removed. + * @type {string|null} + */ let lastActiveProfileId = null; activeProfileStore.subscribe(profileId => { - if (profileId === lastActiveProfileId) { - return; - } - lastActiveProfileId = profileId; void maintenanceSettings.setActiveProfileId(profileId);