From 6bf0a5cb5034a7e684dcc3500e841785237ce2dd Mon Sep 17 00:00:00 2001
From: Daniel Baumann <daniel.baumann@progress-linux.org>
Date: Sun, 7 Apr 2024 19:32:43 +0200
Subject: Adding upstream version 1:115.7.0.

Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
---
 .../newtab/content-src/components/Base/Base.jsx    | 273 +++++++++++++++++++++
 1 file changed, 273 insertions(+)
 create mode 100644 browser/components/newtab/content-src/components/Base/Base.jsx

(limited to 'browser/components/newtab/content-src/components/Base/Base.jsx')

diff --git a/browser/components/newtab/content-src/components/Base/Base.jsx b/browser/components/newtab/content-src/components/Base/Base.jsx
new file mode 100644
index 0000000000..8845ad87cd
--- /dev/null
+++ b/browser/components/newtab/content-src/components/Base/Base.jsx
@@ -0,0 +1,273 @@
+/* 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 }) => (
+  <div className="prefs-button">
+    <button
+      className={`icon ${icon || "icon-settings"}`}
+      onClick={onClick}
+      data-l10n-id="newtab-settings-button"
+    />
+  </div>
+);
+
+// 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 (
+      <ErrorBoundary className="base-content-fallback">
+        <React.Fragment>
+          <BaseContent {...this.props} adminContent={this.state} />
+          {isDevtoolsEnabled ? (
+            <ASRouterAdmin notifyContent={this.notifyContent} />
+          ) : null}
+        </React.Fragment>
+      </ErrorBoundary>
+    );
+  }
+}
+
+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 (
+      <div>
+        <CustomizeMenu
+          onClose={this.closeCustomizationMenu}
+          onOpen={this.openCustomizationMenu}
+          openPreferences={this.openPreferences}
+          setPref={this.setPref}
+          enabledSections={enabledSections}
+          pocketRegion={pocketRegion}
+          mayHaveSponsoredTopSites={mayHaveSponsoredTopSites}
+          showing={customizeMenuVisible}
+        />
+        {/* eslint-disable-next-line jsx-a11y/click-events-have-key-events, jsx-a11y/no-static-element-interactions*/}
+        <div className={outerClassName} onClick={this.closeCustomizationMenu}>
+          <main className={hasSnippet ? "has-snippet" : ""}>
+            {prefs.showSearch && (
+              <div className="non-collapsible-section">
+                <ErrorBoundary>
+                  <Search
+                    showLogo={
+                      noSectionsEnabled || prefs["logowordmark.alwaysVisible"]
+                    }
+                    handoffEnabled={searchHandoffEnabled}
+                    {...props.Search}
+                  />
+                </ErrorBoundary>
+              </div>
+            )}
+            <ASRouterUISurface
+              adminContent={this.props.adminContent}
+              appUpdateChannel={this.props.Prefs.values.appUpdateChannel}
+              fxaEndpoint={this.props.Prefs.values.fxa_endpoint}
+              dispatch={this.props.dispatch}
+            />
+            <div className={`body-wrapper${initialized ? " on" : ""}`}>
+              {isDiscoveryStream ? (
+                <ErrorBoundary className="borderless-error">
+                  <DiscoveryStreamBase locale={props.App.locale} />
+                </ErrorBoundary>
+              ) : (
+                <Sections />
+              )}
+            </div>
+            <ConfirmDialog />
+          </main>
+        </div>
+      </div>
+    );
+  }
+}
+
+export const Base = connect(state => ({
+  App: state.App,
+  Prefs: state.Prefs,
+  Sections: state.Sections,
+  DiscoveryStream: state.DiscoveryStream,
+  Search: state.Search,
+}))(_Base);
-- 
cgit v1.2.3

