From efd88c476896b416aab4e8dc32f18849b510bae7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bryan=20Gald=C3=A1mez?= Date: Mon, 16 Sep 2024 22:39:17 -0600 Subject: [PATCH 1/3] refactor(zenSettings): parse legacy settings using new format - replace ! format with json property disabledOn - added new default property - write defaults on theme install --- src/ZenThemesImporter.mjs | 145 ++++++++++++++++++++++++++++++++------ 1 file changed, 122 insertions(+), 23 deletions(-) diff --git a/src/ZenThemesImporter.mjs b/src/ZenThemesImporter.mjs index 337ae9c..3613015 100644 --- a/src/ZenThemesImporter.mjs +++ b/src/ZenThemesImporter.mjs @@ -8,6 +8,13 @@ const kZenStylesheetThemeHeader = ` const kenStylesheetFooter = ` /* End of Zen Themes */ `; + +const kZenOSToSmallName = { + WINNT: 'windows', + Darwin: 'macos', + Linux: 'linux', +}; + var gZenStylesheetManager = { async writeStylesheet(path, themes) { let content = kZenStylesheetThemeHeader; @@ -41,7 +48,20 @@ var gZenThemeImporter = new (class { try { window.SessionStore.promiseInitialized.then(async () => { this.insertStylesheet(); - await this.writeToDom(); + + const themesWithPreferences = await Promise.all( + Object.values(await this.getThemes()).map(async (theme) => { + const preferences = await this._getThemePreferences(theme); + + return { + name: theme.name, + enabled: theme.enabled, + preferences, + }; + }) + ); + + this.writeToDom(themesWithPreferences); }); console.info('ZenThemeImporter: Zen theme imported'); } catch (e) { @@ -78,6 +98,7 @@ var gZenThemeImporter = new (class { if (!(await IOUtils.exists(this.themesDataFile))) { await IOUtils.writeJSON(this.themesDataFile, {}); } + this._themes = await IOUtils.readJSON(this.themesDataFile); } return this._themes; @@ -111,8 +132,25 @@ var gZenThemeImporter = new (class { async updateStylesheet() { await this.removeStylesheet(); - await this.writeStylesheet(); - await this.writeToDom(); + + const themes = Object.values(await this.getThemes()); + await this.writeStylesheet(themes); + + const themesWithPreferences = await Promise.all( + themes.map(async (theme) => { + const preferences = await this._getThemePreferences(theme); + + return { + name: theme.name, + enabled: theme.enabled, + preferences, + }; + }) + ); + + this.setDefaults(themesWithPreferences); + this.writeToDom(themesWithPreferences); + await this.insertStylesheet(); } @@ -124,44 +162,103 @@ var gZenThemeImporter = new (class { return this.__browser; } + get currentOperatingSystem() { + let os = Services.appinfo.OS; + return kZenOSToSmallName[os]; + } + async _getThemePreferences(theme) { const themePath = PathUtils.join(this.getThemeFolder(theme), 'preferences.json'); if (!(await IOUtils.exists(themePath)) || !theme.preferences) { - return { preferences: [], isLegacyMode: false }; + return []; } - let preferences = await IOUtils.readJSON(themePath); + const preferences = await IOUtils.readJSON(themePath); - // skip transformation, we won't be writing old preferences to dom, all of them can only be checkboxes if (typeof preferences === 'object' && !Array.isArray(preferences)) { - return { preferences: [], areOldPreferences: true }; - } + console.warn( + `[ZenThemesImporter]: Warning, ${theme.name} uses legacy preferences, please migrate them to the new preferences style, as legacy preferences might be removed at a future release. More information at: https://docs.zen-browser.app/themes-store/themes-marketplace-preferences` + ); + const newThemePreferences = []; - return { preferences, areOldPreferences: false }; - } + for (let [entry, label] of Object.entries(preferences)) { + const [_, negation = '', os = '', property] = /(!?)(?:(macos|windows|linux):)?([A-z0-9-_.]+)/g.exec(entry); + const isNegation = negation === '!'; - async writeToDom() { - const browser = this._getBrowser(); + if ( + (isNegation && os === this.currentOperatingSystem) || + (os !== '' && os !== this.currentOperatingSystem && !isNegation) + ) { + continue; + } - for (const theme of Object.values(await this.getThemes())) { - const { preferences, areOldPreferences } = await this._getThemePreferences(theme); - - if (areOldPreferences) { - continue; + newThemePreferences.push({ + property, + label, + type: 'checkbox', + disabledOn: os !== '' ? [os] : [], + }); } - const filteredPreferences = preferences.filter(({ type }) => type !== 'checkbox'); - const sanitizedName = `theme-${theme.name?.replaceAll(/\s/g, '-')?.replaceAll(/[^A-z_-]+/g, '')}`; + return newThemePreferences; + } - if (!theme.enabled) { + return preferences.filter(({ disabledOn = [] }) => !disabledOn.includes(this.currentOperatingSystem)); + } + + setDefaults(themesWithPreferences) { + for (const { preferences } of themesWithPreferences) { + for (const { type, property, defaultValue } of preferences) { + if (defaultValue === undefined) { + continue; + } + + switch (type) { + case 'checkbox': { + const value = Services.prefs.getBoolPref(property, false); + if (typeof defaultValue !== 'boolean') { + console.log(`[ZenThemesImporter]: Warning, invalid data type received for expected type boolean, skipping.`); + continue; + } + + if (!value) { + Services.prefs.setBoolPref(property, defaultValue); + } + break; + } + + default: { + const value = Services.prefs.getStringPref(property, 'zen-property-no-saved'); + + if (typeof defaultValue !== 'string' && typeof defaultValue !== 'number') { + console.log(`[ZenThemesImporter]: Warning, invalid data type received (${typeof defaultValue}), skipping.`); + continue; + } + + if (value === 'zen-property-no-saved') { + Services.prefs.setStringPref(property, defaultValue.toString()); + } + } + } + } + } + } + + writeToDom(themesWithPreferences) { + const browser = this._getBrowser(); + + for (const { enabled, preferences, name } of themesWithPreferences) { + const sanitizedName = `theme-${name?.replaceAll(/\s/g, '-')?.replaceAll(/[^A-z_-]+/g, '')}`; + + if (!enabled) { const element = browser.document.getElementById(sanitizedName); if (element) { element.remove(); } - for (const { property } of filteredPreferences) { + for (const { property } of preferences.filter(({ type }) => type !== 'checkbox')) { const sanitizedProperty = property?.replaceAll(/\./g, '-'); if (document.querySelector(':root').style.hasProperty(`--${sanitizedProperty}`)) { @@ -211,13 +308,15 @@ var gZenThemeImporter = new (class { } } - async writeStylesheet() { + async writeStylesheet(themeList) { const themes = []; this._themes = null; - for (let theme of Object.values(await this.getThemes())) { + + for (let theme of themeList) { theme._chromeURL = this.getStylesheetURIForTheme(theme).spec; themes.push(theme); } + await gZenStylesheetManager.writeStylesheet(this.styleSheetPath, themes); } })(); From 8f26f88495ad0e18ea8b7c2da56237e98357dc80 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bryan=20Gald=C3=A1mez?= Date: Wed, 18 Sep 2024 00:44:22 -0600 Subject: [PATCH 2/3] fix(zenThemesImporter): enable themes by default on no enabled flag --- src/ZenThemesImporter.mjs | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/ZenThemesImporter.mjs b/src/ZenThemesImporter.mjs index 3613015..f305885 100644 --- a/src/ZenThemesImporter.mjs +++ b/src/ZenThemesImporter.mjs @@ -19,7 +19,7 @@ var gZenStylesheetManager = { async writeStylesheet(path, themes) { let content = kZenStylesheetThemeHeader; for (let theme of themes) { - if (!theme.enabled) { + if (theme.enabled !== undefined && !theme.enabled) { continue; } content += this.getThemeCSS(theme); @@ -208,7 +208,11 @@ var gZenThemeImporter = new (class { } setDefaults(themesWithPreferences) { - for (const { preferences } of themesWithPreferences) { + for (const { preferences, enabled } of themesWithPreferences) { + if (enabled !== undefined && !enabled) { + continue; + } + for (const { type, property, defaultValue } of preferences) { if (defaultValue === undefined) { continue; @@ -251,7 +255,7 @@ var gZenThemeImporter = new (class { for (const { enabled, preferences, name } of themesWithPreferences) { const sanitizedName = `theme-${name?.replaceAll(/\s/g, '-')?.replaceAll(/[^A-z_-]+/g, '')}`; - if (!enabled) { + if (enabled !== undefined && !enabled) { const element = browser.document.getElementById(sanitizedName); if (element) { From 36b93499d20b2b686168d0f2c90d20d0b6745a7e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bryan=20Gald=C3=A1mez?= Date: Wed, 18 Sep 2024 17:39:29 -0600 Subject: [PATCH 3/3] refactor(zenThemes): moved common utils to ZenThemesCommon --- src/ZenThemesCommon.mjs | 97 +++++++++++++++++++++++++++++++++++++++ src/ZenThemesImporter.mjs | 94 +++---------------------------------- 2 files changed, 103 insertions(+), 88 deletions(-) diff --git a/src/ZenThemesCommon.mjs b/src/ZenThemesCommon.mjs index e69de29..6e2b4ce 100644 --- a/src/ZenThemesCommon.mjs +++ b/src/ZenThemesCommon.mjs @@ -0,0 +1,97 @@ +var ZenThemesCommon = { + kZenOSToSmallName: { + WINNT: 'windows', + Darwin: 'macos', + Linux: 'linux', + }, + + kZenColors: ['#aac7ff', '#74d7cb', '#a0d490', '#dec663', '#ffb787', '#dec1b1', '#ffb1c0', '#ddbfc3', '#f6b0ea', '#d4bbff'], + + get currentOperatingSystem() { + let os = Services.appinfo.OS; + return this.kZenOSToSmallName[os]; + }, + + get themesRootPath() { + return PathUtils.join(PathUtils.profileDir, 'chrome', 'zen-themes'); + }, + + get themesDataFile() { + return PathUtils.join(PathUtils.profileDir, 'zen-themes.json'); + }, + + getThemeFolder(themeId) { + return PathUtils.join(this.themesRootPath, themeId); + }, + + getBrowser() { + if (!this.__browser) { + this.__browser = Services.wm.getMostRecentWindow('navigator:browser'); + } + + return this.__browser; + }, + + async getThemes() { + if (!this._themes) { + if (!(await IOUtils.exists(this.themesDataFile))) { + await IOUtils.writeJSON(this.themesDataFile, {}); + } + this._themes = await IOUtils.readJSON(this.themesDataFile); + } + return this._themes; + }, + + async getThemePreferences(theme) { + const themePath = PathUtils.join(this.themesRootPath, theme.id, 'preferences.json'); + if (!(await IOUtils.exists(themePath)) || !theme.preferences) { + return []; + } + + const preferences = await IOUtils.readJSON(themePath); + + // compat mode for old preferences, all of them can only be checkboxes + if (typeof preferences === 'object' && !Array.isArray(preferences)) { + console.warn( + `[ZenThemes]: Warning, ${theme.name} uses legacy preferences, please migrate them to the new preferences style, as legacy preferences might be removed at a future release. More information at: https://docs.zen-browser.app/themes-store/themes-marketplace-preferences` + ); + const newThemePreferences = []; + + for (let [entry, label] of Object.entries(preferences)) { + const [_, negation = '', os = '', property] = /(!?)(?:(macos|windows|linux):)?([A-z0-9-_.]+)/g.exec(entry); + const isNegation = negation === '!'; + + if ( + (isNegation && os === this.currentOperatingSystem) || + (os !== '' && os !== this.currentOperatingSystem && !isNegation) + ) { + continue; + } + + newThemePreferences.push({ + property, + label, + type: 'checkbox', + disabledOn: os !== '' ? [os] : [], + }); + } + + return newThemePreferences; + } + + return preferences.filter(({ disabledOn = [] }) => !disabledOn.includes(this.currentOperatingSystem)); + }, + + throttle(mainFunction, delay) { + let timerFlag = null; + + return (...args) => { + if (timerFlag === null) { + mainFunction(...args); + timerFlag = setTimeout(() => { + timerFlag = null; + }, delay); + } + }; + }, +}; diff --git a/src/ZenThemesImporter.mjs b/src/ZenThemesImporter.mjs index f305885..2e2d357 100644 --- a/src/ZenThemesImporter.mjs +++ b/src/ZenThemesImporter.mjs @@ -9,12 +9,6 @@ const kenStylesheetFooter = ` /* End of Zen Themes */ `; -const kZenOSToSmallName = { - WINNT: 'windows', - Darwin: 'macos', - Linux: 'linux', -}; - var gZenStylesheetManager = { async writeStylesheet(path, themes) { let content = kZenStylesheetThemeHeader; @@ -50,8 +44,8 @@ var gZenThemeImporter = new (class { this.insertStylesheet(); const themesWithPreferences = await Promise.all( - Object.values(await this.getThemes()).map(async (theme) => { - const preferences = await this._getThemePreferences(theme); + Object.values(await ZenThemesCommon.getThemes()).map(async (theme) => { + const preferences = await ZenThemesCommon.getThemePreferences(theme); return { name: theme.name, @@ -81,29 +75,6 @@ var gZenThemeImporter = new (class { return PathUtils.join(PathUtils.profileDir, 'chrome', 'zen-themes.css'); } - get themesRootPath() { - return PathUtils.join(PathUtils.profileDir, 'chrome', 'zen-themes'); - } - - get themesDataFile() { - return PathUtils.join(PathUtils.profileDir, 'zen-themes.json'); - } - - getThemeFolder(theme) { - return PathUtils.join(this.themesRootPath, theme.id); - } - - async getThemes() { - if (!this._themes) { - if (!(await IOUtils.exists(this.themesDataFile))) { - await IOUtils.writeJSON(this.themesDataFile, {}); - } - - this._themes = await IOUtils.readJSON(this.themesDataFile); - } - return this._themes; - } - async rebuildThemeStylesheet() { this._themes = null; await this.updateStylesheet(); @@ -117,7 +88,7 @@ var gZenThemeImporter = new (class { } getStylesheetURIForTheme(theme) { - return Services.io.newFileURI(new FileUtils.File(PathUtils.join(this.getThemeFolder(theme), 'chrome.css'))); + return Services.io.newFileURI(new FileUtils.File(PathUtils.join(ZenThemesCommon.getThemeFolder(theme.id), 'chrome.css'))); } async insertStylesheet() { @@ -133,12 +104,12 @@ var gZenThemeImporter = new (class { async updateStylesheet() { await this.removeStylesheet(); - const themes = Object.values(await this.getThemes()); + const themes = Object.values(await ZenThemesCommon.getThemes()); await this.writeStylesheet(themes); const themesWithPreferences = await Promise.all( themes.map(async (theme) => { - const preferences = await this._getThemePreferences(theme); + const preferences = await ZenThemesCommon.getThemePreferences(theme); return { name: theme.name, @@ -154,59 +125,6 @@ var gZenThemeImporter = new (class { await this.insertStylesheet(); } - _getBrowser() { - if (!this.__browser) { - this.__browser = Services.wm.getMostRecentWindow('navigator:browser'); - } - - return this.__browser; - } - - get currentOperatingSystem() { - let os = Services.appinfo.OS; - return kZenOSToSmallName[os]; - } - - async _getThemePreferences(theme) { - const themePath = PathUtils.join(this.getThemeFolder(theme), 'preferences.json'); - - if (!(await IOUtils.exists(themePath)) || !theme.preferences) { - return []; - } - - const preferences = await IOUtils.readJSON(themePath); - - if (typeof preferences === 'object' && !Array.isArray(preferences)) { - console.warn( - `[ZenThemesImporter]: Warning, ${theme.name} uses legacy preferences, please migrate them to the new preferences style, as legacy preferences might be removed at a future release. More information at: https://docs.zen-browser.app/themes-store/themes-marketplace-preferences` - ); - const newThemePreferences = []; - - for (let [entry, label] of Object.entries(preferences)) { - const [_, negation = '', os = '', property] = /(!?)(?:(macos|windows|linux):)?([A-z0-9-_.]+)/g.exec(entry); - const isNegation = negation === '!'; - - if ( - (isNegation && os === this.currentOperatingSystem) || - (os !== '' && os !== this.currentOperatingSystem && !isNegation) - ) { - continue; - } - - newThemePreferences.push({ - property, - label, - type: 'checkbox', - disabledOn: os !== '' ? [os] : [], - }); - } - - return newThemePreferences; - } - - return preferences.filter(({ disabledOn = [] }) => !disabledOn.includes(this.currentOperatingSystem)); - } - setDefaults(themesWithPreferences) { for (const { preferences, enabled } of themesWithPreferences) { if (enabled !== undefined && !enabled) { @@ -250,7 +168,7 @@ var gZenThemeImporter = new (class { } writeToDom(themesWithPreferences) { - const browser = this._getBrowser(); + const browser = ZenThemesCommon.getBrowser(); for (const { enabled, preferences, name } of themesWithPreferences) { const sanitizedName = `theme-${name?.replaceAll(/\s/g, '-')?.replaceAll(/[^A-z_-]+/g, '')}`;