/* 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);