commit c0fdda3a1e0a663e96f403e1db42d06f2d0689cf parent 437712a9e466460933a4d854eacb4d2ec87d5053 Author: Reem H <42309026+reemhamz@users.noreply.github.com> Date: Thu, 11 Dec 2025 22:52:38 +0000 Bug 2003204 - Open customization panel from about:preferences#home. r=home-newtab-reviewers,fluent-reviewers,tgiles,npypchenko,desktop-theme-reviewers,bolsson Differential Revision: https://phabricator.services.mozilla.com/D274525 Diffstat:
11 files changed, 218 insertions(+), 27 deletions(-)
diff --git a/browser/components/preferences/home.js b/browser/components/preferences/home.js @@ -89,6 +89,22 @@ if (Services.prefs.getBoolPref("browser.settings-redesign.enabled")) { type: "bool", }, { + id: "browser.newtabpage.activity-stream.discoverystream.sections.enabled", + type: "bool", + }, + { + id: "browser.newtabpage.activity-stream.discoverystream.topicLabels.enabled", + type: "bool", + }, + { + id: "browser.newtabpage.activity-stream.discoverystream.sections.personalization.enabled", + type: "bool", + }, + { + id: "browser.newtabpage.activity-stream.discoverystream.sections.customizeMenuPanel.enabled", + type: "bool", + }, + { id: "browser.newtabpage.activity-stream.showSponsoredCheckboxes", type: "bool", }, @@ -191,6 +207,44 @@ if (Services.prefs.getBoolPref("browser.settings-redesign.enabled")) { id: "stories", pref: "browser.newtabpage.activity-stream.feeds.section.topstories", }); + Preferences.addSetting({ + id: "sectionsEnabled", + pref: "browser.newtabpage.activity-stream.discoverystream.sections.enabled", + }); + Preferences.addSetting({ + id: "topicLabelsEnabled", + pref: "browser.newtabpage.activity-stream.discoverystream.topicLabels.enabled", + }); + Preferences.addSetting({ + id: "sectionsPersonalizationEnabled", + pref: "browser.newtabpage.activity-stream.discoverystream.sections.personalization.enabled", + }); + Preferences.addSetting({ + id: "sectionsCustomizeMenuPanelEnabled", + pref: "browser.newtabpage.activity-stream.discoverystream.sections.customizeMenuPanel.enabled", + }); + Preferences.addSetting({ + id: "manageTopics", + deps: [ + "sectionsEnabled", + "topicLabelsEnabled", + "sectionsPersonalizationEnabled", + "sectionsCustomizeMenuPanelEnabled", + "sectionTopstories", + ], + visible: ({ + sectionsEnabled, + topicLabelsEnabled, + sectionsPersonalizationEnabled, + sectionsCustomizeMenuPanelEnabled, + sectionTopstories, + }) => + sectionsEnabled.value && + topicLabelsEnabled.value && + sectionsPersonalizationEnabled.value && + sectionsCustomizeMenuPanelEnabled.value && + sectionTopstories.value, + }); // Dependency prefs for sponsored stories visibility Preferences.addSetting({ @@ -256,6 +310,10 @@ if (Services.prefs.getBoolPref("browser.settings-redesign.enabled")) { id: "recentActivityDownloads", pref: "browser.newtabpage.activity-stream.section.highlights.includeDownloads", }); + + Preferences.addSetting({ + id: "chooseWallpaper", + }); } var gHomePane = { diff --git a/browser/components/preferences/main.js b/browser/components/preferences/main.js @@ -1875,6 +1875,16 @@ SettingGroupManager.registerGroups({ id: "stories", l10nId: "home-prefs-stories-header2", control: "moz-toggle", + items: [ + { + id: "manageTopics", + l10nId: "home-prefs-manage-topics-link2", + control: "moz-box-link", + controlAttrs: { + href: "about:newtab#customize-topics", + }, + }, + ], }, { id: "supportFirefox", @@ -1956,6 +1966,15 @@ SettingGroupManager.registerGroups({ }, ], }, + { + id: "chooseWallpaper", + l10nId: "home-prefs-choose-wallpaper-link2", + control: "moz-box-link", + controlAttrs: { + href: "about:newtab#customize", + }, + iconSrc: "chrome://browser/skin/customize.svg", + }, ], }, zoom: { diff --git a/browser/extensions/newtab/content-src/components/Base/Base.jsx b/browser/extensions/newtab/content-src/components/Base/Base.jsx @@ -112,6 +112,7 @@ export class BaseContent extends React.PureComponent { this.handleDismissDownloadHighlight = this.handleDismissDownloadHighlight.bind(this); this.applyBodyClasses = this.applyBodyClasses.bind(this); + this.toggleSectionsMgmtPanel = this.toggleSectionsMgmtPanel.bind(this); this.state = { fixedSearch: false, firstVisibleTimestamp: null, @@ -120,6 +121,7 @@ export class BaseContent extends React.PureComponent { wallpaperTheme: "", showDownloadHighlightOverride: null, visible: false, + showSectionsMgmtPanel: false, }; } @@ -235,6 +237,20 @@ export class BaseContent extends React.PureComponent { if (wallpapersEnabled) { this.updateWallpaper(); } + + this._onHashChange = () => { + const hash = globalThis.location?.hash || ""; + if (hash === "#customize" || hash === "#customize-topics") { + this.openCustomizationMenu(); + + if (hash === "#customize-topics") { + this.toggleSectionsMgmtPanel(); + } + } + }; + + this._onHashChange(); + globalThis.addEventListener("hashchange", this._onHashChange); } componentDidUpdate(prevProps) { @@ -310,6 +326,9 @@ export class BaseContent extends React.PureComponent { this._onVisibilityChange ); } + if (this._onHashChange) { + globalThis.removeEventListener("hashchange", this._onHashChange); + } } onWindowScroll() { @@ -586,6 +605,12 @@ export class BaseContent extends React.PureComponent { return 0.2125 * r + 0.7154 * g + 0.0721 * b <= 110; } + toggleSectionsMgmtPanel() { + this.setState(prevState => ({ + showSectionsMgmtPanel: !prevState.showSectionsMgmtPanel, + })); + } + shouldDisplayTopicSelectionModal() { const prefs = this.props.Prefs.values; const pocketEnabled = @@ -876,6 +901,8 @@ export class BaseContent extends React.PureComponent { mayHaveTimerWidget={mayHaveTimerWidget} mayHaveListsWidget={mayHaveListsWidget} showing={customizeMenuVisible} + toggleSectionsMgmtPanel={this.toggleSectionsMgmtPanel} + showSectionsMgmtPanel={this.state.showSectionsMgmtPanel} /> {this.shouldShowOMCHighlight("CustomWallpaperHighlight") && ( <MessageWrapper dispatch={this.props.dispatch}> diff --git a/browser/extensions/newtab/content-src/components/CustomizeMenu/ContentSection/ContentSection.jsx b/browser/extensions/newtab/content-src/components/CustomizeMenu/ContentSection/ContentSection.jsx @@ -108,6 +108,8 @@ export class ContentSection extends React.PureComponent { mayHaveTopicSections, exitEventFired, onSubpanelToggle, + toggleSectionsMgmtPanel, + showSectionsMgmtPanel, } = this.props; const { topSitesEnabled, @@ -303,6 +305,8 @@ export class ContentSection extends React.PureComponent { exitEventFired={exitEventFired} pocketEnabled={pocketEnabled} onSubpanelToggle={onSubpanelToggle} + togglePanel={toggleSectionsMgmtPanel} + showPanel={showSectionsMgmtPanel} /> )} </div> diff --git a/browser/extensions/newtab/content-src/components/CustomizeMenu/CustomizeMenu.jsx b/browser/extensions/newtab/content-src/components/CustomizeMenu/CustomizeMenu.jsx @@ -112,6 +112,8 @@ export class _CustomizeMenu extends React.PureComponent { dispatch={this.props.dispatch} exitEventFired={this.state.exitEventFired} onSubpanelToggle={this.onSubpanelToggle} + toggleSectionsMgmtPanel={this.props.toggleSectionsMgmtPanel} + showSectionsMgmtPanel={this.props.showSectionsMgmtPanel} /> </div> </div> diff --git a/browser/extensions/newtab/content-src/components/CustomizeMenu/SectionsMgmtPanel/SectionsMgmtPanel.jsx b/browser/extensions/newtab/content-src/components/CustomizeMenu/SectionsMgmtPanel/SectionsMgmtPanel.jsx @@ -2,7 +2,7 @@ * 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/. */ -import React, { useState, useCallback, useEffect } from "react"; +import React, { useState, useCallback, useEffect, useRef } from "react"; import { useDispatch, useSelector } from "react-redux"; import { actionCreators as ac, actionTypes as at } from "common/Actions.mjs"; // eslint-disable-next-line no-shadow @@ -12,8 +12,10 @@ function SectionsMgmtPanel({ exitEventFired, pocketEnabled, onSubpanelToggle, + togglePanel, + showPanel, }) { - const [showPanel, setShowPanel] = useState(false); // State management with useState + const arrowButtonRef = useRef(null); const { sectionPersonalization } = useSelector( state => state.DiscoveryStream ); @@ -173,10 +175,10 @@ function SectionsMgmtPanel({ // Close followed/blocked topic subpanel when parent menu is closed useEffect(() => { - if (exitEventFired) { - setShowPanel(false); + if (exitEventFired && showPanel) { + togglePanel(); } - }, [exitEventFired]); + }, [exitEventFired, showPanel, togglePanel]); // Notify parent menu when subpanel opens/closes useEffect(() => { @@ -185,13 +187,15 @@ function SectionsMgmtPanel({ } }, [showPanel, onSubpanelToggle]); - const togglePanel = () => { - setShowPanel(prevShowPanel => !prevShowPanel); - - // Fire when the panel is open - if (!showPanel) { + useEffect(() => { + if (showPanel) { updateCachedData(); } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [showPanel]); + + const handlePanelEntered = () => { + arrowButtonRef.current?.focus(); }; const followedSectionsList = followedSectionsData.map( @@ -286,9 +290,14 @@ function SectionsMgmtPanel({ timeout={300} classNames="sections-mgmt-panel" unmountOnExit={true} + onEntered={handlePanelEntered} > <div className="sections-mgmt-panel"> - <button className="arrow-button" onClick={togglePanel}> + <button + ref={arrowButtonRef} + className="arrow-button" + onClick={togglePanel} + > <h1 data-l10n-id="newtab-section-mangage-topics-title"></h1> </button> <h3 data-l10n-id="newtab-section-mangage-topics-followed-topics"></h3> diff --git a/browser/extensions/newtab/content-src/components/CustomizeMenu/_CustomizeMenu.scss b/browser/extensions/newtab/content-src/components/CustomizeMenu/_CustomizeMenu.scss @@ -510,6 +510,8 @@ -moz-context-properties: fill; fill: currentColor; min-height: var(--space-large); + margin-inline-start: var(--space-xsmall); + margin-block-start: var(--space-xsmall); h1 { font-size: var(--font-size-root); diff --git a/browser/extensions/newtab/css/activity-stream.css b/browser/extensions/newtab/css/activity-stream.css @@ -2395,6 +2395,8 @@ main section { -moz-context-properties: fill; fill: currentColor; min-height: var(--space-large); + margin-inline-start: var(--space-xsmall); + margin-block-start: var(--space-xsmall); } .sections-mgmt-panel .arrow-button:dir(rtl) { background: url("chrome://global/skin/icons/arrow-right.svg") no-repeat right center; diff --git a/browser/extensions/newtab/data/content/activity-stream.bundle.js b/browser/extensions/newtab/data/content/activity-stream.bundle.js @@ -13501,9 +13501,11 @@ function SectionsMgmtPanel_extends() { return SectionsMgmtPanel_extends = Object function SectionsMgmtPanel({ exitEventFired, pocketEnabled, - onSubpanelToggle + onSubpanelToggle, + togglePanel, + showPanel }) { - const [showPanel, setShowPanel] = (0,external_React_namespaceObject.useState)(false); // State management with useState + const arrowButtonRef = (0,external_React_namespaceObject.useRef)(null); const { sectionPersonalization } = (0,external_ReactRedux_namespaceObject.useSelector)(state => state.DiscoveryStream); @@ -13616,10 +13618,10 @@ function SectionsMgmtPanel({ // Close followed/blocked topic subpanel when parent menu is closed (0,external_React_namespaceObject.useEffect)(() => { - if (exitEventFired) { - setShowPanel(false); + if (exitEventFired && showPanel) { + togglePanel(); } - }, [exitEventFired]); + }, [exitEventFired, showPanel, togglePanel]); // Notify parent menu when subpanel opens/closes (0,external_React_namespaceObject.useEffect)(() => { @@ -13627,13 +13629,14 @@ function SectionsMgmtPanel({ onSubpanelToggle(showPanel); } }, [showPanel, onSubpanelToggle]); - const togglePanel = () => { - setShowPanel(prevShowPanel => !prevShowPanel); - - // Fire when the panel is open - if (!showPanel) { + (0,external_React_namespaceObject.useEffect)(() => { + if (showPanel) { updateCachedData(); } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [showPanel]); + const handlePanelEntered = () => { + arrowButtonRef.current?.focus(); }; const followedSectionsList = followedSectionsData.map(({ sectionKey, @@ -13702,10 +13705,12 @@ function SectionsMgmtPanel({ in: showPanel, timeout: 300, classNames: "sections-mgmt-panel", - unmountOnExit: true + unmountOnExit: true, + onEntered: handlePanelEntered }, /*#__PURE__*/external_React_default().createElement("div", { className: "sections-mgmt-panel" }, /*#__PURE__*/external_React_default().createElement("button", { + ref: arrowButtonRef, className: "arrow-button", onClick: togglePanel }, /*#__PURE__*/external_React_default().createElement("h1", { @@ -14457,7 +14462,9 @@ class ContentSection extends (external_React_default()).PureComponent { setPref, mayHaveTopicSections, exitEventFired, - onSubpanelToggle + onSubpanelToggle, + toggleSectionsMgmtPanel, + showSectionsMgmtPanel } = this.props; const { topSitesEnabled, @@ -14617,7 +14624,9 @@ class ContentSection extends (external_React_default()).PureComponent { })), mayHaveTopicSections && /*#__PURE__*/external_React_default().createElement(SectionsMgmtPanel, { exitEventFired: exitEventFired, pocketEnabled: pocketEnabled, - onSubpanelToggle: onSubpanelToggle + onSubpanelToggle: onSubpanelToggle, + togglePanel: toggleSectionsMgmtPanel, + showPanel: showSectionsMgmtPanel }))))))), /*#__PURE__*/external_React_default().createElement("span", { className: "divider", role: "separator" @@ -14730,7 +14739,9 @@ class _CustomizeMenu extends (external_React_default()).PureComponent { mayHaveListsWidget: this.props.mayHaveListsWidget, dispatch: this.props.dispatch, exitEventFired: this.state.exitEventFired, - onSubpanelToggle: this.onSubpanelToggle + onSubpanelToggle: this.onSubpanelToggle, + toggleSectionsMgmtPanel: this.props.toggleSectionsMgmtPanel, + showSectionsMgmtPanel: this.props.showSectionsMgmtPanel }))))); } } @@ -15612,6 +15623,7 @@ class BaseContent extends (external_React_default()).PureComponent { this.toggleDownloadHighlight = this.toggleDownloadHighlight.bind(this); this.handleDismissDownloadHighlight = this.handleDismissDownloadHighlight.bind(this); this.applyBodyClasses = this.applyBodyClasses.bind(this); + this.toggleSectionsMgmtPanel = this.toggleSectionsMgmtPanel.bind(this); this.state = { fixedSearch: false, firstVisibleTimestamp: null, @@ -15619,7 +15631,8 @@ class BaseContent extends (external_React_default()).PureComponent { fixedNavStyle: {}, wallpaperTheme: "", showDownloadHighlightOverride: null, - visible: false + visible: false, + showSectionsMgmtPanel: false }; } setFirstVisibleTimestamp() { @@ -15713,6 +15726,17 @@ class BaseContent extends (external_React_default()).PureComponent { if (wallpapersEnabled) { this.updateWallpaper(); } + this._onHashChange = () => { + const hash = globalThis.location?.hash || ""; + if (hash === "#customize" || hash === "#customize-topics") { + this.openCustomizationMenu(); + if (hash === "#customize-topics") { + this.toggleSectionsMgmtPanel(); + } + } + }; + this._onHashChange(); + globalThis.addEventListener("hashchange", this._onHashChange); } componentDidUpdate(prevProps) { this.applyBodyClasses(); @@ -15780,6 +15804,9 @@ class BaseContent extends (external_React_default()).PureComponent { if (this._onVisibilityChange) { this.props.document.removeEventListener(Base_VISIBILITY_CHANGE_EVENT, this._onVisibilityChange); } + if (this._onHashChange) { + globalThis.removeEventListener("hashchange", this._onHashChange); + } } onWindowScroll() { if (window.innerHeight <= 700) { @@ -16025,6 +16052,11 @@ class BaseContent extends (external_React_default()).PureComponent { isWallpaperColorDark([r, g, b]) { return 0.2125 * r + 0.7154 * g + 0.0721 * b <= 110; } + toggleSectionsMgmtPanel() { + this.setState(prevState => ({ + showSectionsMgmtPanel: !prevState.showSectionsMgmtPanel + })); + } shouldDisplayTopicSelectionModal() { const prefs = this.props.Prefs.values; const pocketEnabled = prefs["feeds.section.topstories"] && prefs["feeds.system.topstories"]; @@ -16192,7 +16224,9 @@ class BaseContent extends (external_React_default()).PureComponent { mayHaveWidgets: mayHaveWidgets, mayHaveTimerWidget: mayHaveTimerWidget, mayHaveListsWidget: mayHaveListsWidget, - showing: customizeMenuVisible + showing: customizeMenuVisible, + toggleSectionsMgmtPanel: this.toggleSectionsMgmtPanel, + showSectionsMgmtPanel: this.state.showSectionsMgmtPanel }), this.shouldShowOMCHighlight("CustomWallpaperHighlight") && /*#__PURE__*/external_React_default().createElement(MessageWrapper, { dispatch: this.props.dispatch }, /*#__PURE__*/external_React_default().createElement(WallpaperFeatureHighlight, { diff --git a/browser/locales/en-US/browser/preferences/preferences.ftl b/browser/locales/en-US/browser/preferences/preferences.ftl @@ -864,8 +864,15 @@ home-prefs-mission-message2 = .message = Our sponsors support our mission to build a better web. home-prefs-manage-topics-link = Manage topics + +home-prefs-manage-topics-link2 = + .label = Manage topics + home-prefs-choose-wallpaper-link = Choose a wallpaper +home-prefs-choose-wallpaper-link2 = + .label = Choose a wallpaper + # Variables: # $num (number) - Number of rows displayed home-prefs-sections-rows-option = diff --git a/python/l10n/fluent_migrations/bug_2003204_wallpapers_and_topics_labels_settings.py b/python/l10n/fluent_migrations/bug_2003204_wallpapers_and_topics_labels_settings.py @@ -0,0 +1,27 @@ +# Any copyright is dedicated to the Public Domain. +# http://creativecommons.org/publicdomain/zero/1.0/ + +from fluent.migrate.helpers import transforms_from + + +def migrate(ctx): + """Bug 2003204 - Open customization panel from about:preferences#home, part {index}""" + + source = "browser/browser/preferences/preferences.ftl" + target = source + + ctx.add_transforms( + target, + target, + transforms_from( + """ +home-prefs-manage-topics-link2 = + .label = { COPY_PATTERN(from_path, "home-prefs-manage-topics-link")} + +home-prefs-choose-wallpaper-link2 = + .label = { COPY_PATTERN(from_path, "home-prefs-choose-wallpaper-link")} + +""", + from_path=source, + ), + )