/* 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 { actionCreators as ac, actionTypes as at, } from "common/Actions.sys.mjs"; import { ASRouterAdmin } from "content-src/components/ASRouterAdmin/ASRouterAdmin"; import { ASRouterUISurface } from "../../asrouter/asrouter-content"; import { ConfirmDialog } from "content-src/components/ConfirmDialog/ConfirmDialog"; import { connect } from "react-redux"; import { DiscoveryStreamBase } from "content-src/components/DiscoveryStreamBase/DiscoveryStreamBase"; import { ErrorBoundary } from "content-src/components/ErrorBoundary/ErrorBoundary"; import { CustomizeMenu } from "content-src/components/CustomizeMenu/CustomizeMenu"; import React from "react"; import { Search } from "content-src/components/Search/Search"; import { Sections } from "content-src/components/Sections/Sections"; export const PrefsButton = ({ onClick, icon }) => (
); // Returns a function will not be continuously triggered when called. The // function will be triggered if called again after `wait` milliseconds. function debounce(func, wait) { let timer; return (...args) => { if (timer) { return; } let wakeUp = () => { timer = null; }; timer = setTimeout(wakeUp, wait); func.apply(this, args); }; } export class _Base extends React.PureComponent { constructor(props) { super(props); this.state = { message: {}, }; this.notifyContent = this.notifyContent.bind(this); } notifyContent(state) { this.setState(state); } componentWillUnmount() { this.updateTheme(); } componentWillUpdate() { this.updateTheme(); } updateTheme() { const bodyClassName = [ "activity-stream", // If we skipped the about:welcome overlay and removed the CSS classes // we don't want to add them back to the Activity Stream view document.body.classList.contains("inline-onboarding") ? "inline-onboarding" : "", ] .filter(v => v) .join(" "); global.document.body.className = bodyClassName; } render() { const { props } = this; const { App } = props; const isDevtoolsEnabled = props.Prefs.values["asrouter.devtoolsEnabled"]; if (!App.initialized) { return null; } return ( {isDevtoolsEnabled ? ( ) : null} ); } } export class BaseContent extends React.PureComponent { constructor(props) { super(props); this.openPreferences = this.openPreferences.bind(this); this.openCustomizationMenu = this.openCustomizationMenu.bind(this); this.closeCustomizationMenu = this.closeCustomizationMenu.bind(this); this.handleOnKeyDown = this.handleOnKeyDown.bind(this); this.onWindowScroll = debounce(this.onWindowScroll.bind(this), 5); this.setPref = this.setPref.bind(this); this.state = { fixedSearch: false }; } componentDidMount() { global.addEventListener("scroll", this.onWindowScroll); global.addEventListener("keydown", this.handleOnKeyDown); } componentWillUnmount() { global.removeEventListener("scroll", this.onWindowScroll); global.removeEventListener("keydown", this.handleOnKeyDown); } onWindowScroll() { const prefs = this.props.Prefs.values; const SCROLL_THRESHOLD = prefs["logowordmark.alwaysVisible"] ? 179 : 34; if (global.scrollY > SCROLL_THRESHOLD && !this.state.fixedSearch) { this.setState({ fixedSearch: true }); } else if (global.scrollY <= SCROLL_THRESHOLD && this.state.fixedSearch) { this.setState({ fixedSearch: false }); } } openPreferences() { this.props.dispatch(ac.OnlyToMain({ type: at.SETTINGS_OPEN })); this.props.dispatch(ac.UserEvent({ event: "OPEN_NEWTAB_PREFS" })); } openCustomizationMenu() { this.props.dispatch({ type: at.SHOW_PERSONALIZE }); this.props.dispatch(ac.UserEvent({ event: "SHOW_PERSONALIZE" })); } closeCustomizationMenu() { if (this.props.App.customizeMenuVisible) { this.props.dispatch({ type: at.HIDE_PERSONALIZE }); this.props.dispatch(ac.UserEvent({ event: "HIDE_PERSONALIZE" })); } } handleOnKeyDown(e) { if (e.key === "Escape") { this.closeCustomizationMenu(); } } setPref(pref, value) { this.props.dispatch(ac.SetPref(pref, value)); } render() { const { props } = this; const { App } = props; const { initialized, customizeMenuVisible } = App; const prefs = props.Prefs.values; const isDiscoveryStream = props.DiscoveryStream.config && props.DiscoveryStream.config.enabled; let filteredSections = props.Sections.filter( section => section.id !== "topstories" ); const pocketEnabled = prefs["feeds.section.topstories"] && prefs["feeds.system.topstories"]; const noSectionsEnabled = !prefs["feeds.topsites"] && !pocketEnabled && filteredSections.filter(section => section.enabled).length === 0; const searchHandoffEnabled = prefs["improvesearch.handoffToAwesomebar"]; const enabledSections = { topSitesEnabled: prefs["feeds.topsites"], pocketEnabled: prefs["feeds.section.topstories"], highlightsEnabled: prefs["feeds.section.highlights"], showSponsoredTopSitesEnabled: prefs.showSponsoredTopSites, showSponsoredPocketEnabled: prefs.showSponsored, showRecentSavesEnabled: prefs.showRecentSaves, topSitesRowsCount: prefs.topSitesRows, }; const pocketRegion = prefs["feeds.system.topstories"]; const { mayHaveSponsoredTopSites } = prefs; const outerClassName = [ "outer-wrapper", isDiscoveryStream && pocketEnabled && "ds-outer-wrapper-search-alignment", isDiscoveryStream && "ds-outer-wrapper-breakpoint-override", prefs.showSearch && this.state.fixedSearch && !noSectionsEnabled && "fixed-search", prefs.showSearch && noSectionsEnabled && "only-search", prefs["logowordmark.alwaysVisible"] && "visible-logo", ] .filter(v => v) .join(" "); const hasSnippet = prefs["feeds.snippets"] && this.props.adminContent && this.props.adminContent.message && this.props.adminContent.message.id; return (
{/* eslint-disable-next-line jsx-a11y/click-events-have-key-events, jsx-a11y/no-static-element-interactions*/}
{prefs.showSearch && (
)}
{isDiscoveryStream ? ( ) : ( )}
); } } export const Base = connect(state => ({ App: state.App, Prefs: state.Prefs, Sections: state.Sections, DiscoveryStream: state.DiscoveryStream, Search: state.Search, }))(_Base);