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>
---
 .../aboutwelcome/components/LanguageSwitcher.jsx   | 294 +++++++++++++++++++++
 1 file changed, 294 insertions(+)
 create mode 100644 browser/components/newtab/content-src/aboutwelcome/components/LanguageSwitcher.jsx

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

diff --git a/browser/components/newtab/content-src/aboutwelcome/components/LanguageSwitcher.jsx b/browser/components/newtab/content-src/aboutwelcome/components/LanguageSwitcher.jsx
new file mode 100644
index 0000000000..9abd749a6d
--- /dev/null
+++ b/browser/components/newtab/content-src/aboutwelcome/components/LanguageSwitcher.jsx
@@ -0,0 +1,294 @@
+/* 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, { useState, useEffect } from "react";
+import { Localized } from "./MSLocalized";
+import { AboutWelcomeUtils } from "../../lib/aboutwelcome-utils";
+
+/**
+ * The language switcher implements a hook that should be placed at a higher level
+ * than the actual language switcher component, as it needs to preemptively fetch
+ * and install langpacks for the user if there is a language mismatch screen.
+ */
+export function useLanguageSwitcher(
+  appAndSystemLocaleInfo,
+  screens,
+  screenIndex,
+  setScreenIndex
+) {
+  const languageMismatchScreenIndex = screens.findIndex(
+    ({ id }) => id === "AW_LANGUAGE_MISMATCH"
+  );
+  const screen = screens[languageMismatchScreenIndex];
+
+  // Ensure fluent messages have the negotiatedLanguage args set, as they are rendered
+  // before the negotiatedLanguage is known. If the arg isn't present then Firefox will
+  // crash in development mode.
+  useEffect(() => {
+    if (screen?.content?.languageSwitcher) {
+      for (const text of Object.values(screen.content.languageSwitcher)) {
+        if (text?.args && text.args.negotiatedLanguage === undefined) {
+          text.args.negotiatedLanguage = "";
+        }
+      }
+    }
+  }, [screen]);
+
+  // If there is a mismatch, then Firefox can negotiate a better langpack to offer
+  // the user.
+  const [negotiatedLanguage, setNegotiatedLanguage] = useState(null);
+  useEffect(
+    function getNegotiatedLanguage() {
+      if (!appAndSystemLocaleInfo) {
+        return;
+      }
+      if (appAndSystemLocaleInfo.matchType !== "language-mismatch") {
+        // There is no language mismatch, so there is no need to negotiate a langpack.
+        return;
+      }
+
+      (async () => {
+        const { langPack, langPackDisplayName } =
+          await window.AWNegotiateLangPackForLanguageMismatch(
+            appAndSystemLocaleInfo
+          );
+        if (langPack) {
+          setNegotiatedLanguage({
+            langPackDisplayName,
+            appDisplayName: appAndSystemLocaleInfo.displayNames.appLanguage,
+            langPack,
+            requestSystemLocales: [
+              langPack.target_locale,
+              appAndSystemLocaleInfo.appLocaleRaw,
+            ],
+            originalAppLocales: [appAndSystemLocaleInfo.appLocaleRaw],
+          });
+        } else {
+          setNegotiatedLanguage({
+            langPackDisplayName: null,
+            appDisplayName: null,
+            langPack: null,
+            requestSystemLocales: null,
+          });
+        }
+      })();
+    },
+    [appAndSystemLocaleInfo]
+  );
+
+  /**
+   * @type {
+   *  "before-installation"
+   *  | "installing"
+   *  | "installed"
+   *  | "installation-error"
+   *  | "none-available"
+   * }
+   */
+  const [langPackInstallPhase, setLangPackInstallPhase] = useState(
+    "before-installation"
+  );
+  useEffect(
+    function ensureLangPackInstalled() {
+      if (!negotiatedLanguage) {
+        // There are no negotiated languages to download yet.
+        return;
+      }
+      setLangPackInstallPhase("installing");
+      window
+        .AWEnsureLangPackInstalled(negotiatedLanguage, screen?.content)
+        .then(
+          content => {
+            // Update screen content with strings that might have changed.
+            screen.content = content;
+            setLangPackInstallPhase("installed");
+          },
+          error => {
+            console.error(error);
+            setLangPackInstallPhase("installation-error");
+          }
+        );
+    },
+    [negotiatedLanguage]
+  );
+
+  const [languageFilteredScreens, setLanguageFilteredScreens] =
+    useState(screens);
+  useEffect(
+    function filterScreen() {
+      // Remove the language screen if it exists (already removed for no live
+      // reload) and we either don't-need-to or can't switch.
+      if (
+        screen &&
+        (appAndSystemLocaleInfo?.matchType !== "language-mismatch" ||
+          negotiatedLanguage?.langPack === null)
+      ) {
+        if (screenIndex > languageMismatchScreenIndex) {
+          setScreenIndex(screenIndex - 1);
+        }
+        setLanguageFilteredScreens(
+          screens.filter(s => s.id !== "AW_LANGUAGE_MISMATCH")
+        );
+      } else {
+        setLanguageFilteredScreens(screens);
+      }
+    },
+    [screens, negotiatedLanguage]
+  );
+
+  return {
+    negotiatedLanguage,
+    langPackInstallPhase,
+    languageFilteredScreens,
+  };
+}
+
+/**
+ * The language switcher is a separate component as it needs to perform some asynchronous
+ * network actions such as retrieving the list of langpacks available, and downloading
+ * a new langpack. On a fast connection, this won't be noticeable, but on slow or unreliable
+ * internet this may fail for a user.
+ */
+export function LanguageSwitcher(props) {
+  const {
+    content,
+    handleAction,
+    negotiatedLanguage,
+    langPackInstallPhase,
+    messageId,
+  } = props;
+
+  const [isAwaitingLangpack, setIsAwaitingLangpack] = useState(false);
+
+  // Determine the status of the langpack installation.
+  useEffect(() => {
+    if (isAwaitingLangpack && langPackInstallPhase !== "installing") {
+      window.AWSetRequestedLocales(negotiatedLanguage.requestSystemLocales);
+      requestAnimationFrame(() => {
+        handleAction(
+          // Simulate the click event.
+          { currentTarget: { value: "download_complete" } }
+        );
+      });
+    }
+  }, [isAwaitingLangpack, langPackInstallPhase]);
+
+  let showWaitingScreen = false;
+  let showPreloadingScreen = false;
+  let showReadyScreen = false;
+
+  if (isAwaitingLangpack && langPackInstallPhase !== "installed") {
+    showWaitingScreen = true;
+  } else if (langPackInstallPhase === "before-installation") {
+    showPreloadingScreen = true;
+  } else {
+    showReadyScreen = true;
+  }
+
+  // Use {display: "none"} rather than if statements to prevent layout thrashing with
+  // the localized text elements rendering as blank, then filling in the text.
+  return (
+    <div className="action-buttons language-switcher-container">
+      {/* Pre-loading screen */}
+      <div style={{ display: showPreloadingScreen ? "block" : "none" }}>
+        <button
+          className="primary"
+          value="primary_button"
+          disabled={true}
+          type="button"
+        >
+          <img
+            className="language-loader"
+            src="chrome://browser/skin/tabbrowser/tab-connecting.png"
+            alt=""
+          />
+          <Localized text={content.languageSwitcher.waiting} />
+        </button>
+        <div className="secondary-cta">
+          <Localized text={content.languageSwitcher.skip}>
+            <button
+              value="decline_waiting"
+              type="button"
+              className="secondary text-link arrow-icon"
+              onClick={handleAction}
+            />
+          </Localized>
+        </div>
+      </div>
+      {/* Waiting to download the language screen. */}
+      <div style={{ display: showWaitingScreen ? "block" : "none" }}>
+        <button
+          className="primary"
+          value="primary_button"
+          disabled={true}
+          type="button"
+        >
+          <img
+            className="language-loader"
+            src="chrome://browser/skin/tabbrowser/tab-connecting.png"
+            alt=""
+          />
+          <Localized text={content.languageSwitcher.downloading} />
+        </button>
+        <div className="secondary-cta">
+          <Localized text={content.languageSwitcher.cancel}>
+            <button
+              type="button"
+              className="secondary text-link"
+              onClick={() => {
+                setIsAwaitingLangpack(false);
+                handleAction({
+                  currentTarget: { value: "cancel_waiting" },
+                });
+              }}
+            />
+          </Localized>
+        </div>
+      </div>
+      {/* The typical ready screen. */}
+      <div style={{ display: showReadyScreen ? "block" : "none" }}>
+        <div>
+          <button
+            className="primary"
+            value="primary_button"
+            onClick={() => {
+              AboutWelcomeUtils.sendActionTelemetry(
+                messageId,
+                "download_langpack"
+              );
+              setIsAwaitingLangpack(true);
+            }}
+          >
+            {content.languageSwitcher.switch ? (
+              <Localized text={content.languageSwitcher.switch} />
+            ) : (
+              // This is the localized name from the Intl.DisplayNames API.
+              negotiatedLanguage?.langPackDisplayName
+            )}
+          </button>
+        </div>
+        <div>
+          <button
+            type="button"
+            className="primary"
+            value="decline"
+            onClick={event => {
+              window.AWSetRequestedLocales(
+                negotiatedLanguage.originalAppLocales
+              );
+              handleAction(event);
+            }}
+          >
+            {content.languageSwitcher.continue ? (
+              <Localized text={content.languageSwitcher.continue} />
+            ) : (
+              // This is the localized name from the Intl.DisplayNames API.
+              negotiatedLanguage?.appDisplayName
+            )}
+          </button>
+        </div>
+      </div>
+    </div>
+  );
+}
-- 
cgit v1.2.3

