3505 lines
No EOL
148 KiB
JavaScript
3505 lines
No EOL
148 KiB
JavaScript
/*!
|
|
*
|
|
* NOTE: This file is generated by webpack from aboutwelcome.jsx
|
|
* using the npm bundle task.
|
|
*
|
|
*/
|
|
/******/ (() => { // webpackBootstrap
|
|
/******/ "use strict";
|
|
/******/ var __webpack_modules__ = ([
|
|
/* 0 */,
|
|
/* 1 */
|
|
/***/ ((module) => {
|
|
|
|
module.exports = React;
|
|
|
|
/***/ }),
|
|
/* 2 */
|
|
/***/ ((module) => {
|
|
|
|
module.exports = ReactDOM;
|
|
|
|
/***/ }),
|
|
/* 3 */
|
|
/***/ ((__unused_webpack___webpack_module__, __webpack_exports__, __webpack_require__) => {
|
|
|
|
__webpack_require__.r(__webpack_exports__);
|
|
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
|
|
/* harmony export */ AboutWelcomeUtils: () => (/* binding */ AboutWelcomeUtils),
|
|
/* harmony export */ DEFAULT_RTAMO_CONTENT: () => (/* binding */ DEFAULT_RTAMO_CONTENT)
|
|
/* harmony export */ });
|
|
/* 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/. */
|
|
|
|
// If the container has a "page" data attribute, then this is
|
|
// a Spotlight modal or Feature Callout. Otherwise, this is
|
|
// about:welcome and we should return the current page.
|
|
const page =
|
|
document.querySelector(
|
|
"#multi-stage-message-root.onboardingContainer[data-page]"
|
|
)?.dataset.page || document.location.href;
|
|
|
|
const AboutWelcomeUtils = {
|
|
handleUserAction(action) {
|
|
return window.AWSendToParent("SPECIAL_ACTION", action);
|
|
},
|
|
sendImpressionTelemetry(messageId, context) {
|
|
window.AWSendEventTelemetry?.({
|
|
event: "IMPRESSION",
|
|
event_context: {
|
|
...context,
|
|
page,
|
|
},
|
|
message_id: messageId,
|
|
});
|
|
},
|
|
sendActionTelemetry(messageId, elementId, eventName = "CLICK_BUTTON") {
|
|
const ping = {
|
|
event: eventName,
|
|
event_context: {
|
|
source: elementId,
|
|
page,
|
|
},
|
|
message_id: messageId,
|
|
};
|
|
window.AWSendEventTelemetry?.(ping);
|
|
},
|
|
sendDismissTelemetry(messageId, elementId) {
|
|
// Don't send DISMISS telemetry in spotlight modals since they already send
|
|
// their own equivalent telemetry.
|
|
if (page !== "spotlight") {
|
|
this.sendActionTelemetry(messageId, elementId, "DISMISS");
|
|
}
|
|
},
|
|
async fetchFlowParams(metricsFlowUri) {
|
|
let flowParams;
|
|
try {
|
|
const response = await fetch(metricsFlowUri, {
|
|
credentials: "omit",
|
|
});
|
|
if (response.status === 200) {
|
|
const { deviceId, flowId, flowBeginTime } = await response.json();
|
|
flowParams = { deviceId, flowId, flowBeginTime };
|
|
} else {
|
|
console.error("Non-200 response", response);
|
|
}
|
|
} catch (e) {
|
|
flowParams = null;
|
|
}
|
|
return flowParams;
|
|
},
|
|
sendEvent(type, detail) {
|
|
document.dispatchEvent(
|
|
new CustomEvent(`AWPage:${type}`, {
|
|
bubbles: true,
|
|
detail,
|
|
})
|
|
);
|
|
},
|
|
getLoadingStrategyFor(url) {
|
|
return url?.startsWith("http") ? "lazy" : "eager";
|
|
},
|
|
handleCampaignAction(action, messageId) {
|
|
window.AWSendToParent("HANDLE_CAMPAIGN_ACTION", action).then(handled => {
|
|
if (handled) {
|
|
this.sendActionTelemetry(messageId, "CAMPAIGN_ACTION");
|
|
}
|
|
});
|
|
},
|
|
getValidStyle(style, validStyles, allowVars) {
|
|
if (!style) {
|
|
return null;
|
|
}
|
|
return Object.keys(style)
|
|
.filter(
|
|
key => validStyles.includes(key) || (allowVars && key.startsWith("--"))
|
|
)
|
|
.reduce((obj, key) => {
|
|
obj[key] = style[key];
|
|
return obj;
|
|
}, {});
|
|
},
|
|
};
|
|
|
|
const DEFAULT_RTAMO_CONTENT = {
|
|
template: "return_to_amo",
|
|
utm_term: "rtamo",
|
|
content: {
|
|
position: "split",
|
|
title: { string_id: "mr1-return-to-amo-subtitle" },
|
|
has_noodles: false,
|
|
subtitle: {
|
|
string_id: "mr1-return-to-amo-addon-title",
|
|
},
|
|
backdrop:
|
|
"var(--mr-welcome-background-color) var(--mr-welcome-background-gradient)",
|
|
background:
|
|
"url('chrome://activity-stream/content/data/content/assets/mr-rtamo-background-image.svg') no-repeat center",
|
|
progress_bar: true,
|
|
primary_button: {
|
|
label: { string_id: "mr1-return-to-amo-add-extension-label" },
|
|
source_id: "ADD_EXTENSION_BUTTON",
|
|
action: {
|
|
type: "INSTALL_ADDON_FROM_URL",
|
|
data: { url: null, telemetrySource: "rtamo" },
|
|
},
|
|
},
|
|
secondary_button: {
|
|
label: {
|
|
string_id: "onboarding-not-now-button-label",
|
|
},
|
|
source_id: "RTAMO_START_BROWSING_BUTTON",
|
|
action: {
|
|
type: "OPEN_AWESOME_BAR",
|
|
},
|
|
},
|
|
secondary_button_top: {
|
|
label: {
|
|
string_id: "mr1-onboarding-sign-in-button-label",
|
|
},
|
|
source_id: "RTAMO_FXA_SIGNIN_BUTTON",
|
|
action: {
|
|
data: {
|
|
entrypoint: "activity-stream-firstrun",
|
|
where: "tab",
|
|
},
|
|
type: "SHOW_FIREFOX_ACCOUNTS",
|
|
addFlowParams: true,
|
|
},
|
|
},
|
|
},
|
|
};
|
|
|
|
|
|
/***/ }),
|
|
/* 4 */
|
|
/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
|
|
|
|
__webpack_require__.r(__webpack_exports__);
|
|
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
|
|
/* harmony export */ MultiStageAboutWelcome: () => (/* binding */ MultiStageAboutWelcome),
|
|
/* harmony export */ ProgressBar: () => (/* binding */ ProgressBar),
|
|
/* harmony export */ SecondaryCTA: () => (/* binding */ SecondaryCTA),
|
|
/* harmony export */ StepsIndicator: () => (/* binding */ StepsIndicator),
|
|
/* harmony export */ WelcomeScreen: () => (/* binding */ WelcomeScreen)
|
|
/* harmony export */ });
|
|
/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(1);
|
|
/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_0__);
|
|
/* harmony import */ var _MSLocalized__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(5);
|
|
/* harmony import */ var _lib_aboutwelcome_utils_mjs__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(3);
|
|
/* harmony import */ var _MultiStageProtonScreen__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(6);
|
|
/* harmony import */ var _LanguageSwitcher__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(7);
|
|
/* harmony import */ var _SubmenuButton__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(12);
|
|
/* harmony import */ var _lib_addUtmParams_mjs__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(22);
|
|
/* 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/. */
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Amount of milliseconds for all transitions to complete (including delays).
|
|
const TRANSITION_OUT_TIME = 1000;
|
|
const LANGUAGE_MISMATCH_SCREEN_ID = "AW_LANGUAGE_MISMATCH";
|
|
const MultiStageAboutWelcome = props => {
|
|
let {
|
|
defaultScreens
|
|
} = props;
|
|
const didFilter = (0,react__WEBPACK_IMPORTED_MODULE_0__.useRef)(false);
|
|
const [didMount, setDidMount] = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)(false);
|
|
const [screens, setScreens] = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)(defaultScreens);
|
|
const [index, setScreenIndex] = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)(props.startScreen);
|
|
const [previousOrder, setPreviousOrder] = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)(props.startScreen - 1);
|
|
(0,react__WEBPACK_IMPORTED_MODULE_0__.useEffect)(() => {
|
|
(async () => {
|
|
// If we want to load index from history state, we don't want to send impression yet
|
|
if (!didMount) {
|
|
return;
|
|
}
|
|
// On about:welcome first load, screensVisited should be empty
|
|
let screensVisited = didFilter.current ? screens.slice(0, index) : [];
|
|
let upcomingScreens = defaultScreens.filter(s => !screensVisited.find(v => v.id === s.id))
|
|
// Filter out Language Mismatch screen from upcoming
|
|
// screens if screens set from useLanguageSwitcher hook
|
|
// has filtered language screen
|
|
.filter(upcomingScreen => !(!screens.find(s => s.id === LANGUAGE_MISMATCH_SCREEN_ID) && upcomingScreen.id === LANGUAGE_MISMATCH_SCREEN_ID));
|
|
let filteredScreens = screensVisited.concat((await window.AWEvaluateScreenTargeting(upcomingScreens)) ?? upcomingScreens);
|
|
|
|
// Use existing screen for the filtered screen to carry over any modification
|
|
// e.g. if AW_LANGUAGE_MISMATCH exists, use it from existing screens
|
|
setScreens(filteredScreens.map(filtered => screens.find(s => s.id === filtered.id) ?? filtered));
|
|
didFilter.current = true;
|
|
|
|
// After completing screen filtering, trigger any unhandled campaign
|
|
// action present in the attribution campaign data. This updates the
|
|
// "trailhead.firstrun.didHandleCampaignAction" preference, marking the
|
|
// actions as complete to prevent them from being handled on subsequent
|
|
// visits to about:welcome. Do not await getting the action to avoid
|
|
// blocking the thread.
|
|
window.AWGetUnhandledCampaignAction?.().then(action => {
|
|
if (typeof action === "string") {
|
|
_lib_aboutwelcome_utils_mjs__WEBPACK_IMPORTED_MODULE_2__.AboutWelcomeUtils.handleCampaignAction(action, props.message_id);
|
|
}
|
|
}).catch(error => {
|
|
console.error("Failed to get unhandled campaign action:", error);
|
|
});
|
|
const screenInitials = filteredScreens.map(({
|
|
id
|
|
}) => id?.split("_")[1]?.[0]).join("");
|
|
// Send impression ping when respective screen first renders
|
|
// eslint-disable-next-line no-shadow
|
|
filteredScreens.forEach((screen, order) => {
|
|
if (index === order) {
|
|
const messageId = `${props.message_id}_${order}_${screen.id}_${screenInitials}`;
|
|
_lib_aboutwelcome_utils_mjs__WEBPACK_IMPORTED_MODULE_2__.AboutWelcomeUtils.sendImpressionTelemetry(messageId, {
|
|
screen_family: props.message_id,
|
|
screen_index: order,
|
|
screen_id: screen.id,
|
|
screen_initials: screenInitials
|
|
});
|
|
window.AWAddScreenImpression?.(screen);
|
|
}
|
|
});
|
|
|
|
// Remember that a new screen has loaded for browser navigation
|
|
if (props.updateHistory && index > window.history.state) {
|
|
window.history.pushState(index, "");
|
|
}
|
|
|
|
// Remember the previous screen index so we can animate the transition
|
|
setPreviousOrder(index);
|
|
})();
|
|
}, [index, didMount]); // eslint-disable-line react-hooks/exhaustive-deps
|
|
|
|
const [flowParams, setFlowParams] = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)(null);
|
|
const {
|
|
metricsFlowUri
|
|
} = props;
|
|
(0,react__WEBPACK_IMPORTED_MODULE_0__.useEffect)(() => {
|
|
(async () => {
|
|
if (metricsFlowUri) {
|
|
setFlowParams(await _lib_aboutwelcome_utils_mjs__WEBPACK_IMPORTED_MODULE_2__.AboutWelcomeUtils.fetchFlowParams(metricsFlowUri));
|
|
}
|
|
})();
|
|
}, [metricsFlowUri]);
|
|
|
|
// Allow "in" style to render to actually transition towards regular state,
|
|
// which also makes using browser back/forward navigation skip transitions.
|
|
const [transition, setTransition] = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)(props.transitions ? "in" : "");
|
|
(0,react__WEBPACK_IMPORTED_MODULE_0__.useEffect)(() => {
|
|
if (transition === "in") {
|
|
requestAnimationFrame(() => requestAnimationFrame(() => setTransition("")));
|
|
}
|
|
}, [transition]);
|
|
|
|
// Transition to next screen, opening about:home on last screen button CTA
|
|
const handleTransition = () => {
|
|
// Only handle transitioning out from a screen once.
|
|
if (transition === "out") {
|
|
return;
|
|
}
|
|
|
|
// Start transitioning things "out" immediately when moving forwards.
|
|
setTransition(props.transitions ? "out" : "");
|
|
|
|
// Actually move forwards after all transitions finish.
|
|
setTimeout(() => {
|
|
if (index < screens.length - 1) {
|
|
setTransition(props.transitions ? "in" : "");
|
|
setScreenIndex(prevState => prevState + 1);
|
|
} else {
|
|
window.AWFinish();
|
|
}
|
|
}, props.transitions ? TRANSITION_OUT_TIME : 0);
|
|
};
|
|
(0,react__WEBPACK_IMPORTED_MODULE_0__.useEffect)(() => {
|
|
// When about:welcome loads (on refresh or pressing back button
|
|
// from about:home), ensure history state usEffect runs before
|
|
// useEffect hook that send impression telemetry
|
|
setDidMount(true);
|
|
if (props.updateHistory) {
|
|
// Switch to the screen tracked in state (null for initial state)
|
|
// or last screen index if a user navigates by pressing back
|
|
// button from about:home
|
|
const handler = ({
|
|
state
|
|
}) => {
|
|
if (transition === "out") {
|
|
return;
|
|
}
|
|
setTransition(props.transitions ? "out" : "");
|
|
setTimeout(() => {
|
|
setTransition(props.transitions ? "in" : "");
|
|
setScreenIndex(Math.min(state, screens.length - 1));
|
|
}, props.transitions ? TRANSITION_OUT_TIME : 0);
|
|
};
|
|
|
|
// Handle page load, e.g., going back to about:welcome from about:home
|
|
const {
|
|
state
|
|
} = window.history;
|
|
if (state) {
|
|
setScreenIndex(Math.min(state, screens.length - 1));
|
|
setPreviousOrder(Math.min(state, screens.length - 1));
|
|
}
|
|
|
|
// Watch for browser back/forward button navigation events
|
|
window.addEventListener("popstate", handler);
|
|
return () => window.removeEventListener("popstate", handler);
|
|
}
|
|
return false;
|
|
}, []); // eslint-disable-line react-hooks/exhaustive-deps
|
|
|
|
const [multiSelects, setMultiSelects] = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)({});
|
|
|
|
// Save the active multi select state for each screen as an object keyed by
|
|
// screen id. Each screen id has an array containing checkbox ids used in
|
|
// handleAction to update MULTI_ACTION data. This allows us to remember the
|
|
// state of each screen's multi select checkboxes when navigating back and
|
|
// forth between screens, while also allowing a message to have more than one
|
|
// multi select screen.
|
|
const [activeMultiSelects, setActiveMultiSelects] = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)({});
|
|
|
|
// Save the active single select state for each screen as string value keyed
|
|
// by screen id. Similar to above, this allows us to remember the state of
|
|
// each screen's single select picker when navigating back and forth between
|
|
// screens.
|
|
const [activeSingleSelects, setActiveSingleSelects] = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)({});
|
|
|
|
// Get the active theme so the rendering code can make it selected
|
|
// by default.
|
|
const [activeTheme, setActiveTheme] = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)(null);
|
|
const [initialTheme, setInitialTheme] = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)(null);
|
|
(0,react__WEBPACK_IMPORTED_MODULE_0__.useEffect)(() => {
|
|
(async () => {
|
|
let theme = await window.AWGetSelectedTheme();
|
|
setInitialTheme(theme);
|
|
setActiveTheme(theme);
|
|
})();
|
|
}, []);
|
|
const {
|
|
negotiatedLanguage,
|
|
langPackInstallPhase,
|
|
languageFilteredScreens
|
|
} = (0,_LanguageSwitcher__WEBPACK_IMPORTED_MODULE_4__.useLanguageSwitcher)(props.appAndSystemLocaleInfo, screens, index, setScreenIndex);
|
|
(0,react__WEBPACK_IMPORTED_MODULE_0__.useEffect)(() => {
|
|
setScreens(languageFilteredScreens);
|
|
}, [languageFilteredScreens]);
|
|
const [installedAddons, setInstalledAddons] = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)(null);
|
|
(0,react__WEBPACK_IMPORTED_MODULE_0__.useEffect)(() => {
|
|
(async () => {
|
|
let addons = await window.AWGetInstalledAddons();
|
|
setInstalledAddons(addons);
|
|
})();
|
|
}, [index]);
|
|
return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement((react__WEBPACK_IMPORTED_MODULE_0___default().Fragment), null, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", {
|
|
className: `outer-wrapper onboardingContainer proton transition-${transition}`,
|
|
style: props.backdrop ? {
|
|
background: props.backdrop
|
|
} : {}
|
|
}, screens.map((currentScreen, order) => {
|
|
const isFirstScreen = currentScreen === screens[0];
|
|
const isLastScreen = currentScreen === screens[screens.length - 1];
|
|
const totalNumberOfScreens = screens.length;
|
|
const isSingleScreen = totalNumberOfScreens === 1;
|
|
const setActiveMultiSelect = (valueOrFn, multiSelectId) => {
|
|
setActiveMultiSelects(prevState => {
|
|
const currentScreenSelections = prevState[currentScreen.id] || {};
|
|
return {
|
|
...prevState,
|
|
[currentScreen.id]: {
|
|
...currentScreenSelections,
|
|
[multiSelectId]: typeof valueOrFn === "function" ? valueOrFn(currentScreenSelections[multiSelectId]) : valueOrFn
|
|
}
|
|
};
|
|
});
|
|
};
|
|
const setScreenMultiSelects = (valueOrFn, multiSelectId) => {
|
|
setMultiSelects(prevState => {
|
|
const currentMultiSelects = prevState[currentScreen.id] || {};
|
|
return {
|
|
...prevState,
|
|
[currentScreen.id]: {
|
|
...currentMultiSelects,
|
|
[multiSelectId]: typeof valueOrFn === "function" ? valueOrFn(currentMultiSelects[multiSelectId]) : valueOrFn
|
|
}
|
|
};
|
|
});
|
|
};
|
|
const setActiveSingleSelect = valueOrFn => setActiveSingleSelects(prevState => ({
|
|
...prevState,
|
|
[currentScreen.id]: typeof valueOrFn === "function" ? valueOrFn(prevState[currentScreen.id]) : valueOrFn
|
|
}));
|
|
return index === order ? /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(WelcomeScreen, {
|
|
key: currentScreen.id + order,
|
|
id: currentScreen.id,
|
|
totalNumberOfScreens: totalNumberOfScreens,
|
|
isFirstScreen: isFirstScreen,
|
|
isLastScreen: isLastScreen,
|
|
isSingleScreen: isSingleScreen,
|
|
order: order,
|
|
previousOrder: previousOrder,
|
|
content: currentScreen.content,
|
|
navigate: handleTransition,
|
|
messageId: `${props.message_id}_${order}_${currentScreen.id}`,
|
|
UTMTerm: props.utm_term,
|
|
flowParams: flowParams,
|
|
activeTheme: activeTheme,
|
|
initialTheme: initialTheme,
|
|
setActiveTheme: setActiveTheme,
|
|
setInitialTheme: setInitialTheme,
|
|
screenMultiSelects: multiSelects[currentScreen.id],
|
|
setScreenMultiSelects: setScreenMultiSelects,
|
|
activeMultiSelect: activeMultiSelects[currentScreen.id],
|
|
setActiveMultiSelect: setActiveMultiSelect,
|
|
autoAdvance: currentScreen.auto_advance,
|
|
activeSingleSelect: activeSingleSelects[currentScreen.id],
|
|
setActiveSingleSelect: setActiveSingleSelect,
|
|
negotiatedLanguage: negotiatedLanguage,
|
|
langPackInstallPhase: langPackInstallPhase,
|
|
forceHideStepsIndicator: currentScreen.force_hide_steps_indicator,
|
|
ariaRole: props.ariaRole,
|
|
aboveButtonStepsIndicator: currentScreen.above_button_steps_indicator,
|
|
installedAddons: installedAddons,
|
|
setInstalledAddons: setInstalledAddons
|
|
}) : null;
|
|
})));
|
|
};
|
|
const SecondaryCTA = props => {
|
|
const targetElement = props.position ? `secondary_button_${props.position}` : `secondary_button`;
|
|
let buttonStyling = props.content.secondary_button?.has_arrow_icon ? `secondary arrow-icon` : `secondary`;
|
|
const isPrimary = props.content.secondary_button?.style === "primary";
|
|
const isTextLink = !["split", "callout"].includes(props.content.position) && props.content.tiles?.type !== "addons-picker" && !isPrimary;
|
|
const isSplitButton = props.content.submenu_button?.attached_to === targetElement;
|
|
let className = "secondary-cta";
|
|
if (props.position) {
|
|
className += ` ${props.position}`;
|
|
}
|
|
if (isSplitButton) {
|
|
className += " split-button-container";
|
|
}
|
|
const isDisabled = react__WEBPACK_IMPORTED_MODULE_0___default().useCallback(disabledValue => {
|
|
if (disabledValue === "hasActiveMultiSelect") {
|
|
if (!props.activeMultiSelect) {
|
|
return true;
|
|
}
|
|
for (const key in props.activeMultiSelect) {
|
|
if (props.activeMultiSelect[key]?.length > 0) {
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
return disabledValue;
|
|
}, [props.activeMultiSelect]);
|
|
if (isTextLink) {
|
|
buttonStyling += " text-link";
|
|
}
|
|
if (isPrimary) {
|
|
buttonStyling = props.content.secondary_button?.has_arrow_icon ? `primary arrow-icon` : `primary`;
|
|
}
|
|
return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", {
|
|
className: className
|
|
}, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_MSLocalized__WEBPACK_IMPORTED_MODULE_1__.Localized, {
|
|
text: props.content[targetElement].text
|
|
}, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("span", null)), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_MSLocalized__WEBPACK_IMPORTED_MODULE_1__.Localized, {
|
|
text: props.content[targetElement].label
|
|
}, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("button", {
|
|
className: buttonStyling,
|
|
value: targetElement,
|
|
disabled: isDisabled(props.content.secondary_button?.disabled),
|
|
onClick: props.handleAction
|
|
})), isSplitButton ? /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_SubmenuButton__WEBPACK_IMPORTED_MODULE_5__.SubmenuButton, {
|
|
content: props.content,
|
|
handleAction: props.handleAction
|
|
}) : null);
|
|
};
|
|
const StepsIndicator = props => {
|
|
let steps = [];
|
|
for (let i = 0; i < props.totalNumberOfScreens; i++) {
|
|
let className = `${i === props.order ? "current" : ""} ${i < props.order ? "complete" : ""}`;
|
|
steps.push(/*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", {
|
|
key: i,
|
|
className: `indicator ${className}`,
|
|
role: "presentation"
|
|
}));
|
|
}
|
|
return steps;
|
|
};
|
|
const ProgressBar = ({
|
|
step,
|
|
previousStep,
|
|
totalNumberOfScreens
|
|
}) => {
|
|
const [progress, setProgress] = react__WEBPACK_IMPORTED_MODULE_0___default().useState(previousStep / totalNumberOfScreens);
|
|
(0,react__WEBPACK_IMPORTED_MODULE_0__.useEffect)(() => {
|
|
// We don't need to hook any dependencies because any time the step changes,
|
|
// the screen's entire DOM tree will be re-rendered.
|
|
setProgress(step / totalNumberOfScreens);
|
|
}, []); // eslint-disable-line react-hooks/exhaustive-deps
|
|
return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", {
|
|
className: "indicator",
|
|
role: "presentation",
|
|
style: {
|
|
"--progress-bar-progress": `${progress * 100}%`
|
|
}
|
|
});
|
|
};
|
|
class WelcomeScreen extends (react__WEBPACK_IMPORTED_MODULE_0___default().PureComponent) {
|
|
constructor(props) {
|
|
super(props);
|
|
this.handleAction = this.handleAction.bind(this);
|
|
}
|
|
handleOpenURL(action, flowParams, UTMTerm) {
|
|
let {
|
|
type,
|
|
data
|
|
} = action;
|
|
if (type === "SHOW_FIREFOX_ACCOUNTS") {
|
|
let params = {
|
|
..._lib_addUtmParams_mjs__WEBPACK_IMPORTED_MODULE_6__.BASE_PARAMS,
|
|
utm_term: `${UTMTerm}-screen`
|
|
};
|
|
if (action.addFlowParams && flowParams) {
|
|
params = {
|
|
...params,
|
|
...flowParams
|
|
};
|
|
}
|
|
data = {
|
|
...data,
|
|
extraParams: params
|
|
};
|
|
} else if (type === "OPEN_URL") {
|
|
let url = new URL(data.args);
|
|
(0,_lib_addUtmParams_mjs__WEBPACK_IMPORTED_MODULE_6__.addUtmParams)(url, `${UTMTerm}-screen`);
|
|
if (action.addFlowParams && flowParams) {
|
|
url.searchParams.append("device_id", flowParams.deviceId);
|
|
url.searchParams.append("flow_id", flowParams.flowId);
|
|
url.searchParams.append("flow_begin_time", flowParams.flowBeginTime);
|
|
}
|
|
data = {
|
|
...data,
|
|
args: url.toString()
|
|
};
|
|
}
|
|
return _lib_aboutwelcome_utils_mjs__WEBPACK_IMPORTED_MODULE_2__.AboutWelcomeUtils.handleUserAction({
|
|
type,
|
|
data
|
|
});
|
|
}
|
|
async handleAction(event) {
|
|
let {
|
|
props
|
|
} = this;
|
|
const value = event.currentTarget.value ?? event.currentTarget.getAttribute("value");
|
|
const source = event.source || value;
|
|
let targetContent = props.content[value] || props.content.tiles || props.content.languageSwitcher;
|
|
if (value === "submenu_button" && event.action) {
|
|
targetContent = {
|
|
action: event.action
|
|
};
|
|
}
|
|
if (!(targetContent && targetContent.action)) {
|
|
return;
|
|
}
|
|
// Send telemetry before waiting on actions
|
|
_lib_aboutwelcome_utils_mjs__WEBPACK_IMPORTED_MODULE_2__.AboutWelcomeUtils.sendActionTelemetry(props.messageId, source, event.name);
|
|
|
|
// Send additional telemetry if a messaging surface like feature callout is
|
|
// dismissed via the dismiss button. Other causes of dismissal will be
|
|
// handled separately by the messaging surface's own code.
|
|
if (value === "dismiss_button" && !event.name) {
|
|
_lib_aboutwelcome_utils_mjs__WEBPACK_IMPORTED_MODULE_2__.AboutWelcomeUtils.sendDismissTelemetry(props.messageId, source);
|
|
}
|
|
let {
|
|
action
|
|
} = targetContent;
|
|
action = JSON.parse(JSON.stringify(action));
|
|
if (action.collectSelect) {
|
|
this.setMultiSelectActions(action);
|
|
}
|
|
let actionResult;
|
|
if (["OPEN_URL", "SHOW_FIREFOX_ACCOUNTS"].includes(action.type)) {
|
|
this.handleOpenURL(action, props.flowParams, props.UTMTerm);
|
|
} else if (action.type) {
|
|
let actionPromise = _lib_aboutwelcome_utils_mjs__WEBPACK_IMPORTED_MODULE_2__.AboutWelcomeUtils.handleUserAction(action);
|
|
if (action.needsAwait) {
|
|
actionResult = await actionPromise;
|
|
}
|
|
if (action.type === "FXA_SIGNIN_FLOW") {
|
|
_lib_aboutwelcome_utils_mjs__WEBPACK_IMPORTED_MODULE_2__.AboutWelcomeUtils.sendActionTelemetry(props.messageId, actionResult ? "sign_in" : "sign_in_cancel", "FXA_SIGNIN_FLOW");
|
|
}
|
|
// Wait until migration closes to complete the action
|
|
const hasMigrate = a => a.type === "SHOW_MIGRATION_WIZARD" || a.type === "MULTI_ACTION" && a.data?.actions?.some(hasMigrate);
|
|
if (hasMigrate(action)) {
|
|
await window.AWWaitForMigrationClose();
|
|
_lib_aboutwelcome_utils_mjs__WEBPACK_IMPORTED_MODULE_2__.AboutWelcomeUtils.sendActionTelemetry(props.messageId, "migrate_close");
|
|
}
|
|
}
|
|
|
|
// A special tiles.action.theme value indicates we should use the event's value vs provided value.
|
|
if (action.theme) {
|
|
let themeToUse = action.theme === "<event>" ? event.currentTarget.value : this.props.initialTheme || action.theme;
|
|
this.props.setActiveTheme(themeToUse);
|
|
window.AWSelectTheme(themeToUse);
|
|
}
|
|
if (action.picker) {
|
|
let options = props.content.tiles.data;
|
|
options.forEach(opt => {
|
|
if (opt.id === value) {
|
|
_lib_aboutwelcome_utils_mjs__WEBPACK_IMPORTED_MODULE_2__.AboutWelcomeUtils.handleUserAction(opt.action);
|
|
}
|
|
});
|
|
}
|
|
|
|
// If the action has persistActiveTheme: true, we set the initial theme to the currently active theme
|
|
// so that it can be reverted to in the event that the user navigates away from the screen
|
|
if (action.persistActiveTheme) {
|
|
this.props.setInitialTheme(this.props.activeTheme);
|
|
}
|
|
|
|
// `navigate` and `dismiss` can be true/false/undefined, or they can be a
|
|
// string "actionResult" in which case we should use the actionResult
|
|
// (boolean resolved by handleUserAction)
|
|
const shouldDoBehavior = behavior => {
|
|
if (behavior !== "actionResult") {
|
|
return behavior;
|
|
}
|
|
if (action.needsAwait) {
|
|
return actionResult;
|
|
}
|
|
console.error("actionResult is only supported for actions with needsAwait");
|
|
return false;
|
|
};
|
|
if (shouldDoBehavior(action.navigate)) {
|
|
props.navigate();
|
|
}
|
|
|
|
// Used by FeatureCallout to advance screens by re-rendering the whole
|
|
// wrapper, updating anchor, page_event_listeners, etc. `navigate` only
|
|
// updates the inner content. Only implemented by FeatureCallout.
|
|
if (action.advance_screens) {
|
|
if (shouldDoBehavior(action.advance_screens.behavior ?? true)) {
|
|
window.AWAdvanceScreens?.(action.advance_screens);
|
|
}
|
|
}
|
|
if (shouldDoBehavior(action.dismiss)) {
|
|
window.AWFinish();
|
|
}
|
|
}
|
|
setMultiSelectActions(action) {
|
|
let {
|
|
props
|
|
} = this;
|
|
// Populate MULTI_ACTION data actions property with selected checkbox
|
|
// actions from tiles data
|
|
if (action.type !== "MULTI_ACTION") {
|
|
console.error("collectSelect is only supported for MULTI_ACTION type actions");
|
|
action.type = "MULTI_ACTION";
|
|
}
|
|
if (!Array.isArray(action.data?.actions)) {
|
|
console.error("collectSelect is only supported for MULTI_ACTION type actions with an array of actions");
|
|
action.data = {
|
|
actions: []
|
|
};
|
|
}
|
|
|
|
// Prepend the multi-select actions to the CTA's actions array, but keep
|
|
// the actions in the same order they appear in. This way the CTA action
|
|
// can go last, after the multi-select actions are processed. For example,
|
|
// 1. checkbox action 1
|
|
// 2. checkbox action 2
|
|
// 3. radio action
|
|
// 4. CTA action (which perhaps depends on the radio action)
|
|
// Note, this order is only guaranteed if action.data has the
|
|
// `orderedExecution` flag set to true.
|
|
let multiSelectActions = [];
|
|
const processTile = (tile, tileIndex) => {
|
|
if (tile?.type !== "multiselect" || !Array.isArray(tile.data)) {
|
|
return;
|
|
}
|
|
const multiSelectId = `tile-${tileIndex}`;
|
|
const activeSelections = props.activeMultiSelect[multiSelectId] || [];
|
|
for (const checkbox of tile.data) {
|
|
let checkboxAction;
|
|
if (activeSelections.includes(checkbox.id)) {
|
|
checkboxAction = checkbox.checkedAction ?? checkbox.action;
|
|
} else {
|
|
checkboxAction = checkbox.uncheckedAction;
|
|
}
|
|
if (checkboxAction) {
|
|
multiSelectActions.push(checkboxAction);
|
|
}
|
|
}
|
|
};
|
|
|
|
// Process tiles (this may be a single tile object or an array consisting of
|
|
// tile objects)
|
|
if (props.content?.tiles) {
|
|
if (Array.isArray(props.content.tiles)) {
|
|
props.content.tiles.forEach(processTile);
|
|
} else {
|
|
// Handle case where tiles is a single tile object
|
|
processTile(props.content.tiles, 0);
|
|
}
|
|
}
|
|
|
|
// Prepend the collected multi-select actions to the CTA's actions array
|
|
action.data.actions.unshift(...multiSelectActions);
|
|
for (const value of Object.values(props.activeMultiSelect)) {
|
|
// Send telemetry with selected checkbox ids
|
|
_lib_aboutwelcome_utils_mjs__WEBPACK_IMPORTED_MODULE_2__.AboutWelcomeUtils.sendActionTelemetry(props.messageId, value.flat(), "SELECT_CHECKBOX");
|
|
}
|
|
}
|
|
render() {
|
|
return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_MultiStageProtonScreen__WEBPACK_IMPORTED_MODULE_3__.MultiStageProtonScreen, {
|
|
content: this.props.content,
|
|
id: this.props.id,
|
|
order: this.props.order,
|
|
previousOrder: this.props.previousOrder,
|
|
activeTheme: this.props.activeTheme,
|
|
installedAddons: this.props.installedAddons,
|
|
screenMultiSelects: this.props.screenMultiSelects,
|
|
setScreenMultiSelects: this.props.setScreenMultiSelects,
|
|
activeMultiSelect: this.props.activeMultiSelect,
|
|
setActiveMultiSelect: this.props.setActiveMultiSelect,
|
|
activeSingleSelect: this.props.activeSingleSelect,
|
|
setActiveSingleSelect: this.props.setActiveSingleSelect,
|
|
totalNumberOfScreens: this.props.totalNumberOfScreens,
|
|
appAndSystemLocaleInfo: this.props.appAndSystemLocaleInfo,
|
|
negotiatedLanguage: this.props.negotiatedLanguage,
|
|
langPackInstallPhase: this.props.langPackInstallPhase,
|
|
handleAction: this.handleAction,
|
|
messageId: this.props.messageId,
|
|
isFirstScreen: this.props.isFirstScreen,
|
|
isLastScreen: this.props.isLastScreen,
|
|
isSingleScreen: this.props.isSingleScreen,
|
|
startsWithCorner: this.props.startsWithCorner,
|
|
autoAdvance: this.props.autoAdvance,
|
|
forceHideStepsIndicator: this.props.forceHideStepsIndicator,
|
|
ariaRole: this.props.ariaRole,
|
|
aboveButtonStepsIndicator: this.props.aboveButtonStepsIndicator
|
|
});
|
|
}
|
|
}
|
|
|
|
/***/ }),
|
|
/* 5 */
|
|
/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
|
|
|
|
__webpack_require__.r(__webpack_exports__);
|
|
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
|
|
/* harmony export */ CONFIGURABLE_STYLES: () => (/* binding */ CONFIGURABLE_STYLES),
|
|
/* harmony export */ Localized: () => (/* binding */ Localized)
|
|
/* harmony export */ });
|
|
/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(1);
|
|
/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_0__);
|
|
/* 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/. */
|
|
|
|
|
|
const CONFIGURABLE_STYLES = ["color", "fontSize", "fontWeight", "letterSpacing", "lineHeight", "marginBlock", "marginInline", "paddingBlock", "paddingInline", "textAlign", "whiteSpace"];
|
|
const ZAP_SIZE_THRESHOLD = 160;
|
|
|
|
/**
|
|
* Based on the .text prop, localizes an inner element if a string_id
|
|
* is provided, OR renders plain text, OR hides it if nothing is provided.
|
|
* Allows configuring of some styles including zap underline and color.
|
|
*
|
|
* Examples:
|
|
*
|
|
* Localized text
|
|
* ftl:
|
|
* title = Welcome
|
|
* jsx:
|
|
* <Localized text={{string_id: "title"}}><h1 /></Localized>
|
|
* output:
|
|
* <h1 data-l10n-id="title">Welcome</h1>
|
|
*
|
|
* Unlocalized text
|
|
* jsx:
|
|
* <Localized text="Welcome"><h1 /></Localized>
|
|
* <Localized text={{raw: "Welcome"}}><h1 /></Localized>
|
|
* output:
|
|
* <h1>Welcome</h1>
|
|
*/
|
|
|
|
const Localized = ({
|
|
text,
|
|
children
|
|
}) => {
|
|
// Dynamically determine the size of the zap style.
|
|
const zapRef = /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createRef();
|
|
(0,react__WEBPACK_IMPORTED_MODULE_0__.useEffect)(() => {
|
|
const {
|
|
current
|
|
} = zapRef;
|
|
if (current) {
|
|
requestAnimationFrame(() => current?.classList.replace("short", current.getBoundingClientRect().width > ZAP_SIZE_THRESHOLD ? "long" : "short"));
|
|
}
|
|
});
|
|
|
|
// Skip rendering of children with no text.
|
|
if (!text) {
|
|
return null;
|
|
}
|
|
|
|
// Allow augmenting existing child container properties.
|
|
const props = {
|
|
children: [],
|
|
className: "",
|
|
style: {},
|
|
...children?.props
|
|
};
|
|
// Support nested Localized by starting with their children.
|
|
const textNodes = Array.isArray(props.children) ? props.children : [props.children];
|
|
|
|
// Pick desired fluent or raw/plain text to render.
|
|
if (text.string_id) {
|
|
// Set the key so React knows not to reuse when switching to plain text.
|
|
props.key = text.string_id;
|
|
props["data-l10n-id"] = text.string_id;
|
|
if (text.args) {
|
|
props["data-l10n-args"] = JSON.stringify(text.args);
|
|
}
|
|
} else if (text.raw) {
|
|
textNodes.push(text.raw);
|
|
} else if (typeof text === "string") {
|
|
textNodes.push(text);
|
|
}
|
|
|
|
// Add zap style and content in a way that allows fluent to insert too.
|
|
if (text.zap) {
|
|
props.className += " welcomeZap";
|
|
textNodes.push(/*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("span", {
|
|
className: "short zap",
|
|
"data-l10n-name": "zap",
|
|
ref: zapRef
|
|
}, text.zap));
|
|
}
|
|
if (text.aria_label) {
|
|
props["aria-label"] = text.aria_label;
|
|
}
|
|
|
|
// Apply certain configurable styles.
|
|
CONFIGURABLE_STYLES.forEach(style => {
|
|
if (text[style] !== undefined) {
|
|
props.style[style] = text[style];
|
|
}
|
|
});
|
|
return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().cloneElement(
|
|
// Provide a default container for the text if necessary.
|
|
children ?? /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("span", null), props,
|
|
// Conditionally pass in as void elements can't accept empty array.
|
|
textNodes.length ? textNodes : null);
|
|
};
|
|
|
|
/***/ }),
|
|
/* 6 */
|
|
/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
|
|
|
|
__webpack_require__.r(__webpack_exports__);
|
|
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
|
|
/* harmony export */ MultiStageProtonScreen: () => (/* binding */ MultiStageProtonScreen),
|
|
/* harmony export */ ProtonScreen: () => (/* binding */ ProtonScreen),
|
|
/* harmony export */ ProtonScreenActionButtons: () => (/* binding */ ProtonScreenActionButtons)
|
|
/* harmony export */ });
|
|
/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(1);
|
|
/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_0__);
|
|
/* harmony import */ var _MSLocalized__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(5);
|
|
/* harmony import */ var _lib_aboutwelcome_utils_mjs__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(3);
|
|
/* harmony import */ var _MultiStageAboutWelcome__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(4);
|
|
/* harmony import */ var _LanguageSwitcher__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(7);
|
|
/* harmony import */ var _CTAParagraph__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(8);
|
|
/* harmony import */ var _HeroImage__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(9);
|
|
/* harmony import */ var _OnboardingVideo__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(10);
|
|
/* harmony import */ var _AdditionalCTA__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(11);
|
|
/* harmony import */ var _LinkParagraph__WEBPACK_IMPORTED_MODULE_9__ = __webpack_require__(13);
|
|
/* harmony import */ var _ContentTiles__WEBPACK_IMPORTED_MODULE_10__ = __webpack_require__(14);
|
|
/* 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/. */
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const MultiStageProtonScreen = props => {
|
|
const {
|
|
autoAdvance,
|
|
handleAction,
|
|
order
|
|
} = props;
|
|
(0,react__WEBPACK_IMPORTED_MODULE_0__.useEffect)(() => {
|
|
if (autoAdvance) {
|
|
const timer = setTimeout(() => {
|
|
handleAction({
|
|
currentTarget: {
|
|
value: autoAdvance
|
|
},
|
|
name: "AUTO_ADVANCE"
|
|
});
|
|
}, 20000);
|
|
return () => clearTimeout(timer);
|
|
}
|
|
return () => {};
|
|
}, [autoAdvance, handleAction, order]);
|
|
|
|
// Set narrow on an outer element to allow for use of SCSS outer selector and
|
|
// consolidation of styles for small screen widths with those for messages
|
|
// configured to always be narrow
|
|
if (props.content.narrow) {
|
|
document.querySelector("#multi-stage-message-root")?.setAttribute("narrow", "");
|
|
} else {
|
|
// Clear narrow attribute in case it was set by a previous screen
|
|
document.querySelector("#multi-stage-message-root")?.removeAttribute("narrow");
|
|
}
|
|
return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(ProtonScreen, {
|
|
content: props.content,
|
|
id: props.id,
|
|
order: props.order,
|
|
activeTheme: props.activeTheme,
|
|
installedAddons: props.installedAddons,
|
|
screenMultiSelects: props.screenMultiSelects,
|
|
setScreenMultiSelects: props.setScreenMultiSelects,
|
|
activeMultiSelect: props.activeMultiSelect,
|
|
setActiveMultiSelect: props.setActiveMultiSelect,
|
|
activeSingleSelect: props.activeSingleSelect,
|
|
setActiveSingleSelect: props.setActiveSingleSelect,
|
|
totalNumberOfScreens: props.totalNumberOfScreens,
|
|
handleAction: props.handleAction,
|
|
isFirstScreen: props.isFirstScreen,
|
|
isLastScreen: props.isLastScreen,
|
|
isSingleScreen: props.isSingleScreen,
|
|
previousOrder: props.previousOrder,
|
|
autoAdvance: props.autoAdvance,
|
|
isRtamo: props.isRtamo,
|
|
addonName: props.addonName,
|
|
isTheme: props.isTheme,
|
|
iconURL: props.iconURL,
|
|
messageId: props.messageId,
|
|
negotiatedLanguage: props.negotiatedLanguage,
|
|
langPackInstallPhase: props.langPackInstallPhase,
|
|
forceHideStepsIndicator: props.forceHideStepsIndicator,
|
|
ariaRole: props.ariaRole,
|
|
aboveButtonStepsIndicator: props.aboveButtonStepsIndicator
|
|
});
|
|
};
|
|
const ProtonScreenActionButtons = props => {
|
|
const {
|
|
content,
|
|
addonName,
|
|
activeMultiSelect
|
|
} = props;
|
|
const defaultValue = content.checkbox?.defaultValue;
|
|
const [isChecked, setIsChecked] = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)(defaultValue || false);
|
|
const buttonRef = react__WEBPACK_IMPORTED_MODULE_0___default().useRef(null);
|
|
const shouldFocusButton = content?.primary_button?.should_focus_button;
|
|
(0,react__WEBPACK_IMPORTED_MODULE_0__.useEffect)(() => {
|
|
if (shouldFocusButton) {
|
|
buttonRef.current?.focus();
|
|
}
|
|
}, [shouldFocusButton]);
|
|
if (!content.primary_button && !content.secondary_button && !content.additional_button) {
|
|
return null;
|
|
}
|
|
|
|
// If we have a multi-select screen, we want to disable the primary button
|
|
// until the user has selected at least one item.
|
|
const isPrimaryDisabled = primaryDisabledValue => {
|
|
if (primaryDisabledValue === "hasActiveMultiSelect") {
|
|
if (!activeMultiSelect) {
|
|
return true;
|
|
}
|
|
|
|
// Check if there's at least one selection in any of the multiselects
|
|
for (const selectKey in activeMultiSelect) {
|
|
if (activeMultiSelect[selectKey]?.length > 0) {
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
return primaryDisabledValue;
|
|
};
|
|
return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", {
|
|
className: `action-buttons ${content.additional_button ? "additional-cta-container" : ""}`,
|
|
flow: content.additional_button?.flow,
|
|
alignment: content.additional_button?.alignment
|
|
}, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_MSLocalized__WEBPACK_IMPORTED_MODULE_1__.Localized, {
|
|
text: content.primary_button?.label
|
|
}, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("button", {
|
|
ref: buttonRef,
|
|
className: `${content.primary_button?.style ?? "primary"}${content.primary_button?.has_arrow_icon ? " arrow-icon" : ""}`
|
|
// Whether or not the checkbox is checked determines which action
|
|
// should be handled. By setting value here, we indicate to
|
|
// this.handleAction() where in the content tree it should take
|
|
// the action to execute from.
|
|
,
|
|
value: isChecked ? "checkbox" : "primary_button",
|
|
disabled: isPrimaryDisabled(content.primary_button?.disabled),
|
|
onClick: props.handleAction,
|
|
"data-l10n-args": addonName ? JSON.stringify({
|
|
"addon-name": addonName
|
|
}) : ""
|
|
})), content.additional_button ? /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_AdditionalCTA__WEBPACK_IMPORTED_MODULE_8__.AdditionalCTA, {
|
|
content: content,
|
|
handleAction: props.handleAction
|
|
}) : null, content.checkbox ? /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", {
|
|
className: "checkbox-container"
|
|
}, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("input", {
|
|
type: "checkbox",
|
|
id: "action-checkbox",
|
|
checked: isChecked,
|
|
onChange: () => {
|
|
setIsChecked(!isChecked);
|
|
}
|
|
}), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_MSLocalized__WEBPACK_IMPORTED_MODULE_1__.Localized, {
|
|
text: content.checkbox.label
|
|
}, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("label", {
|
|
htmlFor: "action-checkbox"
|
|
}))) : null, content.secondary_button ? /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_MultiStageAboutWelcome__WEBPACK_IMPORTED_MODULE_3__.SecondaryCTA, {
|
|
content: content,
|
|
handleAction: props.handleAction,
|
|
activeMultiSelect: activeMultiSelect
|
|
}) : null);
|
|
};
|
|
class ProtonScreen extends (react__WEBPACK_IMPORTED_MODULE_0___default().PureComponent) {
|
|
componentDidMount() {
|
|
this.mainContentHeader.focus();
|
|
}
|
|
getScreenClassName(isFirstScreen, isLastScreen, includeNoodles, isVideoOnboarding, isAddonsPicker) {
|
|
const screenClass = `screen-${this.props.order % 2 !== 0 ? 1 : 2}`;
|
|
if (isVideoOnboarding) {
|
|
return "with-video";
|
|
}
|
|
if (isAddonsPicker) {
|
|
return "addons-picker";
|
|
}
|
|
return `${isFirstScreen ? `dialog-initial` : ``} ${isLastScreen ? `dialog-last` : ``} ${includeNoodles ? `with-noodles` : ``} ${screenClass}`;
|
|
}
|
|
renderTitle({
|
|
title,
|
|
title_logo
|
|
}) {
|
|
if (title_logo) {
|
|
const {
|
|
alignment,
|
|
...rest
|
|
} = title_logo;
|
|
return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", {
|
|
className: "inline-icon-container",
|
|
alignment: alignment ?? "center"
|
|
}, this.renderPicture({
|
|
...rest
|
|
}), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_MSLocalized__WEBPACK_IMPORTED_MODULE_1__.Localized, {
|
|
text: title
|
|
}, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("h1", {
|
|
id: "mainContentHeader"
|
|
})));
|
|
}
|
|
return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_MSLocalized__WEBPACK_IMPORTED_MODULE_1__.Localized, {
|
|
text: title
|
|
}, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("h1", {
|
|
id: "mainContentHeader"
|
|
}));
|
|
}
|
|
renderPicture({
|
|
imageURL = "chrome://branding/content/about-logo.svg",
|
|
darkModeImageURL,
|
|
reducedMotionImageURL,
|
|
darkModeReducedMotionImageURL,
|
|
alt = "",
|
|
width,
|
|
height,
|
|
marginBlock,
|
|
marginInline,
|
|
className = "logo-container"
|
|
}) {
|
|
function getLoadingStrategy() {
|
|
for (let url of [imageURL, darkModeImageURL, reducedMotionImageURL, darkModeReducedMotionImageURL]) {
|
|
if (_lib_aboutwelcome_utils_mjs__WEBPACK_IMPORTED_MODULE_2__.AboutWelcomeUtils.getLoadingStrategyFor(url) === "lazy") {
|
|
return "lazy";
|
|
}
|
|
}
|
|
return "eager";
|
|
}
|
|
return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("picture", {
|
|
className: className,
|
|
style: {
|
|
marginInline,
|
|
marginBlock
|
|
}
|
|
}, darkModeReducedMotionImageURL ? /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("source", {
|
|
srcset: darkModeReducedMotionImageURL,
|
|
media: "(prefers-color-scheme: dark) and (prefers-reduced-motion: reduce)"
|
|
}) : null, darkModeImageURL ? /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("source", {
|
|
srcset: darkModeImageURL,
|
|
media: "(prefers-color-scheme: dark)"
|
|
}) : null, reducedMotionImageURL ? /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("source", {
|
|
srcset: reducedMotionImageURL,
|
|
media: "(prefers-reduced-motion: reduce)"
|
|
}) : null, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_MSLocalized__WEBPACK_IMPORTED_MODULE_1__.Localized, {
|
|
text: alt
|
|
}, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", {
|
|
className: "sr-only logo-alt"
|
|
})), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("img", {
|
|
className: "brand-logo",
|
|
style: {
|
|
height,
|
|
width
|
|
},
|
|
src: imageURL,
|
|
alt: "",
|
|
loading: getLoadingStrategy(),
|
|
role: alt ? null : "presentation"
|
|
}));
|
|
}
|
|
renderNoodles() {
|
|
return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement((react__WEBPACK_IMPORTED_MODULE_0___default().Fragment), null, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", {
|
|
className: "noodle orange-L"
|
|
}), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", {
|
|
className: "noodle purple-C"
|
|
}), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", {
|
|
className: "noodle solid-L"
|
|
}), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", {
|
|
className: "noodle outline-L"
|
|
}), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", {
|
|
className: "noodle yellow-circle"
|
|
}));
|
|
}
|
|
renderLanguageSwitcher() {
|
|
return this.props.content.languageSwitcher ? /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_LanguageSwitcher__WEBPACK_IMPORTED_MODULE_4__.LanguageSwitcher, {
|
|
content: this.props.content,
|
|
handleAction: this.props.handleAction,
|
|
negotiatedLanguage: this.props.negotiatedLanguage,
|
|
langPackInstallPhase: this.props.langPackInstallPhase,
|
|
messageId: this.props.messageId
|
|
}) : null;
|
|
}
|
|
renderDismissButton() {
|
|
const {
|
|
size,
|
|
marginBlock,
|
|
marginInline,
|
|
label,
|
|
background
|
|
} = this.props.content.dismiss_button;
|
|
return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("button", {
|
|
className: `dismiss-button ${background ? "with-background" : ""}`,
|
|
onClick: this.props.handleAction,
|
|
value: "dismiss_button",
|
|
"data-l10n-id": label?.string_id || "spotlight-dialog-close-button",
|
|
"button-size": size,
|
|
style: {
|
|
marginBlock,
|
|
marginInline
|
|
}
|
|
});
|
|
}
|
|
renderStepsIndicator() {
|
|
const {
|
|
order,
|
|
previousOrder,
|
|
content,
|
|
totalNumberOfScreens: total,
|
|
aboveButtonStepsIndicator
|
|
} = this.props;
|
|
const currentStep = (order ?? 0) + 1;
|
|
const previousStep = (previousOrder ?? -1) + 1;
|
|
return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", {
|
|
id: "steps",
|
|
className: `steps${content.progress_bar ? " progress-bar" : ""}`,
|
|
"above-button": aboveButtonStepsIndicator ? "" : null,
|
|
"data-l10n-id": content.steps_indicator?.string_id || "onboarding-welcome-steps-indicator-label",
|
|
"data-l10n-args": JSON.stringify({
|
|
current: currentStep,
|
|
total: total ?? 0
|
|
}),
|
|
"data-l10n-attrs": "aria-label",
|
|
role: "progressbar",
|
|
"aria-valuenow": currentStep,
|
|
"aria-valuemin": 1,
|
|
"aria-valuemax": total
|
|
}, content.progress_bar ? /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_MultiStageAboutWelcome__WEBPACK_IMPORTED_MODULE_3__.ProgressBar, {
|
|
step: currentStep,
|
|
previousStep: previousStep,
|
|
totalNumberOfScreens: total
|
|
}) : /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_MultiStageAboutWelcome__WEBPACK_IMPORTED_MODULE_3__.StepsIndicator, {
|
|
order: order,
|
|
totalNumberOfScreens: total
|
|
}));
|
|
}
|
|
renderSecondarySection(content) {
|
|
return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", {
|
|
className: `section-secondary ${content.hide_secondary_section ? "with-secondary-section-hidden" : ""}`,
|
|
style: content.background ? {
|
|
background: content.background,
|
|
"--mr-secondary-background-position-y": content.split_narrow_bkg_position
|
|
} : {}
|
|
}, content.dismiss_button && content.reverse_split ? this.renderDismissButton() : null, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_MSLocalized__WEBPACK_IMPORTED_MODULE_1__.Localized, {
|
|
text: content.image_alt_text
|
|
}, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", {
|
|
className: "sr-only image-alt",
|
|
role: "img"
|
|
})), content.hero_image ? /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_HeroImage__WEBPACK_IMPORTED_MODULE_6__.HeroImage, {
|
|
url: content.hero_image.url
|
|
}) : /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement((react__WEBPACK_IMPORTED_MODULE_0___default().Fragment), null, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", {
|
|
className: "message-text"
|
|
}, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", {
|
|
className: "spacer-top"
|
|
}), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_MSLocalized__WEBPACK_IMPORTED_MODULE_1__.Localized, {
|
|
text: content.hero_text
|
|
}, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("h1", null)), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", {
|
|
className: "spacer-bottom"
|
|
}))));
|
|
}
|
|
renderOrderedContent(content) {
|
|
const elements = [];
|
|
for (const item of content) {
|
|
switch (item.type) {
|
|
case "text":
|
|
elements.push(/*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_LinkParagraph__WEBPACK_IMPORTED_MODULE_9__.LinkParagraph, {
|
|
text_content: item,
|
|
handleAction: this.props.handleAction
|
|
}));
|
|
break;
|
|
case "image":
|
|
elements.push(this.renderPicture({
|
|
imageURL: item.url,
|
|
darkModeImageURL: item.darkModeImageURL,
|
|
height: item.height,
|
|
width: item.width,
|
|
alt: item.alt_text,
|
|
marginInline: item.marginInline,
|
|
className: "inline-image"
|
|
}));
|
|
}
|
|
}
|
|
return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement((react__WEBPACK_IMPORTED_MODULE_0___default().Fragment), null, elements);
|
|
}
|
|
render() {
|
|
const {
|
|
autoAdvance,
|
|
content,
|
|
isRtamo,
|
|
isTheme,
|
|
isFirstScreen,
|
|
isLastScreen,
|
|
isSingleScreen,
|
|
forceHideStepsIndicator,
|
|
ariaRole,
|
|
aboveButtonStepsIndicator
|
|
} = this.props;
|
|
const includeNoodles = content.has_noodles;
|
|
// The default screen position is "center"
|
|
const isCenterPosition = content.position === "center" || !content.position;
|
|
const hideStepsIndicator = autoAdvance || content?.video_container || isSingleScreen || forceHideStepsIndicator;
|
|
const textColorClass = content.text_color ? `${content.text_color}-text` : "";
|
|
// Assign proton screen style 'screen-1' or 'screen-2' to centered screens
|
|
// by checking if screen order is even or odd.
|
|
const screenClassName = isCenterPosition ? this.getScreenClassName(isFirstScreen, isLastScreen, includeNoodles, content?.video_container, content.tiles?.type === "addons-picker") : "";
|
|
const isEmbeddedMigration = content.tiles?.type === "migration-wizard";
|
|
const isSystemPromptStyleSpotlight = content.isSystemPromptStyleSpotlight === true;
|
|
return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("main", {
|
|
className: `screen ${this.props.id || ""}
|
|
${screenClassName} ${textColorClass}`,
|
|
"reverse-split": content.reverse_split ? "" : null,
|
|
fullscreen: content.fullscreen ? "" : null,
|
|
style: content.screen_style && _lib_aboutwelcome_utils_mjs__WEBPACK_IMPORTED_MODULE_2__.AboutWelcomeUtils.getValidStyle(content.screen_style, ["overflow", "display"]),
|
|
role: ariaRole ?? "alertdialog",
|
|
layout: content.layout,
|
|
pos: content.position || "center",
|
|
tabIndex: "-1",
|
|
"aria-labelledby": "mainContentHeader",
|
|
ref: input => {
|
|
this.mainContentHeader = input;
|
|
},
|
|
"no-rdm": content.no_rdm ? "" : null
|
|
}, isCenterPosition ? null : this.renderSecondarySection(content), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", {
|
|
className: `section-main ${isEmbeddedMigration ? "embedded-migration" : ""}${isSystemPromptStyleSpotlight ? "system-prompt-spotlight" : ""}`,
|
|
"hide-secondary-section": content.hide_secondary_section ? String(content.hide_secondary_section) : null,
|
|
role: "document",
|
|
style: content.screen_style && _lib_aboutwelcome_utils_mjs__WEBPACK_IMPORTED_MODULE_2__.AboutWelcomeUtils.getValidStyle(content.screen_style, ["width", "padding"])
|
|
}, content.secondary_button_top ? /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_MultiStageAboutWelcome__WEBPACK_IMPORTED_MODULE_3__.SecondaryCTA, {
|
|
content: content,
|
|
handleAction: this.props.handleAction,
|
|
position: "top"
|
|
}) : null, includeNoodles ? this.renderNoodles() : null, content.dismiss_button && !content.reverse_split ? this.renderDismissButton() : null, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", {
|
|
className: `main-content ${hideStepsIndicator ? "no-steps" : ""}`,
|
|
style: {
|
|
background: content.background && isCenterPosition ? content.background : null,
|
|
width: content.width && content.position !== "split" ? content.width : null,
|
|
paddingBlock: content.split_content_padding_block ? content.split_content_padding_block : null,
|
|
paddingInline: content.split_content_padding_inline ? content.split_content_padding_inline : null
|
|
}
|
|
}, content.logo && !content.fullscreen ? this.renderPicture(content.logo) : null, isRtamo ? /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", {
|
|
className: "rtamo-icon"
|
|
}, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("img", {
|
|
className: `${isTheme ? "rtamo-theme-icon" : "brand-logo"}`,
|
|
src: this.props.iconURL,
|
|
loading: _lib_aboutwelcome_utils_mjs__WEBPACK_IMPORTED_MODULE_2__.AboutWelcomeUtils.getLoadingStrategyFor(this.props.iconURL),
|
|
alt: "",
|
|
role: "presentation"
|
|
})) : null, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", {
|
|
className: "main-content-inner",
|
|
style: {
|
|
justifyContent: content.split_content_justify_content
|
|
}
|
|
}, content.logo && content.fullscreen ? this.renderPicture(content.logo) : null, content.title || content.subtitle ? /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", {
|
|
id: "multi-stage-message-welcome-text",
|
|
className: `welcome-text ${content.title_style || ""}`
|
|
}, content.title ? this.renderTitle(content) : null, content.subtitle ? /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_MSLocalized__WEBPACK_IMPORTED_MODULE_1__.Localized, {
|
|
text: content.subtitle
|
|
}, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("h2", {
|
|
"data-l10n-args": JSON.stringify({
|
|
"addon-name": this.props.addonName,
|
|
...this.props.appAndSystemLocaleInfo?.displayNames
|
|
}),
|
|
"aria-flowto": this.props.messageId?.includes("FEATURE_TOUR") ? "steps" : "",
|
|
id: "mainContentSubheader"
|
|
})) : null, content.action_buttons_above_content && /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(ProtonScreenActionButtons, {
|
|
content: content,
|
|
addonName: this.props.addonName,
|
|
handleAction: this.props.handleAction,
|
|
activeMultiSelect: this.props.activeMultiSelect
|
|
}), content.cta_paragraph ? /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_CTAParagraph__WEBPACK_IMPORTED_MODULE_5__.CTAParagraph, {
|
|
content: content.cta_paragraph,
|
|
handleAction: this.props.handleAction
|
|
}) : null) : null, content.video_container ? /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_OnboardingVideo__WEBPACK_IMPORTED_MODULE_7__.OnboardingVideo, {
|
|
content: content.video_container,
|
|
handleAction: this.props.handleAction
|
|
}) : null, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_ContentTiles__WEBPACK_IMPORTED_MODULE_10__.ContentTiles, this.props), this.renderLanguageSwitcher(), content.above_button_content ? this.renderOrderedContent(content.above_button_content) : null, !hideStepsIndicator && aboveButtonStepsIndicator ? this.renderStepsIndicator() : null, !content.action_buttons_above_content && /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(ProtonScreenActionButtons, {
|
|
content: content,
|
|
addonName: this.props.addonName,
|
|
handleAction: this.props.handleAction,
|
|
activeMultiSelect: this.props.activeMultiSelect
|
|
}),
|
|
/* Fullscreen dot-style step indicator should sit inside the
|
|
main inner content to share its padding, which will be
|
|
configurable with Bug 1956042 */
|
|
!hideStepsIndicator && !aboveButtonStepsIndicator && !content.progress_bar && content.fullscreen ? this.renderStepsIndicator() : null), !hideStepsIndicator && !aboveButtonStepsIndicator && !(content.fullscreen && !content.progress_bar) ? this.renderStepsIndicator() : null)), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_MSLocalized__WEBPACK_IMPORTED_MODULE_1__.Localized, {
|
|
text: content.info_text
|
|
}, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("span", {
|
|
className: "info-text"
|
|
})));
|
|
}
|
|
}
|
|
|
|
/***/ }),
|
|
/* 7 */
|
|
/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
|
|
|
|
__webpack_require__.r(__webpack_exports__);
|
|
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
|
|
/* harmony export */ LanguageSwitcher: () => (/* binding */ LanguageSwitcher),
|
|
/* harmony export */ useLanguageSwitcher: () => (/* binding */ useLanguageSwitcher)
|
|
/* harmony export */ });
|
|
/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(1);
|
|
/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_0__);
|
|
/* harmony import */ var _MSLocalized__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(5);
|
|
/* harmony import */ var _lib_aboutwelcome_utils_mjs__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(3);
|
|
/* 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/. */
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
* 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.
|
|
*/
|
|
function useLanguageSwitcher(appAndSystemLocaleInfo, screens, screenIndex, setScreenIndex) {
|
|
const languageMismatchScreenIndex = screens.findIndex(({
|
|
id
|
|
}) => id === "AW_LANGUAGE_MISMATCH");
|
|
const mismatchScreen = 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.
|
|
(0,react__WEBPACK_IMPORTED_MODULE_0__.useEffect)(() => {
|
|
if (mismatchScreen?.content?.languageSwitcher) {
|
|
for (const text of Object.values(mismatchScreen.content.languageSwitcher)) {
|
|
if (text?.args && text.args.negotiatedLanguage === undefined) {
|
|
text.args.negotiatedLanguage = "";
|
|
}
|
|
}
|
|
}
|
|
}, [mismatchScreen]);
|
|
|
|
// If there is a mismatch, then Firefox can negotiate a better langpack to offer
|
|
// the user.
|
|
const [negotiatedLanguage, setNegotiatedLanguage] = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)(null);
|
|
(0,react__WEBPACK_IMPORTED_MODULE_0__.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] = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)("before-installation");
|
|
(0,react__WEBPACK_IMPORTED_MODULE_0__.useEffect)(function ensureLangPackInstalled() {
|
|
if (!negotiatedLanguage) {
|
|
// There are no negotiated languages to download yet.
|
|
return;
|
|
}
|
|
setLangPackInstallPhase("installing");
|
|
window.AWEnsureLangPackInstalled(negotiatedLanguage, mismatchScreen?.content).then(content => {
|
|
// Update screen content with strings that might have changed.
|
|
mismatchScreen.content = content;
|
|
setLangPackInstallPhase("installed");
|
|
}, error => {
|
|
console.error(error);
|
|
setLangPackInstallPhase("installation-error");
|
|
});
|
|
}, [negotiatedLanguage, mismatchScreen]);
|
|
const [languageFilteredScreens, setLanguageFilteredScreens] = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)(screens);
|
|
(0,react__WEBPACK_IMPORTED_MODULE_0__.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 (mismatchScreen && (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);
|
|
}
|
|
},
|
|
// Removing screenIndex as a dependency as it's causing infinite re-renders (1873019)
|
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
[appAndSystemLocaleInfo?.matchType, languageMismatchScreenIndex, negotiatedLanguage, mismatchScreen, screens, setScreenIndex]);
|
|
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.
|
|
*/
|
|
function LanguageSwitcher(props) {
|
|
const {
|
|
content,
|
|
handleAction,
|
|
negotiatedLanguage,
|
|
langPackInstallPhase,
|
|
messageId
|
|
} = props;
|
|
const [isAwaitingLangpack, setIsAwaitingLangpack] = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)(false);
|
|
|
|
// Determine the status of the langpack installation.
|
|
(0,react__WEBPACK_IMPORTED_MODULE_0__.useEffect)(() => {
|
|
if (isAwaitingLangpack && langPackInstallPhase !== "installing") {
|
|
window.AWSetRequestedLocales(negotiatedLanguage.requestSystemLocales);
|
|
requestAnimationFrame(() => {
|
|
handleAction(
|
|
// Simulate the click event.
|
|
{
|
|
currentTarget: {
|
|
value: "download_complete"
|
|
}
|
|
});
|
|
});
|
|
}
|
|
}, [handleAction, isAwaitingLangpack, langPackInstallPhase, negotiatedLanguage?.requestSystemLocales]);
|
|
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 /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", {
|
|
className: "action-buttons language-switcher-container"
|
|
}, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", {
|
|
style: {
|
|
display: showPreloadingScreen ? "block" : "none"
|
|
}
|
|
}, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("button", {
|
|
className: "primary",
|
|
value: "primary_button",
|
|
disabled: true,
|
|
type: "button"
|
|
}, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("img", {
|
|
className: "language-loader",
|
|
src: "chrome://global/skin/icons/loading.svg",
|
|
alt: ""
|
|
}), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_MSLocalized__WEBPACK_IMPORTED_MODULE_1__.Localized, {
|
|
text: content.languageSwitcher.waiting
|
|
})), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", {
|
|
className: "secondary-cta"
|
|
}, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_MSLocalized__WEBPACK_IMPORTED_MODULE_1__.Localized, {
|
|
text: content.languageSwitcher.skip
|
|
}, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("button", {
|
|
value: "decline_waiting",
|
|
type: "button",
|
|
className: "secondary text-link arrow-icon",
|
|
onClick: handleAction
|
|
})))), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", {
|
|
style: {
|
|
display: showWaitingScreen ? "block" : "none"
|
|
}
|
|
}, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("button", {
|
|
className: "primary",
|
|
value: "primary_button",
|
|
disabled: true,
|
|
type: "button"
|
|
}, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("img", {
|
|
className: "language-loader",
|
|
src: "chrome://global/skin/icons/loading.svg",
|
|
alt: ""
|
|
}), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_MSLocalized__WEBPACK_IMPORTED_MODULE_1__.Localized, {
|
|
text: content.languageSwitcher.downloading
|
|
})), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", {
|
|
className: "secondary-cta"
|
|
}, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_MSLocalized__WEBPACK_IMPORTED_MODULE_1__.Localized, {
|
|
text: content.languageSwitcher.cancel
|
|
}, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("button", {
|
|
type: "button",
|
|
className: "secondary text-link",
|
|
onClick: () => {
|
|
setIsAwaitingLangpack(false);
|
|
handleAction({
|
|
currentTarget: {
|
|
value: "cancel_waiting"
|
|
}
|
|
});
|
|
}
|
|
})))), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", {
|
|
style: {
|
|
display: showReadyScreen ? "block" : "none"
|
|
}
|
|
}, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", null, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("button", {
|
|
className: "primary",
|
|
value: "primary_button",
|
|
onClick: () => {
|
|
_lib_aboutwelcome_utils_mjs__WEBPACK_IMPORTED_MODULE_2__.AboutWelcomeUtils.sendActionTelemetry(messageId, "download_langpack");
|
|
setIsAwaitingLangpack(true);
|
|
}
|
|
}, content.languageSwitcher.switch ? /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_MSLocalized__WEBPACK_IMPORTED_MODULE_1__.Localized, {
|
|
text: content.languageSwitcher.switch
|
|
}) :
|
|
// This is the localized name from the Intl.DisplayNames API.
|
|
negotiatedLanguage?.langPackDisplayName)), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", null, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("button", {
|
|
type: "button",
|
|
className: "primary",
|
|
value: "decline",
|
|
onClick: event => {
|
|
window.AWSetRequestedLocales(negotiatedLanguage.originalAppLocales);
|
|
handleAction(event);
|
|
}
|
|
}, content.languageSwitcher.continue ? /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_MSLocalized__WEBPACK_IMPORTED_MODULE_1__.Localized, {
|
|
text: content.languageSwitcher.continue
|
|
}) :
|
|
// This is the localized name from the Intl.DisplayNames API.
|
|
negotiatedLanguage?.appDisplayName))));
|
|
}
|
|
|
|
/***/ }),
|
|
/* 8 */
|
|
/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
|
|
|
|
__webpack_require__.r(__webpack_exports__);
|
|
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
|
|
/* harmony export */ CTAParagraph: () => (/* binding */ CTAParagraph)
|
|
/* harmony export */ });
|
|
/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(1);
|
|
/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_0__);
|
|
/* harmony import */ var _MSLocalized__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(5);
|
|
/* 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/. */
|
|
|
|
|
|
|
|
const CTAParagraph = props => {
|
|
const {
|
|
content,
|
|
handleAction
|
|
} = props;
|
|
if (!content?.text) {
|
|
return null;
|
|
}
|
|
return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("h2", {
|
|
className: "cta-paragraph"
|
|
}, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_MSLocalized__WEBPACK_IMPORTED_MODULE_1__.Localized, {
|
|
text: content.text
|
|
}, content.text.string_name && typeof handleAction === "function" ? /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("span", {
|
|
"data-l10n-id": content.text.string_id,
|
|
onClick: handleAction,
|
|
onKeyUp: event => ["Enter", " "].includes(event.key) ? handleAction(event) : null,
|
|
value: "cta_paragraph",
|
|
tabIndex: "0",
|
|
role: "link"
|
|
}, " ", /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("a", {
|
|
href: "",
|
|
tabIndex: "0",
|
|
"data-l10n-name": content.text.string_name
|
|
}, " ")) : null));
|
|
};
|
|
|
|
/***/ }),
|
|
/* 9 */
|
|
/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
|
|
|
|
__webpack_require__.r(__webpack_exports__);
|
|
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
|
|
/* harmony export */ HeroImage: () => (/* binding */ HeroImage)
|
|
/* harmony export */ });
|
|
/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(1);
|
|
/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_0__);
|
|
/* harmony import */ var _lib_aboutwelcome_utils_mjs__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(3);
|
|
/* 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/. */
|
|
|
|
|
|
|
|
const HeroImage = props => {
|
|
const {
|
|
height,
|
|
url,
|
|
alt
|
|
} = props;
|
|
if (!url) {
|
|
return null;
|
|
}
|
|
return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", {
|
|
className: "hero-image"
|
|
}, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("img", {
|
|
style: height ? {
|
|
height
|
|
} : null,
|
|
src: url,
|
|
loading: _lib_aboutwelcome_utils_mjs__WEBPACK_IMPORTED_MODULE_1__.AboutWelcomeUtils.getLoadingStrategyFor(url),
|
|
alt: alt || "",
|
|
role: alt ? null : "presentation"
|
|
}));
|
|
};
|
|
|
|
/***/ }),
|
|
/* 10 */
|
|
/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
|
|
|
|
__webpack_require__.r(__webpack_exports__);
|
|
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
|
|
/* harmony export */ OnboardingVideo: () => (/* binding */ OnboardingVideo)
|
|
/* harmony export */ });
|
|
/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(1);
|
|
/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_0__);
|
|
/* 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/. */
|
|
|
|
|
|
const OnboardingVideo = props => {
|
|
const vidUrl = props.content.video_url;
|
|
const autoplay = props.content.autoPlay;
|
|
const handleVideoAction = event => {
|
|
props.handleAction({
|
|
currentTarget: {
|
|
value: event
|
|
}
|
|
});
|
|
};
|
|
return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", null, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("video", {
|
|
// eslint-disable-line jsx-a11y/media-has-caption
|
|
controls: true,
|
|
autoPlay: autoplay,
|
|
src: vidUrl,
|
|
width: "604px",
|
|
height: "340px",
|
|
onPlay: () => handleVideoAction("video_start"),
|
|
onEnded: () => handleVideoAction("video_end")
|
|
}, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("source", {
|
|
src: vidUrl
|
|
})));
|
|
};
|
|
|
|
/***/ }),
|
|
/* 11 */
|
|
/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
|
|
|
|
__webpack_require__.r(__webpack_exports__);
|
|
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
|
|
/* harmony export */ AdditionalCTA: () => (/* binding */ AdditionalCTA)
|
|
/* harmony export */ });
|
|
/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(1);
|
|
/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_0__);
|
|
/* harmony import */ var _MSLocalized__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(5);
|
|
/* harmony import */ var _SubmenuButton__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(12);
|
|
/* 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/. */
|
|
|
|
|
|
|
|
|
|
const AdditionalCTA = ({
|
|
content,
|
|
handleAction
|
|
}) => {
|
|
let buttonStyle = "";
|
|
const isSplitButton = content.submenu_button?.attached_to === "additional_button";
|
|
let className = "additional-cta-box";
|
|
if (isSplitButton) {
|
|
className += " split-button-container";
|
|
}
|
|
if (!content.additional_button?.style) {
|
|
buttonStyle = "primary";
|
|
} else {
|
|
buttonStyle = content.additional_button?.style === "link" ? "cta-link" : content.additional_button?.style;
|
|
}
|
|
return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", {
|
|
className: className
|
|
}, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_MSLocalized__WEBPACK_IMPORTED_MODULE_1__.Localized, {
|
|
text: content.additional_button?.label
|
|
}, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("button", {
|
|
className: `${buttonStyle} additional-cta`,
|
|
onClick: handleAction,
|
|
value: "additional_button",
|
|
disabled: content.additional_button?.disabled === true
|
|
})), isSplitButton ? /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_SubmenuButton__WEBPACK_IMPORTED_MODULE_2__.SubmenuButton, {
|
|
content: content,
|
|
handleAction: handleAction
|
|
}) : null);
|
|
};
|
|
|
|
/***/ }),
|
|
/* 12 */
|
|
/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
|
|
|
|
__webpack_require__.r(__webpack_exports__);
|
|
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
|
|
/* harmony export */ SubmenuButton: () => (/* binding */ SubmenuButton)
|
|
/* harmony export */ });
|
|
/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(1);
|
|
/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_0__);
|
|
/* harmony import */ var _MSLocalized__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(5);
|
|
/* 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/. */
|
|
|
|
|
|
|
|
const SubmenuButton = props => {
|
|
return document.createXULElement ? /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(SubmenuButtonInner, props) : null;
|
|
};
|
|
function translateMenuitem(item, element) {
|
|
let {
|
|
label
|
|
} = item;
|
|
if (!label) {
|
|
return;
|
|
}
|
|
if (label.raw) {
|
|
element.setAttribute("label", label.raw);
|
|
}
|
|
if (label.access_key) {
|
|
element.setAttribute("accesskey", label.access_key);
|
|
}
|
|
if (label.aria_label) {
|
|
element.setAttribute("aria-label", label.aria_label);
|
|
}
|
|
if (label.tooltip_text) {
|
|
element.setAttribute("tooltiptext", label.tooltip_text);
|
|
}
|
|
if (label.string_id) {
|
|
element.setAttribute("data-l10n-id", label.string_id);
|
|
if (label.args) {
|
|
element.setAttribute("data-l10n-args", JSON.stringify(label.args));
|
|
}
|
|
}
|
|
}
|
|
function addMenuitems(items, popup) {
|
|
for (let item of items) {
|
|
switch (item.type) {
|
|
case "separator":
|
|
popup.appendChild(document.createXULElement("menuseparator"));
|
|
break;
|
|
case "menu":
|
|
let menu = document.createXULElement("menu");
|
|
menu.className = "fxms-multi-stage-menu";
|
|
translateMenuitem(item, menu);
|
|
if (item.id) {
|
|
menu.value = item.id;
|
|
}
|
|
if (item.icon) {
|
|
menu.classList.add("menu-iconic");
|
|
menu.setAttribute("image", item.icon);
|
|
}
|
|
popup.appendChild(menu);
|
|
let submenuPopup = document.createXULElement("menupopup");
|
|
menu.appendChild(submenuPopup);
|
|
addMenuitems(item.submenu, submenuPopup);
|
|
break;
|
|
case "action":
|
|
let menuitem = document.createXULElement("menuitem");
|
|
translateMenuitem(item, menuitem);
|
|
menuitem.config = item;
|
|
if (item.id) {
|
|
menuitem.value = item.id;
|
|
}
|
|
if (item.icon) {
|
|
menuitem.classList.add("menuitem-iconic");
|
|
menuitem.setAttribute("image", item.icon);
|
|
}
|
|
popup.appendChild(menuitem);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
const SubmenuButtonInner = ({
|
|
content,
|
|
handleAction
|
|
}) => {
|
|
const ref = (0,react__WEBPACK_IMPORTED_MODULE_0__.useRef)(null);
|
|
const [isSubmenuExpanded, setIsSubmenuExpanded] = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)(false);
|
|
const isPrimary = content.submenu_button?.style === "primary";
|
|
const onCommand = (0,react__WEBPACK_IMPORTED_MODULE_0__.useCallback)(event => {
|
|
let {
|
|
config
|
|
} = event.target;
|
|
let mockEvent = {
|
|
currentTarget: ref.current,
|
|
source: config.id,
|
|
name: "command",
|
|
action: config.action
|
|
};
|
|
handleAction(mockEvent);
|
|
}, [handleAction]);
|
|
const onClick = (0,react__WEBPACK_IMPORTED_MODULE_0__.useCallback)(() => {
|
|
let button = ref.current;
|
|
let submenu = button?.querySelector(".fxms-multi-stage-submenu");
|
|
if (submenu && !button.hasAttribute("open")) {
|
|
submenu.openPopup(button, {
|
|
position: "after_end"
|
|
});
|
|
}
|
|
}, []);
|
|
(0,react__WEBPACK_IMPORTED_MODULE_0__.useEffect)(() => {
|
|
let button = ref.current;
|
|
if (!button || button.querySelector(".fxms-multi-stage-submenu")) {
|
|
return null;
|
|
}
|
|
let menupopup = document.createXULElement("menupopup");
|
|
menupopup.className = "fxms-multi-stage-submenu";
|
|
addMenuitems(content.submenu_button.submenu, menupopup);
|
|
button.appendChild(menupopup);
|
|
let stylesheet;
|
|
if (!document.head.querySelector(`link[href="chrome://global/content/widgets.css"], link[href="chrome://global/skin/global.css"]`)) {
|
|
stylesheet = document.createElement("link");
|
|
stylesheet.rel = "stylesheet";
|
|
stylesheet.href = "chrome://global/content/widgets.css";
|
|
document.head.appendChild(stylesheet);
|
|
}
|
|
if (!menupopup.listenersRegistered) {
|
|
menupopup.addEventListener("command", onCommand);
|
|
menupopup.addEventListener("popupshowing", event => {
|
|
if (event.target === menupopup && event.target.anchorNode) {
|
|
event.target.anchorNode.toggleAttribute("open", true);
|
|
setIsSubmenuExpanded(true);
|
|
}
|
|
});
|
|
menupopup.addEventListener("popuphiding", event => {
|
|
if (event.target === menupopup && event.target.anchorNode) {
|
|
event.target.anchorNode.toggleAttribute("open", false);
|
|
setIsSubmenuExpanded(false);
|
|
}
|
|
});
|
|
menupopup.listenersRegistered = true;
|
|
}
|
|
return () => {
|
|
menupopup?.remove();
|
|
stylesheet?.remove();
|
|
};
|
|
}, [onCommand]); // eslint-disable-line react-hooks/exhaustive-deps
|
|
|
|
return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_MSLocalized__WEBPACK_IMPORTED_MODULE_1__.Localized, {
|
|
text: content.submenu_button.label ?? {}
|
|
}, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("button", {
|
|
className: `submenu-button ${isPrimary ? "primary" : "secondary"}`,
|
|
value: "submenu_button",
|
|
onClick: onClick,
|
|
ref: ref,
|
|
"aria-haspopup": "menu",
|
|
"aria-expanded": isSubmenuExpanded
|
|
}));
|
|
};
|
|
|
|
/***/ }),
|
|
/* 13 */
|
|
/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
|
|
|
|
__webpack_require__.r(__webpack_exports__);
|
|
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
|
|
/* harmony export */ LinkParagraph: () => (/* binding */ LinkParagraph)
|
|
/* harmony export */ });
|
|
/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(1);
|
|
/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_0__);
|
|
/* harmony import */ var _MSLocalized__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(5);
|
|
/* 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/. */
|
|
|
|
|
|
|
|
const LinkParagraph = props => {
|
|
const {
|
|
text_content,
|
|
handleAction
|
|
} = props;
|
|
const handleParagraphAction = (0,react__WEBPACK_IMPORTED_MODULE_0__.useCallback)(event => {
|
|
if (event.target.closest("a")) {
|
|
handleAction({
|
|
...event,
|
|
currentTarget: event.target
|
|
});
|
|
}
|
|
}, [handleAction]);
|
|
const onKeyPress = (0,react__WEBPACK_IMPORTED_MODULE_0__.useCallback)(event => {
|
|
if (event.key === "Enter" && !event.repeat) {
|
|
handleParagraphAction(event);
|
|
}
|
|
}, [handleParagraphAction]);
|
|
return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_MSLocalized__WEBPACK_IMPORTED_MODULE_1__.Localized, {
|
|
text: text_content.text
|
|
}, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("p", {
|
|
className: text_content.font_styles === "legal" ? "legal-paragraph" : "link-paragraph",
|
|
onClick: handleParagraphAction,
|
|
value: "link_paragraph",
|
|
onKeyPress: onKeyPress
|
|
}, text_content.link_keys?.map(link => /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("a", {
|
|
key: link,
|
|
value: link,
|
|
role: "link",
|
|
className: "text-link",
|
|
"data-l10n-name": link
|
|
// must pass in tabIndex when no href is provided
|
|
,
|
|
tabIndex: "0"
|
|
}, " "))));
|
|
};
|
|
|
|
/***/ }),
|
|
/* 14 */
|
|
/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
|
|
|
|
__webpack_require__.r(__webpack_exports__);
|
|
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
|
|
/* harmony export */ ContentTiles: () => (/* binding */ ContentTiles)
|
|
/* harmony export */ });
|
|
/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(1);
|
|
/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_0__);
|
|
/* harmony import */ var _MSLocalized__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(5);
|
|
/* harmony import */ var _AddonsPicker__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(15);
|
|
/* harmony import */ var _SingleSelect__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(16);
|
|
/* harmony import */ var _MobileDownloads__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(17);
|
|
/* harmony import */ var _MultiSelect__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(18);
|
|
/* harmony import */ var _EmbeddedMigrationWizard__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(19);
|
|
/* harmony import */ var _ActionChecklist__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(20);
|
|
/* harmony import */ var _EmbeddedBrowser__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(21);
|
|
/* harmony import */ var _lib_aboutwelcome_utils_mjs__WEBPACK_IMPORTED_MODULE_9__ = __webpack_require__(3);
|
|
/* 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/. */
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const HEADER_STYLES = ["backgroundColor", "border", "padding", "margin", "width", "height"];
|
|
const TILE_STYLES = ["marginBlock", "marginInline", "paddingBlock", "paddingInline"];
|
|
const ContentTiles = props => {
|
|
const {
|
|
content
|
|
} = props;
|
|
const [expandedTileIndex, setExpandedTileIndex] = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)(null);
|
|
// State for header that toggles showing and hiding all tiles, if applicable
|
|
const [tilesHeaderExpanded, setTilesHeaderExpanded] = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)(false);
|
|
const {
|
|
tiles
|
|
} = content;
|
|
if (!tiles) {
|
|
return null;
|
|
}
|
|
|
|
// eslint-disable-next-line react-hooks/rules-of-hooks
|
|
(0,react__WEBPACK_IMPORTED_MODULE_0__.useEffect)(() => {
|
|
// Run once when ContentTiles mounts to prefill activeMultiSelect
|
|
if (!props.activeMultiSelect) {
|
|
const tilesArray = Array.isArray(tiles) ? tiles : [tiles];
|
|
tilesArray.forEach((tile, index) => {
|
|
if (tile.type !== "multiselect" || !tile.data) {
|
|
return;
|
|
}
|
|
const multiSelectId = `tile-${index}`;
|
|
const newActiveMultiSelect = [];
|
|
tile.data.forEach(({
|
|
id,
|
|
defaultValue
|
|
}) => {
|
|
if (defaultValue && id) {
|
|
newActiveMultiSelect.push(id);
|
|
}
|
|
});
|
|
if (newActiveMultiSelect.length) {
|
|
props.setActiveMultiSelect(newActiveMultiSelect, multiSelectId);
|
|
}
|
|
});
|
|
}
|
|
}, [tiles]); // eslint-disable-line react-hooks/exhaustive-deps
|
|
|
|
const toggleTile = (index, tile) => {
|
|
const tileId = `${tile.type}${tile.id ? "_" : ""}${tile.id ?? ""}_header`;
|
|
setExpandedTileIndex(prevIndex => prevIndex === index ? null : index);
|
|
_lib_aboutwelcome_utils_mjs__WEBPACK_IMPORTED_MODULE_9__.AboutWelcomeUtils.sendActionTelemetry(props.messageId, tileId);
|
|
};
|
|
const toggleTiles = () => {
|
|
setTilesHeaderExpanded(prev => !prev);
|
|
_lib_aboutwelcome_utils_mjs__WEBPACK_IMPORTED_MODULE_9__.AboutWelcomeUtils.sendActionTelemetry(props.messageId, "content_tiles_header");
|
|
};
|
|
function getTileMultiSelects(screenMultiSelects, index) {
|
|
return screenMultiSelects?.[`tile-${index}`];
|
|
}
|
|
function getTileActiveMultiSelect(activeMultiSelect, index) {
|
|
return activeMultiSelect?.[`tile-${index}`];
|
|
}
|
|
const renderContentTile = (tile, index = 0) => {
|
|
const isExpanded = expandedTileIndex === index;
|
|
const {
|
|
header,
|
|
title,
|
|
subtitle
|
|
} = tile;
|
|
return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", {
|
|
key: index,
|
|
className: `content-tile ${header ? "has-header" : ""}`,
|
|
style: _lib_aboutwelcome_utils_mjs__WEBPACK_IMPORTED_MODULE_9__.AboutWelcomeUtils.getValidStyle(tile.style, TILE_STYLES)
|
|
}, header?.title && /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("button", {
|
|
className: "tile-header secondary",
|
|
onClick: () => toggleTile(index, tile),
|
|
"aria-expanded": isExpanded,
|
|
"aria-controls": `tile-content-${index}`,
|
|
style: _lib_aboutwelcome_utils_mjs__WEBPACK_IMPORTED_MODULE_9__.AboutWelcomeUtils.getValidStyle(header.style, HEADER_STYLES)
|
|
}, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", {
|
|
className: "header-text-container"
|
|
}, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_MSLocalized__WEBPACK_IMPORTED_MODULE_1__.Localized, {
|
|
text: header.title
|
|
}, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("span", {
|
|
className: "header-title"
|
|
})), header.subtitle && /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_MSLocalized__WEBPACK_IMPORTED_MODULE_1__.Localized, {
|
|
text: header.subtitle
|
|
}, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("span", {
|
|
className: "header-subtitle"
|
|
}))), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", {
|
|
className: "arrow-icon"
|
|
})), (title || subtitle) && /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", {
|
|
className: "tile-title-container",
|
|
id: `tile-title-container-${index}`
|
|
}, title && /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_MSLocalized__WEBPACK_IMPORTED_MODULE_1__.Localized, {
|
|
text: title
|
|
}, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("h1", {
|
|
className: "tile-title",
|
|
id: `content-tile-title-${index}`
|
|
})), subtitle && /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_MSLocalized__WEBPACK_IMPORTED_MODULE_1__.Localized, {
|
|
text: subtitle
|
|
}, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("p", {
|
|
className: "tile-subtitle"
|
|
}))), isExpanded || !header ? /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", {
|
|
className: "tile-content",
|
|
id: `tile-content-${index}`
|
|
}, tile.type === "addons-picker" && tile.data && /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_AddonsPicker__WEBPACK_IMPORTED_MODULE_2__.AddonsPicker, {
|
|
content: {
|
|
tiles: tile
|
|
},
|
|
installedAddons: props.installedAddons,
|
|
message_id: props.messageId,
|
|
handleAction: props.handleAction,
|
|
layout: content.position
|
|
}), ["theme", "single-select"].includes(tile.type) && tile.data && /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_SingleSelect__WEBPACK_IMPORTED_MODULE_3__.SingleSelect, {
|
|
content: {
|
|
tiles: tile
|
|
},
|
|
activeTheme: props.activeTheme,
|
|
handleAction: props.handleAction,
|
|
activeSingleSelect: props.activeSingleSelect,
|
|
setActiveSingleSelect: props.setActiveSingleSelect
|
|
}), tile.type === "mobile_downloads" && tile.data && /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_MobileDownloads__WEBPACK_IMPORTED_MODULE_4__.MobileDownloads, {
|
|
data: tile.data,
|
|
handleAction: props.handleAction
|
|
}), tile.type === "multiselect" && tile.data && /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_MultiSelect__WEBPACK_IMPORTED_MODULE_5__.MultiSelect, {
|
|
content: {
|
|
tiles: tile
|
|
},
|
|
screenMultiSelects: getTileMultiSelects(props.screenMultiSelects, index),
|
|
setScreenMultiSelects: props.setScreenMultiSelects,
|
|
activeMultiSelect: getTileActiveMultiSelect(props.activeMultiSelect, index),
|
|
setActiveMultiSelect: props.setActiveMultiSelect,
|
|
multiSelectId: `tile-${index}`
|
|
}), tile.type === "migration-wizard" && /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_EmbeddedMigrationWizard__WEBPACK_IMPORTED_MODULE_6__.EmbeddedMigrationWizard, {
|
|
handleAction: props.handleAction,
|
|
content: {
|
|
tiles: tile
|
|
}
|
|
}), tile.type === "action_checklist" && tile.data && /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_ActionChecklist__WEBPACK_IMPORTED_MODULE_7__.ActionChecklist, {
|
|
content: content,
|
|
message_id: props.messageId
|
|
}), tile.type === "embedded_browser" && tile.data?.url && /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_EmbeddedBrowser__WEBPACK_IMPORTED_MODULE_8__.EmbeddedBrowser, {
|
|
url: tile.data.url,
|
|
style: tile.data.style
|
|
})) : null);
|
|
};
|
|
const renderContentTiles = () => {
|
|
if (Array.isArray(tiles)) {
|
|
return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", {
|
|
id: "content-tiles-container"
|
|
}, tiles.map((tile, index) => renderContentTile(tile, index)));
|
|
}
|
|
// If tiles is not an array render the tile alone without a container
|
|
return renderContentTile(tiles, 0);
|
|
};
|
|
if (content.tiles_header) {
|
|
return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement((react__WEBPACK_IMPORTED_MODULE_0___default().Fragment), null, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("button", {
|
|
className: "content-tiles-header secondary",
|
|
onClick: toggleTiles,
|
|
"aria-expanded": tilesHeaderExpanded,
|
|
"aria-controls": `content-tiles-container`
|
|
}, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_MSLocalized__WEBPACK_IMPORTED_MODULE_1__.Localized, {
|
|
text: content.tiles_header.title
|
|
}, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("span", {
|
|
className: "header-title"
|
|
})), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", {
|
|
className: "arrow-icon"
|
|
})), tilesHeaderExpanded && renderContentTiles());
|
|
}
|
|
return renderContentTiles(tiles);
|
|
};
|
|
|
|
/***/ }),
|
|
/* 15 */
|
|
/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
|
|
|
|
__webpack_require__.r(__webpack_exports__);
|
|
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
|
|
/* harmony export */ AddonsPicker: () => (/* binding */ AddonsPicker),
|
|
/* harmony export */ InstallButton: () => (/* binding */ InstallButton),
|
|
/* harmony export */ Loader: () => (/* binding */ Loader)
|
|
/* harmony export */ });
|
|
/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(1);
|
|
/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_0__);
|
|
/* harmony import */ var _lib_aboutwelcome_utils_mjs__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(3);
|
|
/* harmony import */ var _MSLocalized__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(5);
|
|
/* 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/. */
|
|
|
|
|
|
|
|
|
|
const Loader = () => {
|
|
return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("button", {
|
|
className: "primary"
|
|
}, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", {
|
|
className: "loaderContainer"
|
|
}, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("span", {
|
|
className: "loader"
|
|
})));
|
|
};
|
|
const InstallButton = props => {
|
|
// determine if the addon is already installed so the state is
|
|
// consistent on refresh or navigation
|
|
const [installing, setInstalling] = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)(false);
|
|
const [installComplete, setInstallComplete] = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)(false);
|
|
const defaultInstallLabel = {
|
|
string_id: "amo-picker-install-button-label"
|
|
};
|
|
const defaultInstallCompleteLabel = {
|
|
string_id: "amo-picker-install-complete-label"
|
|
};
|
|
(0,react__WEBPACK_IMPORTED_MODULE_0__.useEffect)(() => {
|
|
setInstallComplete(props.installedAddons?.includes(props.addonId));
|
|
}, [props.addonId, props.installedAddons]);
|
|
let buttonLabel = installComplete ? props.install_complete_label || defaultInstallCompleteLabel : props.install_label || defaultInstallLabel;
|
|
function onClick(event) {
|
|
props.handleAction(event);
|
|
// Replace the label with the spinner
|
|
setInstalling(true);
|
|
window.AWEnsureAddonInstalled(props.addonId).then(value => {
|
|
if (value === "complete") {
|
|
// Set the label to "Installed"
|
|
setInstallComplete(true);
|
|
}
|
|
// Whether the addon installs or not, we want to remove the spinner
|
|
setInstalling(false);
|
|
});
|
|
}
|
|
return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", {
|
|
className: "install-button-wrapper"
|
|
}, installing ? /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(Loader, null) : /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_MSLocalized__WEBPACK_IMPORTED_MODULE_2__.Localized, {
|
|
text: buttonLabel
|
|
}, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("button", {
|
|
id: `install-button-${props.addonId}`,
|
|
value: props.index,
|
|
onClick: onClick,
|
|
disabled: installComplete,
|
|
className: "primary"
|
|
})));
|
|
};
|
|
const AddonsPicker = props => {
|
|
const {
|
|
content,
|
|
installedAddons,
|
|
layout
|
|
} = props;
|
|
if (!content) {
|
|
return null;
|
|
}
|
|
function handleAction(event) {
|
|
const {
|
|
message_id
|
|
} = props;
|
|
let {
|
|
action,
|
|
source_id
|
|
} = content.tiles.data[event.currentTarget.value];
|
|
let {
|
|
type,
|
|
data
|
|
} = action;
|
|
if (type === "INSTALL_ADDON_FROM_URL") {
|
|
if (!data) {
|
|
return;
|
|
}
|
|
}
|
|
_lib_aboutwelcome_utils_mjs__WEBPACK_IMPORTED_MODULE_1__.AboutWelcomeUtils.handleUserAction({
|
|
type,
|
|
data
|
|
});
|
|
_lib_aboutwelcome_utils_mjs__WEBPACK_IMPORTED_MODULE_1__.AboutWelcomeUtils.sendActionTelemetry(message_id, source_id);
|
|
}
|
|
function handleAuthorClick(event, authorId) {
|
|
event.stopPropagation();
|
|
_lib_aboutwelcome_utils_mjs__WEBPACK_IMPORTED_MODULE_1__.AboutWelcomeUtils.handleUserAction({
|
|
type: "OPEN_URL",
|
|
data: {
|
|
args: `https://addons.mozilla.org/firefox/user/${authorId}/`,
|
|
where: "tab"
|
|
}
|
|
});
|
|
}
|
|
return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", {
|
|
className: "addons-picker-container"
|
|
}, content.tiles.data.map(({
|
|
id,
|
|
name: addonName,
|
|
type,
|
|
description,
|
|
icon,
|
|
author,
|
|
install_label,
|
|
install_complete_label
|
|
}, index) => addonName ? /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", {
|
|
key: id,
|
|
className: "addon-container"
|
|
}, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", {
|
|
className: "rtamo-icon"
|
|
}, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("img", {
|
|
className: `${type === "theme" ? "rtamo-theme-icon" : "brand-logo"}`,
|
|
src: icon,
|
|
role: "presentation",
|
|
alt: ""
|
|
})), layout === "split" ? /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", {
|
|
className: "addon-rows-container"
|
|
}, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", {
|
|
className: "addon-row"
|
|
}, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", {
|
|
className: "addon-author-details"
|
|
}, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_MSLocalized__WEBPACK_IMPORTED_MODULE_2__.Localized, {
|
|
text: addonName
|
|
}, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", {
|
|
className: "addon-title"
|
|
})), author && /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", {
|
|
className: "addon-author"
|
|
}, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_MSLocalized__WEBPACK_IMPORTED_MODULE_2__.Localized, {
|
|
text: author.byLine
|
|
}, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("span", {
|
|
className: "addon-by-line"
|
|
})), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("button", {
|
|
href: "#",
|
|
onClick: e => {
|
|
handleAuthorClick(e, author.id);
|
|
},
|
|
className: "author-link"
|
|
}, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("span", null, author.name)))), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(InstallButton, {
|
|
key: id,
|
|
addonId: id,
|
|
handleAction: handleAction,
|
|
index: index,
|
|
installedAddons: installedAddons,
|
|
install_label: install_label,
|
|
install_complete_label: install_complete_label
|
|
})), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", {
|
|
className: "addon-row"
|
|
}, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_MSLocalized__WEBPACK_IMPORTED_MODULE_2__.Localized, {
|
|
text: description
|
|
}, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", {
|
|
className: "addon-description"
|
|
})))) : /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement((react__WEBPACK_IMPORTED_MODULE_0___default().Fragment), null, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", {
|
|
className: "addon-details"
|
|
}, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_MSLocalized__WEBPACK_IMPORTED_MODULE_2__.Localized, {
|
|
text: addonName
|
|
}, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", {
|
|
className: "addon-title"
|
|
})), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_MSLocalized__WEBPACK_IMPORTED_MODULE_2__.Localized, {
|
|
text: description
|
|
}, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", {
|
|
className: "addon-description"
|
|
}))), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(InstallButton, {
|
|
key: id,
|
|
addonId: id,
|
|
handleAction: handleAction,
|
|
index: index,
|
|
installedAddons: installedAddons,
|
|
install_label: install_label,
|
|
install_complete_label: install_complete_label
|
|
}))) : null));
|
|
};
|
|
|
|
/***/ }),
|
|
/* 16 */
|
|
/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
|
|
|
|
__webpack_require__.r(__webpack_exports__);
|
|
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
|
|
/* harmony export */ SingleSelect: () => (/* binding */ SingleSelect)
|
|
/* harmony export */ });
|
|
/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(1);
|
|
/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_0__);
|
|
/* harmony import */ var _MSLocalized__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(5);
|
|
/* harmony import */ var _lib_aboutwelcome_utils_mjs__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(3);
|
|
/* 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/. */
|
|
|
|
|
|
|
|
|
|
|
|
// This component was formerly "Themes" and continues to support theme
|
|
const SingleSelect = ({
|
|
activeSingleSelect,
|
|
activeTheme,
|
|
content,
|
|
handleAction,
|
|
setActiveSingleSelect
|
|
}) => {
|
|
const category = content.tiles?.category?.type || content.tiles?.type;
|
|
const isSingleSelect = category === "single-select";
|
|
const autoTriggerAllowed = itemAction => {
|
|
// Currently only enabled for sidebar experiment prefs
|
|
const allowedActions = ["SET_PREF"];
|
|
const allowedPrefs = ["sidebar.revamp", "sidebar.verticalTabs", "sidebar.visibility"];
|
|
const checkAction = action => {
|
|
if (!allowedActions.includes(action.type)) {
|
|
return false;
|
|
}
|
|
if (action.type === "SET_PREF" && !allowedPrefs.includes(action.data?.pref.name)) {
|
|
return false;
|
|
}
|
|
return true;
|
|
};
|
|
if (itemAction.type === "MULTI_ACTION") {
|
|
// Only allow autoTrigger if all actions are allowed
|
|
return !itemAction.data.actions.some(action => !checkAction(action));
|
|
}
|
|
return checkAction(itemAction);
|
|
};
|
|
|
|
// When screen renders for first time or user navigates back, update state to
|
|
// check default option.
|
|
(0,react__WEBPACK_IMPORTED_MODULE_0__.useEffect)(() => {
|
|
if (isSingleSelect && !activeSingleSelect) {
|
|
let newActiveSingleSelect = content.tiles?.selected || content.tiles?.data[0].id;
|
|
setActiveSingleSelect(newActiveSingleSelect);
|
|
let selectedTile = content.tiles?.data.find(opt => opt.id === newActiveSingleSelect);
|
|
// If applicable, automatically trigger the action for the default
|
|
// selected tile.
|
|
if (isSingleSelect && content.tiles?.autoTrigger && autoTriggerAllowed(selectedTile?.action)) {
|
|
handleAction({
|
|
currentTarget: {
|
|
value: selectedTile.id
|
|
}
|
|
});
|
|
}
|
|
}
|
|
}, []); // eslint-disable-line react-hooks/exhaustive-deps
|
|
|
|
const CONFIGURABLE_STYLES = ["background", "borderRadius", "height", "marginBlock", "marginInline", "paddingBlock", "paddingInline", "width"];
|
|
return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", {
|
|
className: "tiles-single-select-container"
|
|
}, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", null, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("fieldset", {
|
|
className: `tiles-single-select-section ${category}`
|
|
}, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_MSLocalized__WEBPACK_IMPORTED_MODULE_1__.Localized, {
|
|
text: content.subtitle
|
|
}, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("legend", {
|
|
className: "sr-only"
|
|
})), content.tiles.data.map(({
|
|
description,
|
|
icon,
|
|
id,
|
|
label = "",
|
|
theme,
|
|
tooltip,
|
|
type = "",
|
|
flair
|
|
}) => {
|
|
const value = id || theme;
|
|
let inputName = "select-item";
|
|
if (!isSingleSelect) {
|
|
inputName = category === "theme" ? "theme" : id; // unique names per item are currently used in the wallpaper picker
|
|
}
|
|
const selected = theme && theme === activeTheme || isSingleSelect && activeSingleSelect === value;
|
|
const valOrObj = val => typeof val === "object" ? val : {};
|
|
const handleClick = evt => {
|
|
if (isSingleSelect) {
|
|
setActiveSingleSelect(value);
|
|
}
|
|
handleAction(evt);
|
|
};
|
|
const handleKeyDown = evt => {
|
|
if (evt.key === "Enter" || evt.keyCode === 13) {
|
|
// Set target value to the input inside of the selected label
|
|
evt.currentTarget.value = value;
|
|
handleClick(evt);
|
|
}
|
|
};
|
|
return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_MSLocalized__WEBPACK_IMPORTED_MODULE_1__.Localized, {
|
|
key: value + (isSingleSelect ? "" : label),
|
|
text: valOrObj(tooltip)
|
|
}, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("label", {
|
|
className: `select-item ${type}`,
|
|
title: value,
|
|
onKeyDown: e => handleKeyDown(e),
|
|
style: icon?.width ? {
|
|
minWidth: icon.width
|
|
} : {}
|
|
}, flair ? /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_MSLocalized__WEBPACK_IMPORTED_MODULE_1__.Localized, {
|
|
text: valOrObj(flair.text)
|
|
}, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("span", {
|
|
className: "flair"
|
|
})) : "", /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_MSLocalized__WEBPACK_IMPORTED_MODULE_1__.Localized, {
|
|
text: valOrObj(description)
|
|
}, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("input", {
|
|
type: "radio",
|
|
value: value,
|
|
name: inputName,
|
|
checked: selected,
|
|
className: "sr-only input",
|
|
onClick: e => handleClick(e)
|
|
})), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", {
|
|
className: `icon ${selected ? " selected" : ""} ${value}`,
|
|
style: _lib_aboutwelcome_utils_mjs__WEBPACK_IMPORTED_MODULE_2__.AboutWelcomeUtils.getValidStyle(icon, CONFIGURABLE_STYLES)
|
|
}), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_MSLocalized__WEBPACK_IMPORTED_MODULE_1__.Localized, {
|
|
text: label
|
|
}, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", {
|
|
className: "text"
|
|
}))));
|
|
}))));
|
|
};
|
|
|
|
/***/ }),
|
|
/* 17 */
|
|
/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
|
|
|
|
__webpack_require__.r(__webpack_exports__);
|
|
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
|
|
/* harmony export */ MarketplaceButtons: () => (/* binding */ MarketplaceButtons),
|
|
/* harmony export */ MobileDownloads: () => (/* binding */ MobileDownloads)
|
|
/* harmony export */ });
|
|
/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(1);
|
|
/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_0__);
|
|
/* harmony import */ var _MSLocalized__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(5);
|
|
/* harmony import */ var _lib_aboutwelcome_utils_mjs__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(3);
|
|
/* 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/. */
|
|
|
|
|
|
|
|
|
|
const MarketplaceButtons = props => {
|
|
return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("ul", {
|
|
className: "mobile-download-buttons"
|
|
}, props.buttons.includes("ios") ? /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("li", {
|
|
className: "ios"
|
|
}, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("button", {
|
|
"data-l10n-id": "spotlight-ios-marketplace-button",
|
|
value: "ios",
|
|
onClick: props.handleAction
|
|
})) : null, props.buttons.includes("android") ? /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("li", {
|
|
className: "android"
|
|
}, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("button", {
|
|
"data-l10n-id": "spotlight-android-marketplace-button",
|
|
value: "android",
|
|
onClick: props.handleAction
|
|
})) : null);
|
|
};
|
|
const MobileDownloads = props => {
|
|
const {
|
|
QR_code: QRCode
|
|
} = props.data;
|
|
const showEmailLink = props.data.email && window.AWSendToDeviceEmailsSupported();
|
|
return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", {
|
|
className: "mobile-downloads"
|
|
}, QRCode ? /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("img", {
|
|
"data-l10n-id": QRCode.alt_text.string_id ? QRCode.alt_text.string_id : null,
|
|
className: "qr-code-image",
|
|
alt: typeof QRCode.alt_text === "string" ? QRCode.alt_text : "",
|
|
src: QRCode.image_url,
|
|
loading: _lib_aboutwelcome_utils_mjs__WEBPACK_IMPORTED_MODULE_2__.AboutWelcomeUtils.getLoadingStrategyFor(QRCode.image_url)
|
|
}) : null, showEmailLink ? /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", null, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_MSLocalized__WEBPACK_IMPORTED_MODULE_1__.Localized, {
|
|
text: props.data.email.link_text
|
|
}, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("button", {
|
|
className: "email-link",
|
|
value: "email_link",
|
|
onClick: props.handleAction
|
|
}))) : null, props.data.marketplace_buttons ? /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(MarketplaceButtons, {
|
|
buttons: props.data.marketplace_buttons,
|
|
handleAction: props.handleAction
|
|
}) : null);
|
|
};
|
|
|
|
/***/ }),
|
|
/* 18 */
|
|
/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
|
|
|
|
__webpack_require__.r(__webpack_exports__);
|
|
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
|
|
/* harmony export */ MultiSelect: () => (/* binding */ MultiSelect)
|
|
/* harmony export */ });
|
|
/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(1);
|
|
/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_0__);
|
|
/* harmony import */ var _MSLocalized__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(5);
|
|
/* harmony import */ var _lib_aboutwelcome_utils_mjs__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(3);
|
|
/* 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/. */
|
|
|
|
|
|
|
|
|
|
const MULTI_SELECT_STYLES = [..._MSLocalized__WEBPACK_IMPORTED_MODULE_1__.CONFIGURABLE_STYLES, "flexDirection", "flexWrap", "flexFlow", "flexGrow", "flexShrink", "justifyContent", "alignItems", "gap"];
|
|
const TILE_STYLES = ["marginBlock", "marginInline", "paddingBlock", "paddingInline"];
|
|
|
|
// Do not include styles applied at the content tile level
|
|
for (let i = MULTI_SELECT_STYLES.length - 1; i >= 0; i--) {
|
|
if (TILE_STYLES.includes(MULTI_SELECT_STYLES[i])) {
|
|
MULTI_SELECT_STYLES.splice(i, 1);
|
|
}
|
|
}
|
|
const MULTI_SELECT_ICON_STYLES = [..._MSLocalized__WEBPACK_IMPORTED_MODULE_1__.CONFIGURABLE_STYLES, "width", "height", "background", "backgroundColor", "backgroundImage", "backgroundSize", "backgroundPosition", "backgroundRepeat", "backgroundOrigin", "backgroundClip", "border", "borderRadius", "appearance", "fill", "stroke", "outline", "outlineOffset", "boxShadow"];
|
|
const MultiSelect = ({
|
|
content,
|
|
screenMultiSelects,
|
|
setScreenMultiSelects,
|
|
activeMultiSelect,
|
|
setActiveMultiSelect,
|
|
multiSelectId
|
|
}) => {
|
|
const {
|
|
data,
|
|
multiSelectItemDesign
|
|
} = content.tiles;
|
|
const isPicker = multiSelectItemDesign === "picker";
|
|
const refs = (0,react__WEBPACK_IMPORTED_MODULE_0__.useRef)({});
|
|
const handleChange = (0,react__WEBPACK_IMPORTED_MODULE_0__.useCallback)(() => {
|
|
const newActiveMultiSelect = [];
|
|
Object.keys(refs.current).forEach(key => {
|
|
if (refs.current[key]?.checked) {
|
|
newActiveMultiSelect.push(key);
|
|
}
|
|
});
|
|
setActiveMultiSelect(newActiveMultiSelect, multiSelectId);
|
|
}, [setActiveMultiSelect, multiSelectId]);
|
|
const items = (0,react__WEBPACK_IMPORTED_MODULE_0__.useMemo)(() => {
|
|
function getOrderedIds() {
|
|
if (screenMultiSelects) {
|
|
return screenMultiSelects;
|
|
}
|
|
let orderedIds = data.map(item => ({
|
|
id: item.id,
|
|
rank: item.randomize ? Math.random() : NaN
|
|
})).sort((a, b) => b.rank - a.rank).map(({
|
|
id
|
|
}) => id);
|
|
setScreenMultiSelects(orderedIds, multiSelectId);
|
|
return orderedIds;
|
|
}
|
|
return getOrderedIds().map(id => data.find(item => item.id === id));
|
|
}, [] // eslint-disable-line react-hooks/exhaustive-deps
|
|
);
|
|
const containerStyle = (0,react__WEBPACK_IMPORTED_MODULE_0__.useMemo)(() => _lib_aboutwelcome_utils_mjs__WEBPACK_IMPORTED_MODULE_2__.AboutWelcomeUtils.getValidStyle(content.tiles.style, MULTI_SELECT_STYLES, true), [content.tiles.style]);
|
|
const PickerIcon = ({
|
|
emoji,
|
|
bgColor,
|
|
isChecked
|
|
}) => {
|
|
return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("span", {
|
|
className: `picker-icon ${isChecked ? "picker-checked" : ""}`,
|
|
style: {
|
|
...(!isChecked && bgColor && {
|
|
backgroundColor: bgColor
|
|
})
|
|
}
|
|
}, !isChecked && emoji ? emoji : "");
|
|
};
|
|
|
|
// This handles interaction for when the user is clicking on or keyboard-interacting
|
|
// with the container element when using the picker design. It is required
|
|
// for appropriate accessibility.
|
|
const handleCheckboxContainerInteraction = e => {
|
|
if (!isPicker) {
|
|
return;
|
|
}
|
|
if (e.type === "keydown") {
|
|
// Prevent scroll on space presses
|
|
if (e.key === " ") {
|
|
e.preventDefault();
|
|
}
|
|
|
|
// Only handle space and enter keypresses
|
|
if (e.key !== " " && e.key !== "Enter") {
|
|
return;
|
|
}
|
|
}
|
|
const container = e.currentTarget;
|
|
// Manually flip the hidden checkbox since handleChange relies on it
|
|
const checkbox = container.querySelector('input[type="checkbox"]');
|
|
checkbox.checked = !checkbox.checked;
|
|
|
|
// Manually call handleChange to update the multiselect state
|
|
handleChange();
|
|
};
|
|
|
|
// When screen renders for first time, update state
|
|
// with checkbox ids that has defaultvalue true
|
|
(0,react__WEBPACK_IMPORTED_MODULE_0__.useEffect)(() => {
|
|
if (!activeMultiSelect) {
|
|
let newActiveMultiSelect = [];
|
|
items.forEach(({
|
|
id,
|
|
defaultValue
|
|
}) => {
|
|
if (defaultValue && id) {
|
|
newActiveMultiSelect.push(id);
|
|
}
|
|
});
|
|
setActiveMultiSelect(newActiveMultiSelect, multiSelectId);
|
|
}
|
|
}, []); // eslint-disable-line react-hooks/exhaustive-deps
|
|
|
|
return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", {
|
|
className: `multi-select-container ${multiSelectItemDesign || ""}`,
|
|
style: containerStyle,
|
|
role: items.some(({
|
|
type,
|
|
group
|
|
}) => type === "radio" && group) ? "radiogroup" : "group",
|
|
"aria-labelledby": "multi-stage-multi-select-label"
|
|
}, content.tiles.label ? /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_MSLocalized__WEBPACK_IMPORTED_MODULE_1__.Localized, {
|
|
text: content.tiles.label
|
|
}, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("h2", {
|
|
id: "multi-stage-multi-select-label"
|
|
})) : null, items.map(({
|
|
id,
|
|
label,
|
|
description,
|
|
icon,
|
|
type = "checkbox",
|
|
group,
|
|
style,
|
|
pickerEmoji,
|
|
pickerEmojiBackgroundColor
|
|
}) => /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", {
|
|
key: id + label,
|
|
className: "checkbox-container multi-select-item",
|
|
style: _lib_aboutwelcome_utils_mjs__WEBPACK_IMPORTED_MODULE_2__.AboutWelcomeUtils.getValidStyle(style, MULTI_SELECT_STYLES),
|
|
tabIndex: isPicker ? "0" : null,
|
|
onClick: isPicker ? handleCheckboxContainerInteraction : null,
|
|
onKeyDown: isPicker ? handleCheckboxContainerInteraction : null,
|
|
role: isPicker ? "checkbox" : null,
|
|
"aria-checked": isPicker ? activeMultiSelect?.includes(id) : null
|
|
}, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("input", {
|
|
type: type // checkbox or radio
|
|
,
|
|
id: id,
|
|
value: id,
|
|
name: group,
|
|
checked: activeMultiSelect?.includes(id),
|
|
style: _lib_aboutwelcome_utils_mjs__WEBPACK_IMPORTED_MODULE_2__.AboutWelcomeUtils.getValidStyle(icon?.style, MULTI_SELECT_ICON_STYLES),
|
|
onChange: handleChange,
|
|
ref: el => refs.current[id] = el,
|
|
"aria-describedby": description ? `${id}-description` : null,
|
|
tabIndex: isPicker ? "-1" : "0"
|
|
}), isPicker && /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(PickerIcon, {
|
|
emoji: pickerEmoji,
|
|
bgColor: pickerEmojiBackgroundColor,
|
|
isChecked: activeMultiSelect?.includes(id)
|
|
}), label ? /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_MSLocalized__WEBPACK_IMPORTED_MODULE_1__.Localized, {
|
|
text: label
|
|
}, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("label", {
|
|
htmlFor: id
|
|
})) : null, description ? /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_MSLocalized__WEBPACK_IMPORTED_MODULE_1__.Localized, {
|
|
text: description
|
|
}, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("p", {
|
|
id: `${id}-description`
|
|
})) : null)));
|
|
};
|
|
|
|
/***/ }),
|
|
/* 19 */
|
|
/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
|
|
|
|
__webpack_require__.r(__webpack_exports__);
|
|
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
|
|
/* harmony export */ EmbeddedMigrationWizard: () => (/* binding */ EmbeddedMigrationWizard)
|
|
/* harmony export */ });
|
|
/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(1);
|
|
/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_0__);
|
|
/* 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/. */
|
|
|
|
|
|
const EmbeddedMigrationWizard = ({
|
|
handleAction,
|
|
content
|
|
}) => {
|
|
const ref = (0,react__WEBPACK_IMPORTED_MODULE_0__.useRef)();
|
|
const options = content.migration_wizard_options;
|
|
(0,react__WEBPACK_IMPORTED_MODULE_0__.useEffect)(() => {
|
|
const handleBeginMigration = () => {
|
|
handleAction({
|
|
currentTarget: {
|
|
value: "migrate_start"
|
|
},
|
|
source: "primary_button"
|
|
});
|
|
};
|
|
const handleClose = () => {
|
|
handleAction({
|
|
currentTarget: {
|
|
value: "migrate_close"
|
|
}
|
|
});
|
|
};
|
|
const {
|
|
current
|
|
} = ref;
|
|
current?.addEventListener("MigrationWizard:BeginMigration", handleBeginMigration);
|
|
current?.addEventListener("MigrationWizard:Close", handleClose);
|
|
return () => {
|
|
current?.removeEventListener("MigrationWizard:BeginMigration", handleBeginMigration);
|
|
current?.removeEventListener("MigrationWizard:Close", handleClose);
|
|
};
|
|
}, []); // eslint-disable-line react-hooks/exhaustive-deps
|
|
return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("migration-wizard", {
|
|
"in-aboutwelcome-bundle": "",
|
|
"force-show-import-all": options?.force_show_import_all || "false",
|
|
"auto-request-state": "",
|
|
ref: ref,
|
|
"option-expander-title-string": options?.option_expander_title_string || "",
|
|
"hide-option-expander-subtitle": options?.hide_option_expander_subtitle || false,
|
|
"data-import-complete-success-string": options?.data_import_complete_success_string || "",
|
|
"selection-header-string": options?.selection_header_string,
|
|
"selection-subheader-string": options?.selection_subheader_string || "",
|
|
"hide-select-all": options?.hide_select_all || false,
|
|
"checkbox-margin-inline": options?.checkbox_margin_inline || "",
|
|
"checkbox-margin-block": options?.checkbox_margin_block || "",
|
|
"import-button-string": options?.import_button_string || "",
|
|
"import-button-class": options?.import_button_class || "",
|
|
"header-font-size": options?.header_font_size || "",
|
|
"header-font-weight": options?.header_font_weight || "",
|
|
"header-margin-block": options?.header_margin_block || "",
|
|
"subheader-font-size": options?.subheader_font_size || "",
|
|
"subheader-font-weight": options?.subheader_font_weight || "",
|
|
"subheader-margin-block": options?.subheader_margin_block || ""
|
|
});
|
|
};
|
|
|
|
/***/ }),
|
|
/* 20 */
|
|
/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
|
|
|
|
__webpack_require__.r(__webpack_exports__);
|
|
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
|
|
/* harmony export */ ActionChecklist: () => (/* binding */ ActionChecklist),
|
|
/* harmony export */ ActionChecklistItem: () => (/* binding */ ActionChecklistItem),
|
|
/* harmony export */ ActionChecklistProgressBar: () => (/* binding */ ActionChecklistProgressBar)
|
|
/* harmony export */ });
|
|
/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(1);
|
|
/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_0__);
|
|
/* harmony import */ var _MSLocalized__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(5);
|
|
/* harmony import */ var _lib_aboutwelcome_utils_mjs__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(3);
|
|
/* 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/. */
|
|
|
|
|
|
|
|
|
|
async function evaluateTargeting(targeting) {
|
|
return await window.AWEvaluateAttributeTargeting(targeting);
|
|
}
|
|
const ActionChecklistItem = ({
|
|
item,
|
|
index,
|
|
handleAction,
|
|
showExternalLinkIcon
|
|
}) => {
|
|
const [actionTargeting, setActionTargeting] = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)(true);
|
|
(0,react__WEBPACK_IMPORTED_MODULE_0__.useEffect)(() => {
|
|
const setInitialTargetingValue = async () => {
|
|
setActionTargeting(await evaluateTargeting(item.targeting));
|
|
};
|
|
setInitialTargetingValue();
|
|
}, []); // eslint-disable-line react-hooks/exhaustive-deps
|
|
|
|
function onButtonClick(event) {
|
|
// Immediately set targeting to true to disable the button.
|
|
// It will re-evaluate its targeting on the next load.
|
|
setActionTargeting(true);
|
|
handleAction(event);
|
|
}
|
|
return (
|
|
/*#__PURE__*/
|
|
// if actionTargeting is false, we want the button to be enabled
|
|
// because it signifies that the action is not yet complete.
|
|
// If it is true, the action has been completed, so we can disable the button.
|
|
react__WEBPACK_IMPORTED_MODULE_0___default().createElement("button", {
|
|
id: item.id,
|
|
value: index,
|
|
key: item.id,
|
|
disabled: actionTargeting,
|
|
onClick: onButtonClick
|
|
}, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", {
|
|
className: "action-checklist-label-container"
|
|
}, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", {
|
|
className: "check-icon-container"
|
|
}, actionTargeting ? /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", {
|
|
className: "check-filled"
|
|
}) : /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", {
|
|
className: "check-empty"
|
|
})), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_MSLocalized__WEBPACK_IMPORTED_MODULE_1__.Localized, {
|
|
text: item.label
|
|
}, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("span", null))), !actionTargeting && showExternalLinkIcon && /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", {
|
|
className: "external-link-icon-container"
|
|
}, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", {
|
|
className: "external-link-icon"
|
|
})))
|
|
);
|
|
};
|
|
const ActionChecklistProgressBar = ({
|
|
progress
|
|
}) => {
|
|
return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", {
|
|
className: "action-checklist-progress-bar"
|
|
}, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("progress", {
|
|
className: "sr-only",
|
|
value: progress || 0,
|
|
max: "100"
|
|
}), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", {
|
|
className: "indicator",
|
|
role: "presentation",
|
|
style: {
|
|
"--action-checklist-progress-bar-progress": `${progress || 0}%`
|
|
}
|
|
}));
|
|
};
|
|
const ActionChecklist = ({
|
|
content,
|
|
message_id
|
|
}) => {
|
|
const tiles = content.tiles.data;
|
|
const [progressValue, setProgressValue] = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)(0);
|
|
const [numberOfCompletedActions, setNumberOfCompletedActions] = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)(0);
|
|
function determineProgressValue() {
|
|
let newValue = numberOfCompletedActions / tiles.length * 100;
|
|
setProgressValue(newValue);
|
|
}
|
|
|
|
// This instance of useEffect is to evaluate the targeting of each individual action
|
|
// when the component is initially loaded so that we can accurately populate the progress bar.
|
|
// We're doing the heavy lifting here once on load, and keeping the rest of the information
|
|
// regarding how many actions are complete handy in state for quick access,
|
|
// and a lesser performance hit.
|
|
(0,react__WEBPACK_IMPORTED_MODULE_0__.useEffect)(() => {
|
|
let evaluateAllActionsTargeting = async () => {
|
|
let completedActions = await Promise.all(tiles.map(async item => await evaluateTargeting(item.targeting)));
|
|
let numCompletedActions = completedActions.filter(item => item).length;
|
|
setNumberOfCompletedActions(numCompletedActions);
|
|
};
|
|
evaluateAllActionsTargeting();
|
|
}, []); // eslint-disable-line react-hooks/exhaustive-deps
|
|
|
|
// This instance of useEffect is to initially update the progress bar,
|
|
// and to also update the progress bar each time an action is completed.
|
|
(0,react__WEBPACK_IMPORTED_MODULE_0__.useEffect)(() => {
|
|
determineProgressValue();
|
|
}, [numberOfCompletedActions]); // eslint-disable-line react-hooks/exhaustive-deps
|
|
|
|
function handleAction(event) {
|
|
let {
|
|
action,
|
|
source_id
|
|
} = content.tiles.data[event.currentTarget.value];
|
|
let {
|
|
type,
|
|
data
|
|
} = action;
|
|
setNumberOfCompletedActions(numberOfCompletedActions + 1);
|
|
_lib_aboutwelcome_utils_mjs__WEBPACK_IMPORTED_MODULE_2__.AboutWelcomeUtils.handleUserAction({
|
|
type,
|
|
data
|
|
});
|
|
_lib_aboutwelcome_utils_mjs__WEBPACK_IMPORTED_MODULE_2__.AboutWelcomeUtils.sendActionTelemetry(message_id, source_id);
|
|
}
|
|
return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", {
|
|
className: "action-checklist"
|
|
}, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("hr", {
|
|
className: "action-checklist-divider"
|
|
}), content.action_checklist_subtitle && /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_MSLocalized__WEBPACK_IMPORTED_MODULE_1__.Localized, {
|
|
text: content.action_checklist_subtitle
|
|
}, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("p", {
|
|
className: "action-checklist-subtitle"
|
|
})), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(ActionChecklistProgressBar, {
|
|
progress: progressValue
|
|
}), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", {
|
|
className: "action-checklist-items"
|
|
}, tiles.map((item, index) => /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(ActionChecklistItem, {
|
|
key: item.id,
|
|
index: index,
|
|
item: item,
|
|
handleAction: handleAction,
|
|
showExternalLinkIcon: item.showExternalLinkIcon
|
|
}))));
|
|
};
|
|
|
|
/***/ }),
|
|
/* 21 */
|
|
/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
|
|
|
|
__webpack_require__.r(__webpack_exports__);
|
|
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
|
|
/* harmony export */ EmbeddedBrowser: () => (/* binding */ EmbeddedBrowser),
|
|
/* harmony export */ "default": () => (__WEBPACK_DEFAULT_EXPORT__)
|
|
/* harmony export */ });
|
|
/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(1);
|
|
/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_0__);
|
|
/* harmony import */ var _lib_aboutwelcome_utils_mjs__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(3);
|
|
/* 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 https://mozilla.org/MPL/2.0/. */
|
|
|
|
|
|
|
|
const BROWSER_STYLES = ["height", "width", "border", "borderRadius", "flex", "margin", "padding"];
|
|
const EmbeddedBrowser = props => {
|
|
// Conditionally render the component only if the environment supports XULElements (such as in Spotlight modals)
|
|
return document.createXULElement && props.url ? /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(EmbeddedBrowserInner, props) : null;
|
|
};
|
|
const EmbeddedBrowserInner = ({
|
|
url,
|
|
style
|
|
}) => {
|
|
const ref = (0,react__WEBPACK_IMPORTED_MODULE_0__.useRef)(null);
|
|
const browserRef = (0,react__WEBPACK_IMPORTED_MODULE_0__.useRef)(null);
|
|
(0,react__WEBPACK_IMPORTED_MODULE_0__.useEffect)(() => {
|
|
if (!ref.current || browserRef.current) {
|
|
return;
|
|
}
|
|
const browserEl = document.createXULElement("browser");
|
|
const remoteType = window.AWPredictRemoteType({
|
|
browserEl,
|
|
url
|
|
});
|
|
const attributes = [["disableglobalhistory", "true"], ["type", "content"], ["remote", "true"], ["maychangeremoteness", "true"], ["nodefaultsrc", "true"], ["remoteType", remoteType]];
|
|
attributes.forEach(([attr, val]) => browserEl.setAttribute(attr, val));
|
|
browserRef.current = browserEl;
|
|
ref.current.appendChild(browserEl);
|
|
// Initialize the browser element only once when the component mounts. The
|
|
// empty dependency array ensures this effect runs only on the first render.
|
|
}, []); // eslint-disable-line react-hooks/exhaustive-deps
|
|
|
|
(0,react__WEBPACK_IMPORTED_MODULE_0__.useEffect)(() => {
|
|
if (browserRef.current) {
|
|
browserRef.current.fixupAndLoadURIString(url, {
|
|
triggeringPrincipal: Services.scriptSecurityManager.createNullPrincipal({})
|
|
});
|
|
}
|
|
}, [url]);
|
|
(0,react__WEBPACK_IMPORTED_MODULE_0__.useEffect)(() => {
|
|
if (browserRef.current && style) {
|
|
const validStyles = _lib_aboutwelcome_utils_mjs__WEBPACK_IMPORTED_MODULE_1__.AboutWelcomeUtils.getValidStyle(style, BROWSER_STYLES);
|
|
Object.keys(validStyles).forEach(key => {
|
|
browserRef.current.style.setProperty(key, style[key]);
|
|
});
|
|
}
|
|
}, [style]);
|
|
return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", {
|
|
className: "embedded-browser-container",
|
|
ref: ref
|
|
});
|
|
};
|
|
/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (EmbeddedBrowser);
|
|
|
|
/***/ }),
|
|
/* 22 */
|
|
/***/ ((__unused_webpack___webpack_module__, __webpack_exports__, __webpack_require__) => {
|
|
|
|
__webpack_require__.r(__webpack_exports__);
|
|
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
|
|
/* harmony export */ BASE_PARAMS: () => (/* binding */ BASE_PARAMS),
|
|
/* harmony export */ addUtmParams: () => (/* binding */ addUtmParams)
|
|
/* harmony export */ });
|
|
/* 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/. */
|
|
|
|
/**
|
|
* BASE_PARAMS keys/values can be modified from outside this file
|
|
*/
|
|
const BASE_PARAMS = {
|
|
utm_source: "activity-stream",
|
|
utm_campaign: "firstrun",
|
|
utm_medium: "referral",
|
|
};
|
|
|
|
/**
|
|
* Takes in a url as a string or URL object and returns a URL object with the
|
|
* utm_* parameters added to it. If a URL object is passed in, the paraemeters
|
|
* are added to it (the return value can be ignored in that case as it's the
|
|
* same object).
|
|
*/
|
|
function addUtmParams(url, utmTerm) {
|
|
let returnUrl = url;
|
|
if (typeof returnUrl === "string") {
|
|
returnUrl = new URL(url);
|
|
}
|
|
for (let [key, value] of Object.entries(BASE_PARAMS)) {
|
|
if (!returnUrl.searchParams.has(key)) {
|
|
returnUrl.searchParams.append(key, value);
|
|
}
|
|
}
|
|
returnUrl.searchParams.append("utm_term", utmTerm);
|
|
return returnUrl;
|
|
}
|
|
|
|
|
|
/***/ }),
|
|
/* 23 */
|
|
/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
|
|
|
|
__webpack_require__.r(__webpack_exports__);
|
|
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
|
|
/* harmony export */ ReturnToAMO: () => (/* binding */ ReturnToAMO)
|
|
/* harmony export */ });
|
|
/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(1);
|
|
/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_0__);
|
|
/* harmony import */ var _lib_aboutwelcome_utils_mjs__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(3);
|
|
/* harmony import */ var _MultiStageProtonScreen__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(6);
|
|
/* harmony import */ var _lib_addUtmParams_mjs__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(22);
|
|
/* 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/. */
|
|
|
|
|
|
|
|
|
|
|
|
class ReturnToAMO extends (react__WEBPACK_IMPORTED_MODULE_0___default().PureComponent) {
|
|
constructor(props) {
|
|
super(props);
|
|
this.fetchFlowParams = this.fetchFlowParams.bind(this);
|
|
this.handleAction = this.handleAction.bind(this);
|
|
}
|
|
async fetchFlowParams() {
|
|
if (this.props.metricsFlowUri) {
|
|
this.setState({
|
|
flowParams: await _lib_aboutwelcome_utils_mjs__WEBPACK_IMPORTED_MODULE_1__.AboutWelcomeUtils.fetchFlowParams(this.props.metricsFlowUri)
|
|
});
|
|
}
|
|
}
|
|
componentDidUpdate() {
|
|
this.fetchFlowParams();
|
|
}
|
|
handleAction(event) {
|
|
const {
|
|
content,
|
|
message_id,
|
|
url,
|
|
utm_term
|
|
} = this.props;
|
|
let {
|
|
action,
|
|
source_id
|
|
} = content[event.currentTarget.value];
|
|
let {
|
|
type,
|
|
data
|
|
} = action;
|
|
if (type === "INSTALL_ADDON_FROM_URL") {
|
|
if (!data) {
|
|
return;
|
|
}
|
|
// Set add-on url in action.data.url property from JSON
|
|
data = {
|
|
...data,
|
|
url
|
|
};
|
|
} else if (type === "SHOW_FIREFOX_ACCOUNTS") {
|
|
let params = {
|
|
..._lib_addUtmParams_mjs__WEBPACK_IMPORTED_MODULE_3__.BASE_PARAMS,
|
|
utm_term: `aboutwelcome-${utm_term}-screen`
|
|
};
|
|
if (action.addFlowParams && this.state.flowParams) {
|
|
params = {
|
|
...params,
|
|
...this.state.flowParams
|
|
};
|
|
}
|
|
data = {
|
|
...data,
|
|
extraParams: params
|
|
};
|
|
}
|
|
_lib_aboutwelcome_utils_mjs__WEBPACK_IMPORTED_MODULE_1__.AboutWelcomeUtils.handleUserAction({
|
|
type,
|
|
data
|
|
});
|
|
_lib_aboutwelcome_utils_mjs__WEBPACK_IMPORTED_MODULE_1__.AboutWelcomeUtils.sendActionTelemetry(message_id, source_id);
|
|
}
|
|
render() {
|
|
const {
|
|
content,
|
|
type
|
|
} = this.props;
|
|
if (!content) {
|
|
return null;
|
|
}
|
|
if (content?.primary_button.label) {
|
|
content.primary_button.label.string_id = type.includes("theme") ? "return-to-amo-add-theme-label" : "mr1-return-to-amo-add-extension-label";
|
|
}
|
|
|
|
// For experiments, when needed below rendered UI allows settings hard coded strings
|
|
// directly inside JSON except for ReturnToAMOText which picks add-on name and icon from fluent string
|
|
return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", {
|
|
className: "outer-wrapper onboardingContainer proton",
|
|
style: content.backdrop ? {
|
|
background: content.backdrop
|
|
} : {}
|
|
}, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_MultiStageProtonScreen__WEBPACK_IMPORTED_MODULE_2__.MultiStageProtonScreen, {
|
|
content: content,
|
|
isRtamo: true,
|
|
isTheme: type.includes("theme"),
|
|
id: this.props.message_id,
|
|
order: this.props.order || 0,
|
|
totalNumberOfScreens: 1,
|
|
isSingleScreen: true,
|
|
autoAdvance: this.props.auto_advance,
|
|
iconURL: type.includes("theme") ? this.props.themeScreenshots[0]?.url : this.props.iconURL,
|
|
addonName: this.props.name,
|
|
handleAction: this.handleAction
|
|
}));
|
|
}
|
|
}
|
|
ReturnToAMO.defaultProps = _lib_aboutwelcome_utils_mjs__WEBPACK_IMPORTED_MODULE_1__.DEFAULT_RTAMO_CONTENT;
|
|
|
|
/***/ })
|
|
/******/ ]);
|
|
/************************************************************************/
|
|
/******/ // The module cache
|
|
/******/ var __webpack_module_cache__ = {};
|
|
/******/
|
|
/******/ // The require function
|
|
/******/ function __webpack_require__(moduleId) {
|
|
/******/ // Check if module is in cache
|
|
/******/ var cachedModule = __webpack_module_cache__[moduleId];
|
|
/******/ if (cachedModule !== undefined) {
|
|
/******/ return cachedModule.exports;
|
|
/******/ }
|
|
/******/ // Create a new module (and put it into the cache)
|
|
/******/ var module = __webpack_module_cache__[moduleId] = {
|
|
/******/ // no module.id needed
|
|
/******/ // no module.loaded needed
|
|
/******/ exports: {}
|
|
/******/ };
|
|
/******/
|
|
/******/ // Execute the module function
|
|
/******/ __webpack_modules__[moduleId](module, module.exports, __webpack_require__);
|
|
/******/
|
|
/******/ // Return the exports of the module
|
|
/******/ return module.exports;
|
|
/******/ }
|
|
/******/
|
|
/************************************************************************/
|
|
/******/ /* webpack/runtime/compat get default export */
|
|
/******/ (() => {
|
|
/******/ // getDefaultExport function for compatibility with non-harmony modules
|
|
/******/ __webpack_require__.n = (module) => {
|
|
/******/ var getter = module && module.__esModule ?
|
|
/******/ () => (module['default']) :
|
|
/******/ () => (module);
|
|
/******/ __webpack_require__.d(getter, { a: getter });
|
|
/******/ return getter;
|
|
/******/ };
|
|
/******/ })();
|
|
/******/
|
|
/******/ /* webpack/runtime/define property getters */
|
|
/******/ (() => {
|
|
/******/ // define getter functions for harmony exports
|
|
/******/ __webpack_require__.d = (exports, definition) => {
|
|
/******/ for(var key in definition) {
|
|
/******/ if(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) {
|
|
/******/ Object.defineProperty(exports, key, { enumerable: true, get: definition[key] });
|
|
/******/ }
|
|
/******/ }
|
|
/******/ };
|
|
/******/ })();
|
|
/******/
|
|
/******/ /* webpack/runtime/hasOwnProperty shorthand */
|
|
/******/ (() => {
|
|
/******/ __webpack_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop))
|
|
/******/ })();
|
|
/******/
|
|
/******/ /* webpack/runtime/make namespace object */
|
|
/******/ (() => {
|
|
/******/ // define __esModule on exports
|
|
/******/ __webpack_require__.r = (exports) => {
|
|
/******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) {
|
|
/******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
|
|
/******/ }
|
|
/******/ Object.defineProperty(exports, '__esModule', { value: true });
|
|
/******/ };
|
|
/******/ })();
|
|
/******/
|
|
/************************************************************************/
|
|
var __webpack_exports__ = {};
|
|
// This entry needs to be wrapped in an IIFE because it needs to be isolated against other modules in the chunk.
|
|
(() => {
|
|
__webpack_require__.r(__webpack_exports__);
|
|
/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(1);
|
|
/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_0__);
|
|
/* harmony import */ var react_dom__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(2);
|
|
/* harmony import */ var react_dom__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(react_dom__WEBPACK_IMPORTED_MODULE_1__);
|
|
/* harmony import */ var _lib_aboutwelcome_utils_mjs__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(3);
|
|
/* harmony import */ var _components_MultiStageAboutWelcome__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(4);
|
|
/* harmony import */ var _components_ReturnToAMO__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(23);
|
|
function _extends() { _extends = Object.assign ? Object.assign.bind() : function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); }
|
|
/* 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/. */
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class AboutWelcome extends (react__WEBPACK_IMPORTED_MODULE_0___default().PureComponent) {
|
|
constructor(props) {
|
|
super(props);
|
|
this.state = {
|
|
metricsFlowUri: null
|
|
};
|
|
this.fetchFxAFlowUri = this.fetchFxAFlowUri.bind(this);
|
|
}
|
|
async fetchFxAFlowUri() {
|
|
this.setState({
|
|
metricsFlowUri: await window.AWGetFxAMetricsFlowURI?.()
|
|
});
|
|
}
|
|
componentDidMount() {
|
|
if (!this.props.skipFxA) {
|
|
this.fetchFxAFlowUri();
|
|
}
|
|
if (document.location.href === "about:welcome") {
|
|
// Record impression with performance data after allowing the page to load
|
|
const recordImpression = domState => {
|
|
const {
|
|
domComplete,
|
|
domInteractive
|
|
} = performance.getEntriesByType("navigation").pop();
|
|
_lib_aboutwelcome_utils_mjs__WEBPACK_IMPORTED_MODULE_2__.AboutWelcomeUtils.sendImpressionTelemetry(this.props.messageId, {
|
|
domComplete,
|
|
domInteractive,
|
|
mountStart: performance.getEntriesByName("mount").pop().startTime,
|
|
domState,
|
|
source: this.props.UTMTerm
|
|
});
|
|
};
|
|
if (document.readyState === "complete") {
|
|
// Page might have already triggered a load event because it waited for async data,
|
|
// e.g., attribution, so the dom load timing could be of a empty content
|
|
// with domState in telemetry captured as 'complete'
|
|
recordImpression(document.readyState);
|
|
} else {
|
|
window.addEventListener("load", () => recordImpression("load"), {
|
|
once: true
|
|
});
|
|
}
|
|
|
|
// Captures user has seen about:welcome by setting
|
|
// firstrun.didSeeAboutWelcome pref to true and capturing welcome UI unique messageId
|
|
window.AWSendToParent("SET_WELCOME_MESSAGE_SEEN", this.props.messageId);
|
|
}
|
|
}
|
|
render() {
|
|
const {
|
|
props
|
|
} = this;
|
|
if (props.template === "return_to_amo") {
|
|
return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_components_ReturnToAMO__WEBPACK_IMPORTED_MODULE_4__.ReturnToAMO, {
|
|
message_id: props.messageId,
|
|
type: props.type,
|
|
name: props.name,
|
|
url: props.url,
|
|
iconURL: props.iconURL,
|
|
themeScreenshots: props.screenshots,
|
|
metricsFlowUri: this.state.metricsFlowUri
|
|
});
|
|
}
|
|
return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_components_MultiStageAboutWelcome__WEBPACK_IMPORTED_MODULE_3__.MultiStageAboutWelcome, {
|
|
message_id: props.messageId,
|
|
defaultScreens: props.screens,
|
|
updateHistory: !props.disableHistoryUpdates,
|
|
metricsFlowUri: this.state.metricsFlowUri,
|
|
utm_term: props.UTMTerm,
|
|
transitions: props.transitions,
|
|
backdrop: props.backdrop,
|
|
startScreen: props.startScreen || 0,
|
|
appAndSystemLocaleInfo: props.appAndSystemLocaleInfo,
|
|
ariaRole: props.aria_role
|
|
});
|
|
}
|
|
}
|
|
|
|
// Computes messageId and UTMTerm info used in telemetry
|
|
function ComputeTelemetryInfo(welcomeContent, experimentId, branchId) {
|
|
let messageId = welcomeContent.template === "return_to_amo" ? `RTAMO_DEFAULT_WELCOME_${welcomeContent.type.toUpperCase()}` : "DEFAULT_ID";
|
|
let UTMTerm = "aboutwelcome-default";
|
|
if (welcomeContent.id) {
|
|
messageId = welcomeContent.id.toUpperCase();
|
|
}
|
|
if (experimentId && branchId) {
|
|
UTMTerm = `aboutwelcome-${experimentId}-${branchId}`.toLowerCase();
|
|
}
|
|
return {
|
|
messageId,
|
|
UTMTerm
|
|
};
|
|
}
|
|
async function retrieveRenderContent() {
|
|
// Feature config includes RTAMO attribution data if exists
|
|
// else below data in order specified
|
|
// user prefs
|
|
// experiment data
|
|
// defaults
|
|
let featureConfig = await window.AWGetFeatureConfig();
|
|
let {
|
|
messageId,
|
|
UTMTerm
|
|
} = ComputeTelemetryInfo(featureConfig, featureConfig.slug, featureConfig.branch && featureConfig.branch.slug);
|
|
return {
|
|
featureConfig,
|
|
messageId,
|
|
UTMTerm
|
|
};
|
|
}
|
|
async function mount() {
|
|
let {
|
|
featureConfig: aboutWelcomeProps,
|
|
messageId,
|
|
UTMTerm
|
|
} = await retrieveRenderContent();
|
|
react_dom__WEBPACK_IMPORTED_MODULE_1___default().render(/*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(AboutWelcome, _extends({
|
|
messageId: messageId,
|
|
UTMTerm: UTMTerm
|
|
}, aboutWelcomeProps)), document.getElementById("multi-stage-message-root"));
|
|
}
|
|
performance.mark("mount");
|
|
mount();
|
|
})();
|
|
|
|
/******/ })()
|
|
; |