From 04599adfccce298a88da633c62edfe0ed910b424 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bryan=20Gald=C3=A1mez?= Date: Mon, 16 Sep 2024 22:38:54 -0600 Subject: [PATCH 1/4] refactor(zenSettings): parse legacy settings using new format - replace ! format with json property disabledOn - added new default property --- .../components/preferences/zen-settings.js | 74 ++++++++----------- 1 file changed, 30 insertions(+), 44 deletions(-) diff --git a/src/browser/components/preferences/zen-settings.js b/src/browser/components/preferences/zen-settings.js index 40fd4305b..7daef8e68 100644 --- a/src/browser/components/preferences/zen-settings.js +++ b/src/browser/components/preferences/zen-settings.js @@ -88,11 +88,12 @@ var gZenMarketplaceManager = { }, async removeTheme(themeId) { + console.info('[ZenThemeMarketplaceParent:settings]: Removing theme ', themePath); + const themePath = PathUtils.join(this.themesRootPath, themeId); - console.info('ZenThemeMarketplaceParent(settings): Removing theme ', themePath); await IOUtils.remove(themePath, { recursive: true, ignoreAbsent: true }); - let themes = await this._getThemes(); + const themes = await this._getThemes(); delete themes[themeId]; await IOUtils.writeJSON(this.themesDataFile, themes); @@ -136,58 +137,44 @@ var gZenMarketplaceManager = { return kZenOSToSmallName[os]; }, - _getValidPreferences(preferences) { - for (let entry of preferences) { - const key = entry.property; - // [!][os:]key - let restOfPreferences = key; - let isNegation = false; - if (key.startsWith('!')) { - isNegation = true; - restOfPreferences = key.slice(1); - } - let os = ''; - if (restOfPreferences.includes(':')) { - [os, restOfPreferences] = restOfPreferences.split(':'); - } - if (isNegation && os === this.currentOperatingSystem) { - delete preferences[key]; - } else if (os && os !== this.currentOperatingSystem && !isNegation) { - delete preferences[key]; - } else { - // Change the key to contain only the rest of the preferences. - preferences[restOfPreferences] = preferences[key]; - if (key !== restOfPreferences) { - delete preferences[key]; - } - } - } - return preferences; - }, - async _getThemePreferences(theme) { const themePath = PathUtils.join(this.themesRootPath, theme.id, 'preferences.json'); if (!(await IOUtils.exists(themePath)) || !theme.preferences) { return []; } - let themePreferences = await IOUtils.readJSON(themePath); + const preferences = await IOUtils.readJSON(themePath); // compat mode for old preferences, all of them can only be checkboxes - if (typeof themePreferences === 'object' && !Array.isArray(themePreferences)) { + if (typeof preferences === 'object' && !Array.isArray(preferences)) { console.warn( - `[ZenThemeMarketplaceManager]: 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: ` + `[ZenThemeMarketplaceManager]: 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` ); - themePreferences = Object.entries(themePreferences).map(([property, label]) => { - return { + 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 this._getValidPreferences(themePreferences); + return preferences.filter(({ disabledOn = [] }) => !disabledOn.includes(this.currentOperatingSystem)); }, _getBrowser() { @@ -306,9 +293,8 @@ var gZenMarketplaceManager = { if (!confirm('Are you sure you want to remove this theme?')) { return; } - const target = event.target; - const themeId = target.getAttribute('zen-theme-id'); - await this.removeTheme(themeId); + + await this.removeTheme(event.target.getAttribute('zen-theme-id')); }); fragment.querySelector('.zenThemeMarketplaceItemConfigureButton').addEventListener('click', () => { dialog.showModal(); @@ -362,7 +348,7 @@ var gZenMarketplaceManager = { if (!['string', 'number'].includes(valueType)) { console.log( - `ZenThemeMarketplaceParent(settings): Warning, invalid data type received (${valueType}), skipping.` + `[ZenThemeMarketplaceParent:settings]: Warning, invalid data type received (${valueType}), skipping.` ); continue; } @@ -486,7 +472,7 @@ var gZenMarketplaceManager = { default: console.log( - `ZenThemeMarketplaceParent(settings): Warning, unknown preference type received (${type}), skipping.` + `[ZenThemeMarketplaceParent:settings]: Warning, unknown preference type received (${type}), skipping.` ); continue; } From 438b232edeba543cb769cec60cafc09f6d6bb752 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bryan=20Gald=C3=A1mez?= Date: Wed, 18 Sep 2024 00:46:29 -0600 Subject: [PATCH 2/4] fix(zen-settings): various fixes in mods rendering - renamed some let to const - conditionally render config button - disable config button appearing on enable/disable on mods without preferences - change mozToggle title based on state --- .../components/preferences/zen-settings.js | 46 ++++++++++++------- .../shared/preferences/zen-preferences.css | 4 ++ 2 files changed, 34 insertions(+), 16 deletions(-) diff --git a/src/browser/components/preferences/zen-settings.js b/src/browser/components/preferences/zen-settings.js index 7daef8e68..fcce3fc11 100644 --- a/src/browser/components/preferences/zen-settings.js +++ b/src/browser/components/preferences/zen-settings.js @@ -213,8 +213,9 @@ var gZenMarketplaceManager = { const themeList = document.createElement('div'); - for (let theme of Object.values(themes)) { + for (const theme of Object.values(themes)) { const sanitizedName = `theme-${theme.name?.replaceAll(/\s/g, '-')?.replaceAll(/[^A-z_-]+/g, '')}`; + const isThemeEnabled = theme.enabled === undefined || theme.enabled; const fragment = window.MozXULElement.parseXULToFragment(` @@ -225,7 +226,7 @@ var gZenMarketplaceManager = { - + ${theme.preferences ? `` : ''} @@ -255,8 +256,8 @@ var gZenMarketplaceManager = { contentDiv.className = 'zenThemeMarketplaceItemPreferenceDialogContent'; mozToggle.className = 'zenThemeMarketplaceItemPreferenceToggle'; - mozToggle.pressed = theme.enabled; - mozToggle.title = theme.enabled ? 'Disable theme' : 'Enable theme'; + mozToggle.pressed = isThemeEnabled; + mozToggle.title = isThemeEnabled ? 'Disable theme' : 'Enable theme'; baseHeader.appendChild(mozToggle); @@ -280,10 +281,20 @@ var gZenMarketplaceManager = { if (!event.target.hasAttribute('pressed')) { await this.disableTheme(themeId); - document.getElementById(`zenThemeMarketplaceItemConfigureButton-${sanitizedName}`).setAttribute('hidden', true); + + mozToggle.title = 'Enable theme'; + + if (theme.preferences) { + document.getElementById(`zenThemeMarketplaceItemConfigureButton-${sanitizedName}`).setAttribute('hidden', true); + } } else { await this.enableTheme(themeId); - document.getElementById(`zenThemeMarketplaceItemConfigureButton-${sanitizedName}`).removeAttribute('hidden'); + + mozToggle.title = 'Disable theme'; + + if (theme.preferences) { + document.getElementById(`zenThemeMarketplaceItemConfigureButton-${sanitizedName}`).removeAttribute('hidden'); + } } }); @@ -296,12 +307,15 @@ var gZenMarketplaceManager = { await this.removeTheme(event.target.getAttribute('zen-theme-id')); }); - fragment.querySelector('.zenThemeMarketplaceItemConfigureButton').addEventListener('click', () => { - dialog.showModal(); - }); - if (theme.enabled && theme.preferences) { - fragment.querySelector('.zenThemeMarketplaceItemConfigureButton').removeAttribute('hidden'); + if (theme.preferences) { + fragment.querySelector('.zenThemeMarketplaceItemConfigureButton').addEventListener('click', () => { + dialog.showModal(); + }); + + if (isThemeEnabled) { + fragment.querySelector('.zenThemeMarketplaceItemConfigureButton').removeAttribute('hidden'); + } } const preferences = await this._getThemePreferences(theme); @@ -311,7 +325,7 @@ var gZenMarketplaceManager = { preferencesWrapper.setAttribute('flex', '1'); - for (let entry of preferences) { + for (const entry of preferences) { const { property, label, type } = entry; switch (type) { @@ -341,7 +355,7 @@ var gZenMarketplaceManager = { menupopup.appendChild(defaultItem); - for (let option of options) { + for (const option of options) { const { label, value } = option; const valueType = typeof value; @@ -409,9 +423,9 @@ var gZenMarketplaceManager = { } checkbox.querySelector('.zenThemeMarketplaceItemPreferenceCheckbox').addEventListener('click', (event) => { - let target = event.target.closest('.zenThemeMarketplaceItemPreferenceCheckbox'); - let key = target.getAttribute('zen-pref'); - let checked = target.hasAttribute('checked'); + const target = event.target.closest('.zenThemeMarketplaceItemPreferenceCheckbox'); + const key = target.getAttribute('zen-pref'); + const checked = target.hasAttribute('checked'); if (!checked) { target.removeAttribute('checked'); diff --git a/src/browser/themes/shared/preferences/zen-preferences.css b/src/browser/themes/shared/preferences/zen-preferences.css index ad37698d0..ba7614262 100644 --- a/src/browser/themes/shared/preferences/zen-preferences.css +++ b/src/browser/themes/shared/preferences/zen-preferences.css @@ -480,6 +480,10 @@ groupbox h2 { flex-direction: row; } +.zenThemeMarketplaceItemPreferenceToggle { + align-self: start; +} + #zenThemeMarketplaceItemContentHeader { display: flex; flex-direction: row; From ed82f7d459a54b0f740fcbbb5c8420a61d44b020 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bryan=20Gald=C3=A1mez?= Date: Wed, 18 Sep 2024 17:39:28 -0600 Subject: [PATCH 3/4] refactor(zen-settings): moved common utils to ZenThemesCommon --- src/browser/base/content/zen-assets.inc.xhtml | 3 +- .../components/preferences/zen-settings.js | 134 +++--------------- 2 files changed, 18 insertions(+), 119 deletions(-) diff --git a/src/browser/base/content/zen-assets.inc.xhtml b/src/browser/base/content/zen-assets.inc.xhtml index 88e141bc5..addf290d8 100644 --- a/src/browser/base/content/zen-assets.inc.xhtml +++ b/src/browser/base/content/zen-assets.inc.xhtml @@ -26,6 +26,7 @@ \ No newline at end of file + diff --git a/src/browser/components/preferences/zen-settings.js b/src/browser/components/preferences/zen-settings.js index d7583e4e2..6c4dc15dc 100644 --- a/src/browser/components/preferences/zen-settings.js +++ b/src/browser/components/preferences/zen-settings.js @@ -2,25 +2,6 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -const kZenColors = [ - '#aac7ff', - '#74d7cb', - '#a0d490', - '#dec663', - '#ffb787', - '#dec1b1', - '#ffb1c0', - '#ddbfc3', - '#f6b0ea', - '#d4bbff', -]; - -const kZenOSToSmallName = { - WINNT: 'windows', - Darwin: 'macos', - Linux: 'linux', -}; - var gZenMarketplaceManager = { init() { const checkForUpdates = document.getElementById('zenThemeMarketplaceCheckForUpdates'); @@ -79,125 +60,42 @@ var gZenMarketplaceManager = { return document.getElementById('zenThemeMarketplaceList'); }, - get themesDataFile() { - return PathUtils.join(PathUtils.profileDir, 'zen-themes.json'); - }, - - get themesRootPath() { - return PathUtils.join(PathUtils.profileDir, 'chrome', 'zen-themes'); - }, - async removeTheme(themeId) { - console.info('[ZenThemeMarketplaceParent:settings]: Removing theme ', themePath); + const themePath = ZenThemesCommon.getThemeFolder(themeId); + + console.info(`[ZenThemeMarketplaceParent:settings]: Removing theme ${themePath}`); - const themePath = PathUtils.join(this.themesRootPath, themeId); await IOUtils.remove(themePath, { recursive: true, ignoreAbsent: true }); - const themes = await this._getThemes(); + const themes = await ZenThemesCommon.getThemes(); delete themes[themeId]; - await IOUtils.writeJSON(this.themesDataFile, themes); + await IOUtils.writeJSON(ZenThemesCommon.themesDataFile, themes); this.triggerThemeUpdate(); }, async disableTheme(themeId) { - const themes = await this._getThemes(); + const themes = await ZenThemesCommon.getThemes(); const theme = themes[themeId]; theme.enabled = false; - await IOUtils.writeJSON(this.themesDataFile, themes); + await IOUtils.writeJSON(ZenThemesCommon.themesDataFile, themes); this._doNotRebuildThemesList = true; this.triggerThemeUpdate(); }, async enableTheme(themeId) { - const themes = await this._getThemes(); + const themes = await ZenThemesCommon.getThemes(); const theme = themes[themeId]; theme.enabled = true; - await IOUtils.writeJSON(this.themesDataFile, themes); + await IOUtils.writeJSON(ZenThemesCommon.themesDataFile, themes); this._doNotRebuildThemesList = true; this.triggerThemeUpdate(); }, - 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; - }, - - get currentOperatingSystem() { - let os = Services.appinfo.OS; - return kZenOSToSmallName[os]; - }, - - 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( - `[ZenThemeMarketplaceManager]: 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)); - }, - - _getBrowser() { - if (!this.__browser) { - this.__browser = Services.wm.getMostRecentWindow('navigator:browser'); - } - - return this.__browser; - }, - - __throttle(mainFunction, delay) { - let timerFlag = null; - - return (...args) => { - if (timerFlag === null) { - mainFunction(...args); - timerFlag = setTimeout(() => { - timerFlag = null; - }, delay); - } - }; - }, - async _buildThemesList() { if (!this.themesList) return; if (this._doNotRebuildThemesList) { @@ -205,11 +103,11 @@ var gZenMarketplaceManager = { return; } - console.log('ZenThemeMarketplaceParent(settings): Building themes list'); + console.log('[ZenThemeMarketplaceParent:settings]: Building themes list'); - let themes = await this._getThemes(); + let themes = await ZenThemesCommon.getThemes(); - const browser = this._getBrowser(); + const browser = ZenThemesCommon.getBrowser(); const themeList = document.createElement('div'); @@ -318,7 +216,7 @@ var gZenMarketplaceManager = { } } - const preferences = await this._getThemePreferences(theme); + const preferences = await ZenThemesCommon.getThemePreferences(theme); if (preferences.length > 0) { const preferencesWrapper = document.createXULElement('vbox'); @@ -457,7 +355,7 @@ var gZenMarketplaceManager = { input.addEventListener( 'input', - this.__throttle((event) => { + ZenThemesCommon.throttle((event) => { const value = event.target.value; Services.prefs.setStringPref(property, value); @@ -608,7 +506,7 @@ var gZenLooksAndFeel = { _initializeColorPicker(accentColor) { let elem = document.getElementById('zenLooksAndFeelColorOptions'); elem.innerHTML = ''; - for (let color of kZenColors) { + for (let color of ZenThemesCommon.kZenColors) { let colorElemParen = document.createElement('div'); let colorElem = document.createElement('div'); colorElemParen.classList.add('zenLooksAndFeelColorOptionParen'); @@ -631,7 +529,7 @@ var gZenLooksAndFeel = { }, _getInitialAccentColor() { - return Services.prefs.getStringPref('zen.theme.accent-color', kZenColors[0]); + return Services.prefs.getStringPref('zen.theme.accent-color', ZenThemesCommon.kZenColors[0]); }, }; From bd7108e7df27a89980a302c9f42d527d35b7856d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bryan=20Gald=C3=A1mez?= Date: Wed, 18 Sep 2024 20:50:12 -0600 Subject: [PATCH 4/4] feature(zen-settings): localize hardcoded content --- .../components/preferences/zen-settings.js | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/src/browser/components/preferences/zen-settings.js b/src/browser/components/preferences/zen-settings.js index 6c4dc15dc..8a7f4d41c 100644 --- a/src/browser/components/preferences/zen-settings.js +++ b/src/browser/components/preferences/zen-settings.js @@ -146,16 +146,21 @@ var gZenMarketplaceManager = { mainDialogDiv.className = 'zenThemeMarketplaceItemPreferenceDialog'; headerDiv.className = 'zenThemeMarketplaceItemPreferenceDialogTopBar'; headerTitle.textContent = themeName; - headerTitle.title = `CSS Selector: ${sanitizedName}`; + browser.document.l10n.setAttributes(headerTitle, 'zen-theme-marketplace-theme-header-title', { + name: sanitizedName, + }); headerTitle.className = 'zenThemeMarketplaceItemTitle'; closeButton.id = `${sanitizedName}-modal-close`; - closeButton.textContent = 'Close'; + browser.document.l10n.setAttributes(closeButton, 'zen-theme-marketplace-close-modal'); contentDiv.id = `${sanitizedName}-preferences-content`; contentDiv.className = 'zenThemeMarketplaceItemPreferenceDialogContent'; mozToggle.className = 'zenThemeMarketplaceItemPreferenceToggle'; mozToggle.pressed = isThemeEnabled; - mozToggle.title = isThemeEnabled ? 'Disable theme' : 'Enable theme'; + browser.document.l10n.setAttributes( + mozToggle, + `zen-theme-marketplace-toggle-${isThemeEnabled ? 'enabled' : 'disabled'}-button` + ); baseHeader.appendChild(mozToggle); @@ -180,7 +185,7 @@ var gZenMarketplaceManager = { if (!event.target.hasAttribute('pressed')) { await this.disableTheme(themeId); - mozToggle.title = 'Enable theme'; + browser.document.l10n.setAttributes(mozToggle, 'zen-theme-marketplace-toggle-disabled-button'); if (theme.preferences) { document.getElementById(`zenThemeMarketplaceItemConfigureButton-${sanitizedName}`).setAttribute('hidden', true); @@ -188,7 +193,7 @@ var gZenMarketplaceManager = { } else { await this.enableTheme(themeId); - mozToggle.title = 'Disable theme'; + browser.document.l10n.setAttributes(mozToggle, 'zen-theme-marketplace-toggle-enabled-button'); if (theme.preferences) { document.getElementById(`zenThemeMarketplaceItemConfigureButton-${sanitizedName}`).removeAttribute('hidden'); @@ -199,7 +204,9 @@ var gZenMarketplaceManager = { fragment.querySelector('.zenThemeMarketplaceItemTitle').textContent = themeName; fragment.querySelector('.zenThemeMarketplaceItemDescription').textContent = theme.description; fragment.querySelector('.zenThemeMarketplaceItemUninstallButton').addEventListener('click', async (event) => { - if (!confirm('Are you sure you want to remove this theme?')) { + const [msg] = await document.l10n.formatValues([{ id: 'zen-theme-marketplace-remove-confirmation' }]); + + if (!confirm(msg)) { return; }