diff options
Diffstat (limited to 'browser/components/newtab/content-src')
49 files changed, 479 insertions, 124 deletions
diff --git a/browser/components/newtab/content-src/activity-stream.jsx b/browser/components/newtab/content-src/activity-stream.jsx index c588e8e850..57ba9f9c92 100644 --- a/browser/components/newtab/content-src/activity-stream.jsx +++ b/browser/components/newtab/content-src/activity-stream.jsx @@ -2,10 +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 { - actionCreators as ac, - actionTypes as at, -} from "common/Actions.sys.mjs"; +import { actionCreators as ac, actionTypes as at } from "common/Actions.mjs"; import { Base } from "content-src/components/Base/Base"; import { DetectUserSessionStart } from "content-src/lib/detect-user-session-start"; import { initStore } from "content-src/lib/init-store"; diff --git a/browser/components/newtab/content-src/components/Base/Base.jsx b/browser/components/newtab/content-src/components/Base/Base.jsx index 20402b09f5..1738f8f51a 100644 --- a/browser/components/newtab/content-src/components/Base/Base.jsx +++ b/browser/components/newtab/content-src/components/Base/Base.jsx @@ -2,10 +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 { - actionCreators as ac, - actionTypes as at, -} from "common/Actions.sys.mjs"; +import { actionCreators as ac, actionTypes as at } from "common/Actions.mjs"; import { DiscoveryStreamAdmin } from "content-src/components/DiscoveryStreamAdmin/DiscoveryStreamAdmin"; import { ConfirmDialog } from "content-src/components/ConfirmDialog/ConfirmDialog"; import { connect } from "react-redux"; @@ -16,6 +13,9 @@ import React from "react"; import { Search } from "content-src/components/Search/Search"; import { Sections } from "content-src/components/Sections/Sections"; +const VISIBLE = "visible"; +const VISIBILITY_CHANGE_EVENT = "visibilitychange"; + export const PrefsButton = ({ onClick, icon }) => ( <div className="prefs-button"> <button @@ -76,7 +76,7 @@ export class _Base extends React.PureComponent { ] .filter(v => v) .join(" "); - global.document.body.className = bodyClassName; + globalThis.document.body.className = bodyClassName; } render() { @@ -110,17 +110,75 @@ export class BaseContent extends React.PureComponent { this.handleOnKeyDown = this.handleOnKeyDown.bind(this); this.onWindowScroll = debounce(this.onWindowScroll.bind(this), 5); this.setPref = this.setPref.bind(this); - this.state = { fixedSearch: false }; + this.updateWallpaper = this.updateWallpaper.bind(this); + this.prefersDarkQuery = null; + this.handleColorModeChange = this.handleColorModeChange.bind(this); + this.state = { + fixedSearch: false, + firstVisibleTimestamp: null, + colorMode: "", + }; + } + + setFirstVisibleTimestamp() { + if (!this.state.firstVisibleTimestamp) { + this.setState({ + firstVisibleTimestamp: Date.now(), + }); + } } componentDidMount() { global.addEventListener("scroll", this.onWindowScroll); global.addEventListener("keydown", this.handleOnKeyDown); + if (this.props.document.visibilityState === VISIBLE) { + this.setFirstVisibleTimestamp(); + } else { + this._onVisibilityChange = () => { + if (this.props.document.visibilityState === VISIBLE) { + this.setFirstVisibleTimestamp(); + this.props.document.removeEventListener( + VISIBILITY_CHANGE_EVENT, + this._onVisibilityChange + ); + this._onVisibilityChange = null; + } + }; + this.props.document.addEventListener( + VISIBILITY_CHANGE_EVENT, + this._onVisibilityChange + ); + } + // track change event to dark/light mode + this.prefersDarkQuery = globalThis.matchMedia( + "(prefers-color-scheme: dark)" + ); + + this.prefersDarkQuery.addEventListener( + "change", + this.handleColorModeChange + ); + this.handleColorModeChange(); + } + + handleColorModeChange() { + const colorMode = this.prefersDarkQuery?.matches ? "dark" : "light"; + this.setState({ colorMode }); } componentWillUnmount() { + this.prefersDarkQuery?.removeEventListener( + "change", + this.handleColorModeChange + ); global.removeEventListener("scroll", this.onWindowScroll); global.removeEventListener("keydown", this.handleOnKeyDown); + if (this._onVisibilityChange) { + this.props.document.removeEventListener( + VISIBILITY_CHANGE_EVENT, + this._onVisibilityChange + ); + } } onWindowScroll() { @@ -160,11 +218,79 @@ export class BaseContent extends React.PureComponent { this.props.dispatch(ac.SetPref(pref, value)); } + renderWallpaperAttribution() { + const { wallpaperList } = this.props.Wallpapers; + const activeWallpaper = + this.props.Prefs.values[ + `newtabWallpapers.wallpaper-${this.state.colorMode}` + ]; + const selected = wallpaperList.find(wp => wp.title === activeWallpaper); + // make sure a wallpaper is selected and that the attribution also exists + if (!selected?.attribution) { + return null; + } + + const { name, webpage } = selected.attribution; + if (activeWallpaper && wallpaperList && name.url) { + return ( + <p + className={`wallpaper-attribution`} + key={name} + data-l10n-id="newtab-wallpaper-attribution" + data-l10n-args={JSON.stringify({ + author_string: name.string, + author_url: name.url, + webpage_string: webpage.string, + webpage_url: webpage.url, + })} + > + <a data-l10n-name="name-link" href={name.url}> + {name.string} + </a> + <a data-l10n-name="webpage-link" href={webpage.url}> + {webpage.string} + </a> + </p> + ); + } + return null; + } + + async updateWallpaper() { + const prefs = this.props.Prefs.values; + const { wallpaperList } = this.props.Wallpapers; + + if (wallpaperList) { + const lightWallpaper = + wallpaperList.find( + wp => wp.title === prefs["newtabWallpapers.wallpaper-light"] + ) || ""; + const darkWallpaper = + wallpaperList.find( + wp => wp.title === prefs["newtabWallpapers.wallpaper-dark"] + ) || ""; + global.document?.body.style.setProperty( + `--newtab-wallpaper-light`, + `url(${lightWallpaper?.wallpaperUrl || ""})` + ); + + global.document?.body.style.setProperty( + `--newtab-wallpaper-dark`, + `url(${darkWallpaper?.wallpaperUrl || ""})` + ); + } + } + render() { const { props } = this; const { App } = props; const { initialized, customizeMenuVisible } = App; const prefs = props.Prefs.values; + + const activeWallpaper = + prefs[`newtabWallpapers.wallpaper-${this.state.colorMode}`]; + const wallpapersEnabled = prefs["newtabWallpapers.enabled"]; + const { pocketConfig } = prefs; const isDiscoveryStream = @@ -215,6 +341,9 @@ export class BaseContent extends React.PureComponent { ] .filter(v => v) .join(" "); + if (wallpapersEnabled) { + this.updateWallpaper(); + } return ( <div> @@ -224,6 +353,8 @@ export class BaseContent extends React.PureComponent { openPreferences={this.openPreferences} setPref={this.setPref} enabledSections={enabledSections} + wallpapersEnabled={wallpapersEnabled} + activeWallpaper={activeWallpaper} pocketRegion={pocketRegion} mayHaveSponsoredTopSites={mayHaveSponsoredTopSites} mayHaveSponsoredStories={mayHaveSponsoredStories} @@ -252,6 +383,7 @@ export class BaseContent extends React.PureComponent { <DiscoveryStreamBase locale={props.App.locale} mayHaveSponsoredStories={mayHaveSponsoredStories} + firstVisibleTimestamp={this.state.firstVisibleTimestamp} /> </ErrorBoundary> ) : ( @@ -259,6 +391,7 @@ export class BaseContent extends React.PureComponent { )} </div> <ConfirmDialog /> + {wallpapersEnabled && this.renderWallpaperAttribution()} </main> </div> </div> @@ -266,10 +399,15 @@ export class BaseContent extends React.PureComponent { } } +BaseContent.defaultProps = { + document: global.document, +}; + export const Base = connect(state => ({ App: state.App, Prefs: state.Prefs, Sections: state.Sections, DiscoveryStream: state.DiscoveryStream, Search: state.Search, + Wallpapers: state.Wallpapers, }))(_Base); diff --git a/browser/components/newtab/content-src/components/Base/_Base.scss b/browser/components/newtab/content-src/components/Base/_Base.scss index 1282173df5..a9141e0923 100644 --- a/browser/components/newtab/content-src/components/Base/_Base.scss +++ b/browser/components/newtab/content-src/components/Base/_Base.scss @@ -24,10 +24,17 @@ } main { - margin: auto; + margin: 0 auto; + display: flex; + flex-direction: column; + justify-content: center; width: $wrapper-default-width; padding: 0; + .vertical-center-wrapper { + margin: auto 0; + } + section { margin-bottom: $section-spacing; position: relative; @@ -124,3 +131,32 @@ main { } } } + +.wallpaper-attribution { + padding: 0 $section-horizontal-padding; + font-size: 14px; + + &.theme-light { + display: inline-block; + + @include dark-theme-only { + display: none; + } + } + + &.theme-dark { + display: none; + + @include dark-theme-only { + display: inline-block; + } + } + + a { + color: var(--newtab-element-color); + + &:hover { + text-decoration: none; + } + } +} diff --git a/browser/components/newtab/content-src/components/Card/Card.jsx b/browser/components/newtab/content-src/components/Card/Card.jsx index 9d03377f1b..da5e0346d7 100644 --- a/browser/components/newtab/content-src/components/Card/Card.jsx +++ b/browser/components/newtab/content-src/components/Card/Card.jsx @@ -2,10 +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 { - actionCreators as ac, - actionTypes as at, -} from "common/Actions.sys.mjs"; +import { actionCreators as ac, actionTypes as at } from "common/Actions.mjs"; import { cardContextTypes } from "./types"; import { connect } from "react-redux"; import { ContextMenuButton } from "content-src/components/ContextMenu/ContextMenuButton"; diff --git a/browser/components/newtab/content-src/components/Card/types.js b/browser/components/newtab/content-src/components/Card/types.mjs index 0b17eea408..0b17eea408 100644 --- a/browser/components/newtab/content-src/components/Card/types.js +++ b/browser/components/newtab/content-src/components/Card/types.mjs diff --git a/browser/components/newtab/content-src/components/CollapsibleSection/CollapsibleSection.jsx b/browser/components/newtab/content-src/components/CollapsibleSection/CollapsibleSection.jsx index 98bf88fbea..2046617ad6 100644 --- a/browser/components/newtab/content-src/components/CollapsibleSection/CollapsibleSection.jsx +++ b/browser/components/newtab/content-src/components/CollapsibleSection/CollapsibleSection.jsx @@ -119,7 +119,7 @@ export class _CollapsibleSection extends React.PureComponent { } _CollapsibleSection.defaultProps = { - document: global.document || { + document: globalThis.document || { addEventListener: () => {}, removeEventListener: () => {}, visibilityState: "hidden", diff --git a/browser/components/newtab/content-src/components/ComponentPerfTimer/ComponentPerfTimer.jsx b/browser/components/newtab/content-src/components/ComponentPerfTimer/ComponentPerfTimer.jsx index 4efd8c712e..ffcc6b62f4 100644 --- a/browser/components/newtab/content-src/components/ComponentPerfTimer/ComponentPerfTimer.jsx +++ b/browser/components/newtab/content-src/components/ComponentPerfTimer/ComponentPerfTimer.jsx @@ -2,10 +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 { - actionCreators as ac, - actionTypes as at, -} from "common/Actions.sys.mjs"; +import { actionCreators as ac, actionTypes as at } from "common/Actions.mjs"; import { perfService as perfSvc } from "content-src/lib/perf-service"; import React from "react"; diff --git a/browser/components/newtab/content-src/components/ConfirmDialog/ConfirmDialog.jsx b/browser/components/newtab/content-src/components/ConfirmDialog/ConfirmDialog.jsx index f69e540079..734f261b27 100644 --- a/browser/components/newtab/content-src/components/ConfirmDialog/ConfirmDialog.jsx +++ b/browser/components/newtab/content-src/components/ConfirmDialog/ConfirmDialog.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 { actionCreators as ac, actionTypes } from "common/Actions.sys.mjs"; +import { actionCreators as ac, actionTypes } from "common/Actions.mjs"; import { connect } from "react-redux"; import React from "react"; diff --git a/browser/components/newtab/content-src/components/ContextMenu/ContextMenu.jsx b/browser/components/newtab/content-src/components/ContextMenu/ContextMenu.jsx index 5ea6a57f71..458f65e644 100644 --- a/browser/components/newtab/content-src/components/ContextMenu/ContextMenu.jsx +++ b/browser/components/newtab/content-src/components/ContextMenu/ContextMenu.jsx @@ -26,12 +26,12 @@ export class ContextMenu extends React.PureComponent { componentDidMount() { this.onShow(); setTimeout(() => { - global.addEventListener("click", this.hideContext); + globalThis.addEventListener("click", this.hideContext); }, 0); } componentWillUnmount() { - global.removeEventListener("click", this.hideContext); + globalThis.removeEventListener("click", this.hideContext); } onClick(event) { diff --git a/browser/components/newtab/content-src/components/CustomizeMenu/ContentSection/ContentSection.jsx b/browser/components/newtab/content-src/components/CustomizeMenu/ContentSection/ContentSection.jsx index 298dedcee5..1dd13fc965 100644 --- a/browser/components/newtab/content-src/components/CustomizeMenu/ContentSection/ContentSection.jsx +++ b/browser/components/newtab/content-src/components/CustomizeMenu/ContentSection/ContentSection.jsx @@ -3,8 +3,9 @@ * You can obtain one at http://mozilla.org/MPL/2.0/. */ import React from "react"; -import { actionCreators as ac } from "common/Actions.sys.mjs"; +import { actionCreators as ac } from "common/Actions.mjs"; import { SafeAnchor } from "../../DiscoveryStreamComponents/SafeAnchor/SafeAnchor"; +import { WallpapersSection } from "../../WallpapersSection/WallpapersSection"; export class ContentSection extends React.PureComponent { constructor(props) { @@ -98,6 +99,9 @@ export class ContentSection extends React.PureComponent { mayHaveRecentSaves, openPreferences, spocMessageVariant, + wallpapersEnabled, + activeWallpaper, + setPref, } = this.props; const { topSitesEnabled, @@ -111,6 +115,15 @@ export class ContentSection extends React.PureComponent { return ( <div className="home-section"> + {wallpapersEnabled && ( + <div className="wallpapers-section"> + <h2 data-l10n-id="newtab-wallpaper-title"></h2> + <WallpapersSection + setPref={setPref} + activeWallpaper={activeWallpaper} + /> + </div> + )} <div id="shortcuts-section" className="section"> <moz-toggle id="shortcuts-toggle" diff --git a/browser/components/newtab/content-src/components/CustomizeMenu/CustomizeMenu.jsx b/browser/components/newtab/content-src/components/CustomizeMenu/CustomizeMenu.jsx index 54dcd550c4..f1c723fed2 100644 --- a/browser/components/newtab/content-src/components/CustomizeMenu/CustomizeMenu.jsx +++ b/browser/components/newtab/content-src/components/CustomizeMenu/CustomizeMenu.jsx @@ -2,7 +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/. */ -import { BackgroundsSection } from "content-src/components/CustomizeMenu/BackgroundsSection/BackgroundsSection"; import { ContentSection } from "content-src/components/CustomizeMenu/ContentSection/ContentSection"; import { connect } from "react-redux"; import React from "react"; @@ -62,11 +61,12 @@ export class _CustomizeMenu extends React.PureComponent { data-l10n-id="newtab-custom-close-button" ref={c => (this.closeButton = c)} /> - <BackgroundsSection /> <ContentSection openPreferences={this.props.openPreferences} setPref={this.props.setPref} enabledSections={this.props.enabledSections} + wallpapersEnabled={this.props.wallpapersEnabled} + activeWallpaper={this.props.activeWallpaper} pocketRegion={this.props.pocketRegion} mayHaveSponsoredTopSites={this.props.mayHaveSponsoredTopSites} mayHaveSponsoredStories={this.props.mayHaveSponsoredStories} diff --git a/browser/components/newtab/content-src/components/CustomizeMenu/_CustomizeMenu.scss b/browser/components/newtab/content-src/components/CustomizeMenu/_CustomizeMenu.scss index 579e455a3f..c20da5ce50 100644 --- a/browser/components/newtab/content-src/components/CustomizeMenu/_CustomizeMenu.scss +++ b/browser/components/newtab/content-src/components/CustomizeMenu/_CustomizeMenu.scss @@ -119,6 +119,10 @@ grid-row-gap: 32px; padding: 0 16px; + .wallpapers-section h2 { + font-size: inherit; + } + .section { moz-toggle { margin-bottom: 10px; diff --git a/browser/components/newtab/content-src/components/DiscoveryStreamAdmin/DiscoveryStreamAdmin.jsx b/browser/components/newtab/content-src/components/DiscoveryStreamAdmin/DiscoveryStreamAdmin.jsx index 3c31a5a29f..8b9d64dfc1 100644 --- a/browser/components/newtab/content-src/components/DiscoveryStreamAdmin/DiscoveryStreamAdmin.jsx +++ b/browser/components/newtab/content-src/components/DiscoveryStreamAdmin/DiscoveryStreamAdmin.jsx @@ -2,10 +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 { - actionCreators as ac, - actionTypes as at, -} from "common/Actions.sys.mjs"; +import { actionCreators as ac, actionTypes as at } from "common/Actions.mjs"; import { connect } from "react-redux"; import React from "react"; import { SimpleHashRouter } from "./SimpleHashRouter"; @@ -445,9 +442,9 @@ export class CollapseToggle extends React.PureComponent { setBodyClass() { if (this.renderAdmin && !this.state.collapsed) { - global.document.body.classList.add("no-scroll"); + globalThis.document.body.classList.add("no-scroll"); } else { - global.document.body.classList.remove("no-scroll"); + globalThis.document.body.classList.remove("no-scroll"); } } @@ -460,7 +457,7 @@ export class CollapseToggle extends React.PureComponent { } componentWillUnmount() { - global.document.body.classList.remove("no-scroll"); + globalThis.document.body.classList.remove("no-scroll"); } render() { diff --git a/browser/components/newtab/content-src/components/DiscoveryStreamAdmin/SimpleHashRouter.jsx b/browser/components/newtab/content-src/components/DiscoveryStreamAdmin/SimpleHashRouter.jsx index 9c3fd8579c..bc7b0c42c5 100644 --- a/browser/components/newtab/content-src/components/DiscoveryStreamAdmin/SimpleHashRouter.jsx +++ b/browser/components/newtab/content-src/components/DiscoveryStreamAdmin/SimpleHashRouter.jsx @@ -8,19 +8,19 @@ export class SimpleHashRouter extends React.PureComponent { constructor(props) { super(props); this.onHashChange = this.onHashChange.bind(this); - this.state = { hash: global.location.hash }; + this.state = { hash: globalThis.location.hash }; } onHashChange() { - this.setState({ hash: global.location.hash }); + this.setState({ hash: globalThis.location.hash }); } componentWillMount() { - global.addEventListener("hashchange", this.onHashChange); + globalThis.addEventListener("hashchange", this.onHashChange); } componentWillUnmount() { - global.removeEventListener("hashchange", this.onHashChange); + globalThis.removeEventListener("hashchange", this.onHashChange); } render() { diff --git a/browser/components/newtab/content-src/components/DiscoveryStreamBase/DiscoveryStreamBase.jsx b/browser/components/newtab/content-src/components/DiscoveryStreamBase/DiscoveryStreamBase.jsx index 0f0ee51ab9..8b5826dd82 100644 --- a/browser/components/newtab/content-src/components/DiscoveryStreamBase/DiscoveryStreamBase.jsx +++ b/browser/components/newtab/content-src/components/DiscoveryStreamBase/DiscoveryStreamBase.jsx @@ -164,7 +164,7 @@ export class _DiscoveryStreamBase extends React.PureComponent { privacyNoticeURL={component.properties.privacyNoticeURL} /> ); - case "CollectionCardGrid": + case "CollectionCardGrid": { const { DiscoveryStream } = this.props; return ( <CollectionCardGrid @@ -178,6 +178,7 @@ export class _DiscoveryStreamBase extends React.PureComponent { dispatch={this.props.dispatch} /> ); + } case "CardGrid": return ( <CardGrid @@ -200,6 +201,7 @@ export class _DiscoveryStreamBase extends React.PureComponent { editorsPicksHeader={component.properties.editorsPicksHeader} recentSavesEnabled={this.props.DiscoveryStream.recentSavesEnabled} hideDescriptions={this.props.DiscoveryStream.hideDescriptions} + firstVisibleTimestamp={this.props.firstVisibleTimestamp} /> ); case "HorizontalRule": @@ -384,6 +386,6 @@ export const DiscoveryStreamBase = connect(state => ({ DiscoveryStream: state.DiscoveryStream, Prefs: state.Prefs, Sections: state.Sections, - document: global.document, + document: globalThis.document, App: state.App, }))(_DiscoveryStreamBase); diff --git a/browser/components/newtab/content-src/components/DiscoveryStreamComponents/CardGrid/CardGrid.jsx b/browser/components/newtab/content-src/components/DiscoveryStreamComponents/CardGrid/CardGrid.jsx index cf00361df2..2a9497d1b4 100644 --- a/browser/components/newtab/content-src/components/DiscoveryStreamComponents/CardGrid/CardGrid.jsx +++ b/browser/components/newtab/content-src/components/DiscoveryStreamComponents/CardGrid/CardGrid.jsx @@ -8,10 +8,7 @@ import { DSDismiss } from "content-src/components/DiscoveryStreamComponents/DSDi import { TopicsWidget } from "../TopicsWidget/TopicsWidget.jsx"; import { SafeAnchor } from "../SafeAnchor/SafeAnchor"; import { FluentOrText } from "../../FluentOrText/FluentOrText.jsx"; -import { - actionCreators as ac, - actionTypes as at, -} from "common/Actions.sys.mjs"; +import { actionCreators as ac, actionTypes as at } from "common/Actions.mjs"; import React, { useEffect, useState, useRef, useCallback } from "react"; import { connect, useSelector } from "react-redux"; const PREF_ONBOARDING_EXPERIENCE_DISMISSED = @@ -31,7 +28,7 @@ export function DSSubHeader({ children }) { ); } -export function OnboardingExperience({ dispatch, windowObj = global }) { +export function OnboardingExperience({ dispatch, windowObj = globalThis }) { const [dismissed, setDismissed] = useState(false); const [maxHeight, setMaxHeight] = useState(null); const heightElement = useRef(null); @@ -361,6 +358,7 @@ export class _CardGrid extends React.PureComponent { url={rec.url} id={rec.id} shim={rec.shim} + fetchTimestamp={rec.fetchTimestamp} type={this.props.type} context={rec.context} sponsor={rec.sponsor} @@ -377,6 +375,7 @@ export class _CardGrid extends React.PureComponent { ctaButtonVariant={ctaButtonVariant} spocMessageVariant={spocMessageVariant} recommendation_id={rec.recommendation_id} + firstVisibleTimestamp={this.props.firstVisibleTimestamp} /> ) ); diff --git a/browser/components/newtab/content-src/components/DiscoveryStreamComponents/CollectionCardGrid/CollectionCardGrid.jsx b/browser/components/newtab/content-src/components/DiscoveryStreamComponents/CollectionCardGrid/CollectionCardGrid.jsx index d089a5c8ab..4f3f150a9b 100644 --- a/browser/components/newtab/content-src/components/DiscoveryStreamComponents/CollectionCardGrid/CollectionCardGrid.jsx +++ b/browser/components/newtab/content-src/components/DiscoveryStreamComponents/CollectionCardGrid/CollectionCardGrid.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 { actionCreators as ac } from "common/Actions.sys.mjs"; +import { actionCreators as ac } from "common/Actions.mjs"; import { CardGrid } from "content-src/components/DiscoveryStreamComponents/CardGrid/CardGrid"; import { DSDismiss } from "content-src/components/DiscoveryStreamComponents/DSDismiss/DSDismiss"; import { LinkMenuOptions } from "content-src/lib/link-menu-options"; diff --git a/browser/components/newtab/content-src/components/DiscoveryStreamComponents/DSCard/DSCard.jsx b/browser/components/newtab/content-src/components/DiscoveryStreamComponents/DSCard/DSCard.jsx index f3e1eab503..b3d965530d 100644 --- a/browser/components/newtab/content-src/components/DiscoveryStreamComponents/DSCard/DSCard.jsx +++ b/browser/components/newtab/content-src/components/DiscoveryStreamComponents/DSCard/DSCard.jsx @@ -2,10 +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 { - actionCreators as ac, - actionTypes as at, -} from "common/Actions.sys.mjs"; +import { actionCreators as ac, actionTypes as at } from "common/Actions.mjs"; import { DSImage } from "../DSImage/DSImage.jsx"; import { DSLinkMenu } from "../DSLinkMenu/DSLinkMenu"; import { ImpressionStats } from "../../DiscoveryStreamImpressionStats/ImpressionStats"; @@ -198,6 +195,8 @@ export class _DSCard extends React.PureComponent { ...(this.props.shim && this.props.shim.click ? { shim: this.props.shim.click } : {}), + fetchTimestamp: this.props.fetchTimestamp, + firstVisibleTimestamp: this.props.firstVisibleTimestamp, }, }) ); @@ -245,6 +244,8 @@ export class _DSCard extends React.PureComponent { ...(this.props.shim && this.props.shim.save ? { shim: this.props.shim.save } : {}), + fetchTimestamp: this.props.fetchTimestamp, + firstVisibleTimestamp: this.props.firstVisibleTimestamp, }, }) ); @@ -441,10 +442,12 @@ export class _DSCard extends React.PureComponent { ? { shim: this.props.shim.impression } : {}), recommendation_id: this.props.recommendation_id, + fetchTimestamp: this.props.fetchTimestamp, }, ]} dispatch={this.props.dispatch} source={this.props.type} + firstVisibleTimestamp={this.props.firstVisibleTimestamp} /> </SafeAnchor> {ctaButtonVariant === "variant-b" && ( diff --git a/browser/components/newtab/content-src/components/DiscoveryStreamComponents/DSContextFooter/DSContextFooter.jsx b/browser/components/newtab/content-src/components/DiscoveryStreamComponents/DSContextFooter/DSContextFooter.jsx index 6c0641cfc1..80af05c585 100644 --- a/browser/components/newtab/content-src/components/DiscoveryStreamComponents/DSContextFooter/DSContextFooter.jsx +++ b/browser/components/newtab/content-src/components/DiscoveryStreamComponents/DSContextFooter/DSContextFooter.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 { cardContextTypes } from "../../Card/types.js"; +import { cardContextTypes } from "../../Card/types.mjs"; import { SponsoredContentHighlight } from "../FeatureHighlight/SponsoredContentHighlight"; import { CSSTransition, TransitionGroup } from "react-transition-group"; import { FluentOrText } from "../../FluentOrText/FluentOrText.jsx"; diff --git a/browser/components/newtab/content-src/components/DiscoveryStreamComponents/DSEmptyState/DSEmptyState.jsx b/browser/components/newtab/content-src/components/DiscoveryStreamComponents/DSEmptyState/DSEmptyState.jsx index ff3886b407..ed90f68606 100644 --- a/browser/components/newtab/content-src/components/DiscoveryStreamComponents/DSEmptyState/DSEmptyState.jsx +++ b/browser/components/newtab/content-src/components/DiscoveryStreamComponents/DSEmptyState/DSEmptyState.jsx @@ -2,10 +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 { - actionCreators as ac, - actionTypes as at, -} from "common/Actions.sys.mjs"; +import { actionCreators as ac, actionTypes as at } from "common/Actions.mjs"; import React from "react"; export class DSEmptyState extends React.PureComponent { diff --git a/browser/components/newtab/content-src/components/DiscoveryStreamComponents/DSLinkMenu/DSLinkMenu.jsx b/browser/components/newtab/content-src/components/DiscoveryStreamComponents/DSLinkMenu/DSLinkMenu.jsx index b75063940c..107adca4da 100644 --- a/browser/components/newtab/content-src/components/DiscoveryStreamComponents/DSLinkMenu/DSLinkMenu.jsx +++ b/browser/components/newtab/content-src/components/DiscoveryStreamComponents/DSLinkMenu/DSLinkMenu.jsx @@ -4,7 +4,7 @@ import { LinkMenu } from "content-src/components/LinkMenu/LinkMenu"; import { ContextMenuButton } from "content-src/components/ContextMenu/ContextMenuButton"; -import { actionCreators as ac } from "common/Actions.sys.mjs"; +import { actionCreators as ac } from "common/Actions.mjs"; import React from "react"; export class DSLinkMenu extends React.PureComponent { diff --git a/browser/components/newtab/content-src/components/DiscoveryStreamComponents/DSPrivacyModal/DSPrivacyModal.jsx b/browser/components/newtab/content-src/components/DiscoveryStreamComponents/DSPrivacyModal/DSPrivacyModal.jsx index b251fb0401..2275f8b22b 100644 --- a/browser/components/newtab/content-src/components/DiscoveryStreamComponents/DSPrivacyModal/DSPrivacyModal.jsx +++ b/browser/components/newtab/content-src/components/DiscoveryStreamComponents/DSPrivacyModal/DSPrivacyModal.jsx @@ -3,10 +3,7 @@ * You can obtain one at http://mozilla.org/MPL/2.0/. */ import React from "react"; -import { - actionCreators as ac, - actionTypes as at, -} from "common/Actions.sys.mjs"; +import { actionCreators as ac, actionTypes as at } from "common/Actions.mjs"; import { ModalOverlayWrapper } from "content-src/components/ModalOverlay/ModalOverlay"; export class DSPrivacyModal extends React.PureComponent { diff --git a/browser/components/newtab/content-src/components/DiscoveryStreamComponents/DSSignup/DSSignup.jsx b/browser/components/newtab/content-src/components/DiscoveryStreamComponents/DSSignup/DSSignup.jsx index b7e3205646..0a4d687c65 100644 --- a/browser/components/newtab/content-src/components/DiscoveryStreamComponents/DSSignup/DSSignup.jsx +++ b/browser/components/newtab/content-src/components/DiscoveryStreamComponents/DSSignup/DSSignup.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 { actionCreators as ac } from "common/Actions.sys.mjs"; +import { actionCreators as ac } from "common/Actions.mjs"; import { LinkMenu } from "content-src/components/LinkMenu/LinkMenu"; import { ContextMenuButton } from "content-src/components/ContextMenu/ContextMenuButton"; import { ImpressionStats } from "../../DiscoveryStreamImpressionStats/ImpressionStats"; diff --git a/browser/components/newtab/content-src/components/DiscoveryStreamComponents/DSTextPromo/DSTextPromo.jsx b/browser/components/newtab/content-src/components/DiscoveryStreamComponents/DSTextPromo/DSTextPromo.jsx index 02a3326eb7..fc52decdf8 100644 --- a/browser/components/newtab/content-src/components/DiscoveryStreamComponents/DSTextPromo/DSTextPromo.jsx +++ b/browser/components/newtab/content-src/components/DiscoveryStreamComponents/DSTextPromo/DSTextPromo.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 { actionCreators as ac } from "common/Actions.sys.mjs"; +import { actionCreators as ac } from "common/Actions.mjs"; import { DSDismiss } from "content-src/components/DiscoveryStreamComponents/DSDismiss/DSDismiss"; import { DSImage } from "../DSImage/DSImage.jsx"; import { ImpressionStats } from "../../DiscoveryStreamImpressionStats/ImpressionStats"; diff --git a/browser/components/newtab/content-src/components/DiscoveryStreamComponents/FeatureHighlight/FeatureHighlight.jsx b/browser/components/newtab/content-src/components/DiscoveryStreamComponents/FeatureHighlight/FeatureHighlight.jsx index 792be40ba3..c650453393 100644 --- a/browser/components/newtab/content-src/components/DiscoveryStreamComponents/FeatureHighlight/FeatureHighlight.jsx +++ b/browser/components/newtab/content-src/components/DiscoveryStreamComponents/FeatureHighlight/FeatureHighlight.jsx @@ -3,7 +3,7 @@ * You can obtain one at http://mozilla.org/MPL/2.0/. */ import React, { useState, useCallback, useRef, useEffect } from "react"; -import { actionCreators as ac } from "common/Actions.sys.mjs"; +import { actionCreators as ac } from "common/Actions.mjs"; export function FeatureHighlight({ message, diff --git a/browser/components/newtab/content-src/components/DiscoveryStreamComponents/Navigation/Navigation.jsx b/browser/components/newtab/content-src/components/DiscoveryStreamComponents/Navigation/Navigation.jsx index 1062c3cade..43865c177c 100644 --- a/browser/components/newtab/content-src/components/DiscoveryStreamComponents/Navigation/Navigation.jsx +++ b/browser/components/newtab/content-src/components/DiscoveryStreamComponents/Navigation/Navigation.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 { actionCreators as ac } from "common/Actions.sys.mjs"; +import { actionCreators as ac } from "common/Actions.mjs"; import React from "react"; import { SafeAnchor } from "../SafeAnchor/SafeAnchor"; import { FluentOrText } from "content-src/components/FluentOrText/FluentOrText"; diff --git a/browser/components/newtab/content-src/components/DiscoveryStreamComponents/SafeAnchor/SafeAnchor.jsx b/browser/components/newtab/content-src/components/DiscoveryStreamComponents/SafeAnchor/SafeAnchor.jsx index 72ec94e1fe..b586730713 100644 --- a/browser/components/newtab/content-src/components/DiscoveryStreamComponents/SafeAnchor/SafeAnchor.jsx +++ b/browser/components/newtab/content-src/components/DiscoveryStreamComponents/SafeAnchor/SafeAnchor.jsx @@ -2,10 +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 { - actionCreators as ac, - actionTypes as at, -} from "common/Actions.sys.mjs"; +import { actionCreators as ac, actionTypes as at } from "common/Actions.mjs"; import React from "react"; export class SafeAnchor extends React.PureComponent { diff --git a/browser/components/newtab/content-src/components/DiscoveryStreamComponents/TopicsWidget/TopicsWidget.jsx b/browser/components/newtab/content-src/components/DiscoveryStreamComponents/TopicsWidget/TopicsWidget.jsx index 1fe2343b94..59b44198a2 100644 --- a/browser/components/newtab/content-src/components/DiscoveryStreamComponents/TopicsWidget/TopicsWidget.jsx +++ b/browser/components/newtab/content-src/components/DiscoveryStreamComponents/TopicsWidget/TopicsWidget.jsx @@ -3,7 +3,7 @@ * You can obtain one at http://mozilla.org/MPL/2.0/. */ import React from "react"; -import { actionCreators as ac } from "common/Actions.sys.mjs"; +import { actionCreators as ac } from "common/Actions.mjs"; import { SafeAnchor } from "../SafeAnchor/SafeAnchor"; import { ImpressionStats } from "../../DiscoveryStreamImpressionStats/ImpressionStats"; import { connect } from "react-redux"; diff --git a/browser/components/newtab/content-src/components/DiscoveryStreamImpressionStats/ImpressionStats.jsx b/browser/components/newtab/content-src/components/DiscoveryStreamImpressionStats/ImpressionStats.jsx index 1eb4863271..9342fcd27a 100644 --- a/browser/components/newtab/content-src/components/DiscoveryStreamImpressionStats/ImpressionStats.jsx +++ b/browser/components/newtab/content-src/components/DiscoveryStreamImpressionStats/ImpressionStats.jsx @@ -2,10 +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 { - actionCreators as ac, - actionTypes as at, -} from "common/Actions.sys.mjs"; +import { actionCreators as ac, actionTypes as at } from "common/Actions.mjs"; import { TOP_SITES_SOURCE } from "../TopSites/TopSitesConstants"; import React from "react"; @@ -100,7 +97,9 @@ export class ImpressionStats extends React.PureComponent { type: this.props.flightId ? "spoc" : "organic", ...(link.shim ? { shim: link.shim } : {}), recommendation_id: link.recommendation_id, + fetchTimestamp: link.fetchTimestamp, })), + firstVisibleTimestamp: this.props.firstVisibleTimestamp, }) ); this.impressionCardGuids = cards.map(link => link.id); @@ -244,8 +243,8 @@ export class ImpressionStats extends React.PureComponent { } ImpressionStats.defaultProps = { - IntersectionObserver: global.IntersectionObserver, - document: global.document, + IntersectionObserver: globalThis.IntersectionObserver, + document: globalThis.document, rows: [], source: "", }; diff --git a/browser/components/newtab/content-src/components/LinkMenu/LinkMenu.jsx b/browser/components/newtab/content-src/components/LinkMenu/LinkMenu.jsx index 650a03eb95..65b1f38623 100644 --- a/browser/components/newtab/content-src/components/LinkMenu/LinkMenu.jsx +++ b/browser/components/newtab/content-src/components/LinkMenu/LinkMenu.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 { actionCreators as ac } from "common/Actions.sys.mjs"; +import { actionCreators as ac } from "common/Actions.mjs"; import { connect } from "react-redux"; import { ContextMenu } from "content-src/components/ContextMenu/ContextMenu"; import { LinkMenuOptions } from "content-src/lib/link-menu-options"; diff --git a/browser/components/newtab/content-src/components/ModalOverlay/ModalOverlay.jsx b/browser/components/newtab/content-src/components/ModalOverlay/ModalOverlay.jsx index fdfdf22db2..5d902b43ba 100644 --- a/browser/components/newtab/content-src/components/ModalOverlay/ModalOverlay.jsx +++ b/browser/components/newtab/content-src/components/ModalOverlay/ModalOverlay.jsx @@ -53,4 +53,4 @@ export class ModalOverlayWrapper extends React.PureComponent { } } -ModalOverlayWrapper.defaultProps = { document: global.document }; +ModalOverlayWrapper.defaultProps = { document: globalThis.document }; diff --git a/browser/components/newtab/content-src/components/Search/Search.jsx b/browser/components/newtab/content-src/components/Search/Search.jsx index 64308963c9..ef7a3757d3 100644 --- a/browser/components/newtab/content-src/components/Search/Search.jsx +++ b/browser/components/newtab/content-src/components/Search/Search.jsx @@ -4,10 +4,7 @@ /* globals ContentSearchUIController, ContentSearchHandoffUIController */ -import { - actionCreators as ac, - actionTypes as at, -} from "common/Actions.sys.mjs"; +import { actionCreators as ac, actionTypes as at } from "common/Actions.mjs"; import { connect } from "react-redux"; import { IS_NEWTAB } from "content-src/lib/constants"; import React from "react"; diff --git a/browser/components/newtab/content-src/components/Sections/Sections.jsx b/browser/components/newtab/content-src/components/Sections/Sections.jsx index e72e9145ad..01b50f6918 100644 --- a/browser/components/newtab/content-src/components/Sections/Sections.jsx +++ b/browser/components/newtab/content-src/components/Sections/Sections.jsx @@ -2,10 +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 { - actionCreators as ac, - actionTypes as at, -} from "common/Actions.sys.mjs"; +import { actionCreators as ac, actionTypes as at } from "common/Actions.mjs"; import { Card, PlaceholderCard } from "content-src/components/Card/Card"; import { CollapsibleSection } from "content-src/components/CollapsibleSection/CollapsibleSection"; import { ComponentPerfTimer } from "content-src/components/ComponentPerfTimer/ComponentPerfTimer"; @@ -33,7 +30,7 @@ export class Section extends React.PureComponent { let cardsPerRow = CARDS_PER_ROW_DEFAULT; if ( props.compactCards && - global.matchMedia(`(min-width: 1072px)`).matches + globalThis.matchMedia(`(min-width: 1072px)`).matches ) { // If the section has compact cards and the viewport is wide enough, we show // 4 columns instead of 3. @@ -326,7 +323,7 @@ export class Section extends React.PureComponent { } Section.defaultProps = { - document: global.document, + document: globalThis.document, rows: [], emptyState: {}, pref: {}, diff --git a/browser/components/newtab/content-src/components/TopSites/SearchShortcutsForm.jsx b/browser/components/newtab/content-src/components/TopSites/SearchShortcutsForm.jsx index 4324c019f6..2d504c52ab 100644 --- a/browser/components/newtab/content-src/components/TopSites/SearchShortcutsForm.jsx +++ b/browser/components/newtab/content-src/components/TopSites/SearchShortcutsForm.jsx @@ -2,10 +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 { - actionCreators as ac, - actionTypes as at, -} from "common/Actions.sys.mjs"; +import { actionCreators as ac, actionTypes as at } from "common/Actions.mjs"; import React from "react"; import { TOP_SITES_SOURCE } from "./TopSitesConstants"; diff --git a/browser/components/newtab/content-src/components/TopSites/TopSite.jsx b/browser/components/newtab/content-src/components/TopSites/TopSite.jsx index c0932104af..3d63398e0e 100644 --- a/browser/components/newtab/content-src/components/TopSites/TopSite.jsx +++ b/browser/components/newtab/content-src/components/TopSites/TopSite.jsx @@ -2,10 +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 { - actionCreators as ac, - actionTypes as at, -} from "common/Actions.sys.mjs"; +import { actionCreators as ac, actionTypes as at } from "common/Actions.mjs"; import { MIN_RICH_FAVICON_SIZE, MIN_SMALL_FAVICON_SIZE, diff --git a/browser/components/newtab/content-src/components/TopSites/TopSiteForm.jsx b/browser/components/newtab/content-src/components/TopSites/TopSiteForm.jsx index 7dd61bdc93..9ca8991735 100644 --- a/browser/components/newtab/content-src/components/TopSites/TopSiteForm.jsx +++ b/browser/components/newtab/content-src/components/TopSites/TopSiteForm.jsx @@ -2,10 +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 { - actionCreators as ac, - actionTypes as at, -} from "common/Actions.sys.mjs"; +import { actionCreators as ac, actionTypes as at } from "common/Actions.mjs"; import { A11yLinkButton } from "content-src/components/A11yLinkButton/A11yLinkButton"; import React from "react"; import { TOP_SITES_SOURCE } from "./TopSitesConstants"; diff --git a/browser/components/newtab/content-src/components/TopSites/TopSiteImpressionWrapper.jsx b/browser/components/newtab/content-src/components/TopSites/TopSiteImpressionWrapper.jsx index 580809dd57..b654a803c7 100644 --- a/browser/components/newtab/content-src/components/TopSites/TopSiteImpressionWrapper.jsx +++ b/browser/components/newtab/content-src/components/TopSites/TopSiteImpressionWrapper.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 { actionCreators as ac } from "common/Actions.sys.mjs"; +import { actionCreators as ac } from "common/Actions.mjs"; import React from "react"; const VISIBLE = "visible"; @@ -142,8 +142,8 @@ export class TopSiteImpressionWrapper extends React.PureComponent { } TopSiteImpressionWrapper.defaultProps = { - IntersectionObserver: global.IntersectionObserver, - document: global.document, + IntersectionObserver: globalThis.IntersectionObserver, + document: globalThis.document, actionType: null, tile: null, }; diff --git a/browser/components/newtab/content-src/components/TopSites/TopSites.jsx b/browser/components/newtab/content-src/components/TopSites/TopSites.jsx index ba7676fd10..d9a12aa97d 100644 --- a/browser/components/newtab/content-src/components/TopSites/TopSites.jsx +++ b/browser/components/newtab/content-src/components/TopSites/TopSites.jsx @@ -2,10 +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 { - actionCreators as ac, - actionTypes as at, -} from "common/Actions.sys.mjs"; +import { actionCreators as ac, actionTypes as at } from "common/Actions.mjs"; import { MIN_RICH_FAVICON_SIZE, TOP_SITES_SOURCE } from "./TopSitesConstants"; import { CollapsibleSection } from "content-src/components/CollapsibleSection/CollapsibleSection"; import { ComponentPerfTimer } from "content-src/components/ComponentPerfTimer/ComponentPerfTimer"; @@ -93,7 +90,7 @@ export class _TopSites extends React.PureComponent { // We hide 2 sites per row when not in the wide layout. let sitesPerRow = TOP_SITES_MAX_SITES_PER_ROW; // $break-point-widest = 1072px (from _variables.scss) - if (!global.matchMedia(`(min-width: 1072px)`).matches) { + if (!globalThis.matchMedia(`(min-width: 1072px)`).matches) { sitesPerRow -= 2; } return this.props.TopSites.rows.slice( diff --git a/browser/components/newtab/content-src/components/TopSites/TopSitesConstants.js b/browser/components/newtab/content-src/components/TopSites/TopSitesConstants.mjs index f488896238..f488896238 100644 --- a/browser/components/newtab/content-src/components/TopSites/TopSitesConstants.js +++ b/browser/components/newtab/content-src/components/TopSites/TopSitesConstants.mjs diff --git a/browser/components/newtab/content-src/components/WallpapersSection/WallpapersSection.jsx b/browser/components/newtab/content-src/components/WallpapersSection/WallpapersSection.jsx new file mode 100644 index 0000000000..0b51a146f5 --- /dev/null +++ b/browser/components/newtab/content-src/components/WallpapersSection/WallpapersSection.jsx @@ -0,0 +1,100 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * 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 from "react"; +import { connect } from "react-redux"; + +export class _WallpapersSection extends React.PureComponent { + constructor(props) { + super(props); + this.handleChange = this.handleChange.bind(this); + this.handleReset = this.handleReset.bind(this); + this.prefersHighContrastQuery = null; + this.prefersDarkQuery = null; + } + + componentDidMount() { + this.prefersDarkQuery = globalThis.matchMedia( + "(prefers-color-scheme: dark)" + ); + } + + handleChange(event) { + const { id } = event.target; + const prefs = this.props.Prefs.values; + const colorMode = this.prefersDarkQuery?.matches ? "dark" : "light"; + this.props.setPref(`newtabWallpapers.wallpaper-${colorMode}`, id); + // bug 1892095 + if ( + prefs["newtabWallpapers.wallpaper-dark"] === "" && + colorMode === "light" + ) { + this.props.setPref( + "newtabWallpapers.wallpaper-dark", + id.replace("light", "dark") + ); + } + + if ( + prefs["newtabWallpapers.wallpaper-light"] === "" && + colorMode === "dark" + ) { + this.props.setPref( + `newtabWallpapers.wallpaper-light`, + id.replace("dark", "light") + ); + } + } + + handleReset() { + const colorMode = this.prefersDarkQuery?.matches ? "dark" : "light"; + this.props.setPref(`newtabWallpapers.wallpaper-${colorMode}`, ""); + } + + render() { + const { wallpaperList } = this.props.Wallpapers; + const { activeWallpaper } = this.props; + return ( + <div> + <fieldset className="wallpaper-list"> + {wallpaperList.map(({ title, theme, fluent_id }) => { + return ( + <> + <input + onChange={this.handleChange} + type="radio" + name={`wallpaper-${title}`} + id={title} + value={title} + checked={title === activeWallpaper} + aria-checked={title === activeWallpaper} + className={`wallpaper-input theme-${theme} ${title}`} + /> + <label + htmlFor={title} + className="sr-only" + data-l10n-id={fluent_id} + > + {fluent_id} + </label> + </> + ); + })} + </fieldset> + <button + className="wallpapers-reset" + onClick={this.handleReset} + data-l10n-id="newtab-wallpaper-reset" + /> + </div> + ); + } +} + +export const WallpapersSection = connect(state => { + return { + Wallpapers: state.Wallpapers, + Prefs: state.Prefs, + }; +})(_WallpapersSection); diff --git a/browser/components/newtab/content-src/components/WallpapersSection/_WallpapersSection.scss b/browser/components/newtab/content-src/components/WallpapersSection/_WallpapersSection.scss new file mode 100644 index 0000000000..689661750b --- /dev/null +++ b/browser/components/newtab/content-src/components/WallpapersSection/_WallpapersSection.scss @@ -0,0 +1,87 @@ +.wallpaper-list { + display: grid; + gap: 16px; + grid-template-columns: 1fr 1fr 1fr; + grid-auto-rows: 86px; + margin: 16px 0; + padding: 0; + border: none; + + .wallpaper-input, + .sr-only { + &.theme-light { + display: inline-block; + + @include dark-theme-only { + display: none; + } + } + + &.theme-dark { + display: none; + + @include dark-theme-only { + display: inline-block; + } + } + } + + .wallpaper-input { + appearance: none; + margin: 0; + padding: 0; + height: 86px; + width: 100%; + box-shadow: $shadow-secondary; + border-radius: 8px; + background-clip: content-box; + background-repeat: no-repeat; + background-size: cover; + cursor: pointer; + outline: 2px solid transparent; + + $wallpapers: dark-landscape, dark-color, dark-mountain, dark-panda, dark-sky, dark-beach, light-beach, light-color, light-landscape, light-mountain, light-panda, light-sky; + + @each $wallpaper in $wallpapers { + &.#{$wallpaper} { + background-image: url('chrome://activity-stream/content/data/content/assets/wallpapers/#{$wallpaper}.avif') + } + } + + &:checked { + outline-color: var(--color-accent-primary-active); + } + + &:focus-visible { + outline-color: var(--newtab-primary-action-background); + } + + &:hover { + filter: brightness(55%); + outline-color: transparent; + } + } + + // visually hide label, but still read by screen readers + .sr-only { + opacity: 0; + overflow: hidden; + position: absolute; + pointer-events: none; + } +} + +.wallpapers-reset { + background: none; + border: none; + text-decoration: underline; + margin-inline: auto; + display: block; + font-size: var(--font-size-small); + color: var(--newtab-text-primary-color); + cursor: pointer; + + &:hover { + text-decoration: none; + } +} diff --git a/browser/components/newtab/content-src/lib/constants.js b/browser/components/newtab/content-src/lib/constants.mjs index 2c96160b4b..4f07a77e29 100644 --- a/browser/components/newtab/content-src/lib/constants.js +++ b/browser/components/newtab/content-src/lib/constants.mjs @@ -3,7 +3,7 @@ * You can obtain one at http://mozilla.org/MPL/2.0/. */ export const IS_NEWTAB = - global.document && global.document.documentURI === "about:newtab"; + globalThis.document && globalThis.document.documentURI === "about:newtab"; export const NEWTAB_DARK_THEME = { ntp_background: { r: 42, diff --git a/browser/components/newtab/content-src/lib/detect-user-session-start.js b/browser/components/newtab/content-src/lib/detect-user-session-start.mjs index 43aa388967..d4c36efd4a 100644 --- a/browser/components/newtab/content-src/lib/detect-user-session-start.js +++ b/browser/components/newtab/content-src/lib/detect-user-session-start.mjs @@ -5,8 +5,8 @@ import { actionCreators as ac, actionTypes as at, -} from "common/Actions.sys.mjs"; -import { perfService as perfSvc } from "content-src/lib/perf-service"; +} from "../../common/Actions.mjs"; +import { perfService as perfSvc } from "./perf-service.mjs"; const VISIBLE = "visible"; const VISIBILITY_CHANGE_EVENT = "visibilitychange"; @@ -15,7 +15,7 @@ export class DetectUserSessionStart { constructor(store, options = {}) { this._store = store; // Overrides for testing - this.document = options.document || global.document; + this.document = options.document || globalThis.document; this._perfService = options.perfService || perfSvc; this._onVisibilityChange = this._onVisibilityChange.bind(this); } diff --git a/browser/components/newtab/content-src/lib/init-store.js b/browser/components/newtab/content-src/lib/init-store.mjs index f0ab2db86a..85b3b0b470 100644 --- a/browser/components/newtab/content-src/lib/init-store.js +++ b/browser/components/newtab/content-src/lib/init-store.mjs @@ -8,7 +8,10 @@ import { actionCreators as ac, actionTypes as at, actionUtils as au, -} from "common/Actions.sys.mjs"; +} from "../../common/Actions.mjs"; +// We disable import checking here as redux is installed via the npm packages +// at the newtab level, rather than in the top-level package.json. +// eslint-disable-next-line import/no-unresolved import { applyMiddleware, combineReducers, createStore } from "redux"; export const MERGE_STORE_ACTION = "NEW_TAB_INITIAL_STATE"; @@ -117,12 +120,12 @@ export function initStore(reducers, initialState) { const store = createStore( mergeStateReducer(combineReducers(reducers)), initialState, - global.RPMAddMessageListener && + globalThis.RPMAddMessageListener && applyMiddleware(rehydrationMiddleware, messageMiddleware) ); - if (global.RPMAddMessageListener) { - global.RPMAddMessageListener(INCOMING_MESSAGE_NAME, msg => { + if (globalThis.RPMAddMessageListener) { + globalThis.RPMAddMessageListener(INCOMING_MESSAGE_NAME, msg => { try { store.dispatch(msg.data); } catch (ex) { diff --git a/browser/components/newtab/content-src/lib/link-menu-options.js b/browser/components/newtab/content-src/lib/link-menu-options.mjs index 12e47259c1..f10a5e34c6 100644 --- a/browser/components/newtab/content-src/lib/link-menu-options.js +++ b/browser/components/newtab/content-src/lib/link-menu-options.mjs @@ -5,7 +5,7 @@ import { actionCreators as ac, actionTypes as at, -} from "common/Actions.sys.mjs"; +} from "../../common/Actions.mjs"; const _OpenInPrivateWindow = site => ({ id: "newtab-menu-open-new-private-window", diff --git a/browser/components/newtab/content-src/lib/perf-service.js b/browser/components/newtab/content-src/lib/perf-service.mjs index 6ea99ce877..25fc430726 100644 --- a/browser/components/newtab/content-src/lib/perf-service.js +++ b/browser/components/newtab/content-src/lib/perf-service.mjs @@ -2,8 +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/. */ -"use strict"; - let usablePerfObj = window.performance; export function _PerfService(options) { @@ -37,8 +35,8 @@ _PerfService.prototype = { * @param {String} type eg "mark" * @return {Array} Performance* objects */ - getEntriesByName: function getEntriesByName(name, type) { - return this._perf.getEntriesByName(name, type); + getEntriesByName: function getEntriesByName(entryName, type) { + return this._perf.getEntriesByName(entryName, type); }, /** @@ -89,11 +87,11 @@ _PerfService.prototype = { * See [bug 1369303](https://bugzilla.mozilla.org/show_bug.cgi?id=1369303) * for more info. */ - getMostRecentAbsMarkStartByName(name) { - let entries = this.getEntriesByName(name, "mark"); + getMostRecentAbsMarkStartByName(entryName) { + let entries = this.getEntriesByName(entryName, "mark"); if (!entries.length) { - throw new Error(`No marks with the name ${name}`); + throw new Error(`No marks with the name ${entryName}`); } let mostRecentEntry = entries[entries.length - 1]; diff --git a/browser/components/newtab/content-src/lib/screenshot-utils.js b/browser/components/newtab/content-src/lib/screenshot-utils.mjs index 7ea93f12ae..2d1342be4f 100644 --- a/browser/components/newtab/content-src/lib/screenshot-utils.js +++ b/browser/components/newtab/content-src/lib/screenshot-utils.mjs @@ -30,7 +30,7 @@ export const ScreenshotUtils = { } if (this.isBlob(false, remoteImage)) { return { - url: global.URL.createObjectURL(remoteImage.data), + url: globalThis.URL.createObjectURL(remoteImage.data), path: remoteImage.path, }; } @@ -41,7 +41,7 @@ export const ScreenshotUtils = { // This should always be called with a local image and not a remote image. maybeRevokeBlobObjectURL(localImage) { if (this.isBlob(true, localImage)) { - global.URL.revokeObjectURL(localImage.url); + globalThis.URL.revokeObjectURL(localImage.url); } }, diff --git a/browser/components/newtab/content-src/lib/selectLayoutRender.js b/browser/components/newtab/content-src/lib/selectLayoutRender.mjs index 8ef4dd428f..8ef4dd428f 100644 --- a/browser/components/newtab/content-src/lib/selectLayoutRender.js +++ b/browser/components/newtab/content-src/lib/selectLayoutRender.mjs diff --git a/browser/components/newtab/content-src/styles/_activity-stream.scss b/browser/components/newtab/content-src/styles/_activity-stream.scss index 88ed530b6a..d2e66667b2 100644 --- a/browser/components/newtab/content-src/styles/_activity-stream.scss +++ b/browser/components/newtab/content-src/styles/_activity-stream.scss @@ -21,6 +21,17 @@ body { background-color: var(--newtab-background-color); font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Ubuntu, 'Helvetica Neue', sans-serif; font-size: 16px; + + // rules for HNT wallpapers + background-repeat: no-repeat; + background-size: cover; + background-position: center; + background-attachment: fixed; + background-image: var(--newtab-wallpaper-light, ''); + + @media (prefers-color-scheme: dark) { + background-image: var(--newtab-wallpaper-dark, ''); + } } .no-scroll { @@ -137,6 +148,7 @@ input { @import '../components/ContextMenu/ContextMenu'; @import '../components/ConfirmDialog/ConfirmDialog'; @import '../components/CustomizeMenu/CustomizeMenu'; +@import '../components/WallpapersSection/WallpapersSection'; @import '../components/Card/Card'; @import '../components/CollapsibleSection/CollapsibleSection'; @import '../components/DiscoveryStreamAdmin/DiscoveryStreamAdmin'; |