aboutwelcome.bundle.js (181119B)
1 /*! 2 * 3 * NOTE: This file is generated by webpack from aboutwelcome.jsx 4 * using the npm bundle task. 5 * 6 */ 7 /******/ (() => { // webpackBootstrap 8 /******/ "use strict"; 9 /******/ var __webpack_modules__ = ([ 10 /* 0 */, 11 /* 1 */ 12 /***/ ((module) => { 13 14 module.exports = React; 15 16 /***/ }), 17 /* 2 */ 18 /***/ ((module) => { 19 20 module.exports = ReactDOM; 21 22 /***/ }), 23 /* 3 */ 24 /***/ ((__unused_webpack___webpack_module__, __webpack_exports__, __webpack_require__) => { 25 26 __webpack_require__.r(__webpack_exports__); 27 /* harmony export */ __webpack_require__.d(__webpack_exports__, { 28 /* harmony export */ AboutWelcomeUtils: () => (/* binding */ AboutWelcomeUtils) 29 /* harmony export */ }); 30 /* This Source Code Form is subject to the terms of the Mozilla Public 31 * License, v. 2.0. If a copy of the MPL was not distributed with this file, 32 * You can obtain one at http://mozilla.org/MPL/2.0/. */ 33 34 // If the container has a "page" data attribute, then this is 35 // a Spotlight modal or Feature Callout. Otherwise, this is 36 // about:welcome and we should return the current page. 37 const page = 38 document.querySelector( 39 "#multi-stage-message-root.onboardingContainer[data-page]" 40 )?.dataset.page || document.location.href; 41 42 const AboutWelcomeUtils = { 43 handleUserAction(action) { 44 return window.AWSendToParent("SPECIAL_ACTION", action); 45 }, 46 sendImpressionTelemetry(messageId, context) { 47 window.AWSendEventTelemetry?.({ 48 event: "IMPRESSION", 49 event_context: { 50 ...context, 51 page, 52 }, 53 message_id: messageId, 54 }); 55 }, 56 sendActionTelemetry(messageId, elementId, eventName = "CLICK_BUTTON") { 57 const ping = { 58 event: eventName, 59 event_context: { 60 source: elementId, 61 page, 62 }, 63 message_id: messageId, 64 }; 65 window.AWSendEventTelemetry?.(ping); 66 }, 67 sendDismissTelemetry(messageId, elementId) { 68 // Don't send DISMISS telemetry in spotlight modals since they already send 69 // their own equivalent telemetry. 70 if (page !== "spotlight") { 71 this.sendActionTelemetry(messageId, elementId, "DISMISS"); 72 } 73 }, 74 async fetchFlowParams(metricsFlowUri) { 75 let flowParams; 76 try { 77 const response = await fetch(metricsFlowUri, { 78 credentials: "omit", 79 }); 80 if (response.status === 200) { 81 const { deviceId, flowId, flowBeginTime } = await response.json(); 82 flowParams = { deviceId, flowId, flowBeginTime }; 83 } else { 84 console.error("Non-200 response", response); 85 } 86 } catch (e) { 87 flowParams = null; 88 } 89 return flowParams; 90 }, 91 sendEvent(type, detail) { 92 document.dispatchEvent( 93 new CustomEvent(`AWPage:${type}`, { 94 bubbles: true, 95 detail, 96 }) 97 ); 98 }, 99 getLoadingStrategyFor(url) { 100 return url?.startsWith("http") ? "lazy" : "eager"; 101 }, 102 handleCampaignAction(action, messageId) { 103 window.AWSendToParent("HANDLE_CAMPAIGN_ACTION", action).then(handled => { 104 if (handled) { 105 this.sendActionTelemetry(messageId, "CAMPAIGN_ACTION"); 106 } 107 }); 108 }, 109 getValidStyle(style, validStyles, allowVars) { 110 if (!style) { 111 return null; 112 } 113 return Object.keys(style) 114 .filter( 115 key => validStyles.includes(key) || (allowVars && key.startsWith("--")) 116 ) 117 .reduce((obj, key) => { 118 obj[key] = style[key]; 119 return obj; 120 }, {}); 121 }, 122 getTileStyle(tile, validStyle) { 123 const preferredTileStyle = tile?.style; 124 const legacyTileStyle = tile?.tiles?.style ?? null; 125 126 return this.getValidStyle( 127 preferredTileStyle ?? legacyTileStyle, 128 validStyle, 129 true 130 ); 131 }, 132 }; 133 134 135 /***/ }), 136 /* 4 */ 137 /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { 138 139 __webpack_require__.r(__webpack_exports__); 140 /* harmony export */ __webpack_require__.d(__webpack_exports__, { 141 /* harmony export */ MultiStageAboutWelcome: () => (/* binding */ MultiStageAboutWelcome), 142 /* harmony export */ ProgressBar: () => (/* binding */ ProgressBar), 143 /* harmony export */ SecondaryCTA: () => (/* binding */ SecondaryCTA), 144 /* harmony export */ StepsIndicator: () => (/* binding */ StepsIndicator), 145 /* harmony export */ WelcomeScreen: () => (/* binding */ WelcomeScreen) 146 /* harmony export */ }); 147 /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(1); 148 /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_0__); 149 /* harmony import */ var _MSLocalized__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(5); 150 /* harmony import */ var _lib_aboutwelcome_utils_mjs__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(3); 151 /* harmony import */ var _MultiStageProtonScreen__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(6); 152 /* harmony import */ var _LanguageSwitcher__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(7); 153 /* harmony import */ var _SubmenuButton__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(12); 154 /* harmony import */ var _lib_addUtmParams_mjs__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(28); 155 /* This Source Code Form is subject to the terms of the Mozilla Public 156 * License, v. 2.0. If a copy of the MPL was not distributed with this file, 157 * You can obtain one at http://mozilla.org/MPL/2.0/. */ 158 159 160 161 162 163 164 165 166 167 // Amount of milliseconds for all transitions to complete (including delays). 168 const TRANSITION_OUT_TIME = 1000; 169 const LANGUAGE_MISMATCH_SCREEN_ID = "AW_LANGUAGE_MISMATCH"; 170 const MultiStageAboutWelcome = props => { 171 const gateInitialPaint = props.gateInitialPaint ?? false; 172 let { 173 defaultScreens 174 } = props; 175 const didFilter = (0,react__WEBPACK_IMPORTED_MODULE_0__.useRef)(false); 176 const [didMount, setDidMount] = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)(false); 177 const [screens, setScreens] = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)(defaultScreens); 178 const [index, setScreenIndex] = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)(props.startScreen); 179 const [previousOrder, setPreviousOrder] = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)(props.startScreen - 1); 180 // Gate first paint until we've finished the initial filtering pass. 181 const [ready, setReady] = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)(false); 182 (0,react__WEBPACK_IMPORTED_MODULE_0__.useEffect)(() => { 183 (async () => { 184 // If we want to load index from history state, we don't want to send impression yet 185 if (!didMount) { 186 return; 187 } 188 // On about:welcome first load, screensVisited should be empty 189 let screensVisited = didFilter.current ? screens.slice(0, index) : []; 190 let upcomingScreens = defaultScreens.filter(s => !screensVisited.find(v => v.id === s.id)) 191 // Filter out Language Mismatch screen from upcoming 192 // screens if screens set from useLanguageSwitcher hook 193 // has filtered language screen 194 .filter(upcomingScreen => !(!screens.find(s => s.id === LANGUAGE_MISMATCH_SCREEN_ID) && upcomingScreen.id === LANGUAGE_MISMATCH_SCREEN_ID)); 195 let filteredScreens = screensVisited.concat((await window.AWEvaluateScreenTargeting(upcomingScreens)) ?? upcomingScreens); 196 197 // Use existing screen for the filtered screen to carry over any modification 198 // e.g. if AW_LANGUAGE_MISMATCH exists, use it from existing screens 199 setScreens(filteredScreens.map(filtered => screens.find(s => s.id === filtered.id) ?? filtered)); 200 // Mark the initial filter pass complete and allow the first paint. 201 if (!didFilter.current) { 202 didFilter.current = true; 203 setReady(true); 204 } 205 206 // After completing screen filtering, trigger any unhandled campaign 207 // action present in the attribution campaign data. This updates the 208 // "trailhead.firstrun.didHandleCampaignAction" preference, marking the 209 // actions as complete to prevent them from being handled on subsequent 210 // visits to about:welcome. Do not await getting the action to avoid 211 // blocking the thread. 212 window.AWGetUnhandledCampaignAction?.().then(action => { 213 if (typeof action === "string") { 214 _lib_aboutwelcome_utils_mjs__WEBPACK_IMPORTED_MODULE_2__.AboutWelcomeUtils.handleCampaignAction(action, props.message_id); 215 } 216 }).catch(error => { 217 console.error("Failed to get unhandled campaign action:", error); 218 }); 219 const screenInitials = filteredScreens.map(({ 220 id 221 }) => id?.split("_")[1]?.[0]).join(""); 222 // Send impression ping when respective screen first renders 223 // eslint-disable-next-line no-shadow 224 filteredScreens.forEach((screen, order) => { 225 if (index === order) { 226 const messageId = `${props.message_id}_${order}_${screen.id}_${screenInitials}`; 227 _lib_aboutwelcome_utils_mjs__WEBPACK_IMPORTED_MODULE_2__.AboutWelcomeUtils.sendImpressionTelemetry(messageId, { 228 screen_family: props.message_id, 229 screen_index: order, 230 screen_id: screen.id, 231 screen_initials: screenInitials 232 }); 233 window.AWAddScreenImpression?.(screen); 234 } 235 }); 236 237 // Remember that a new screen has loaded for browser navigation 238 if (props.updateHistory && index > window.history.state) { 239 window.history.pushState(index, ""); 240 } 241 242 // Remember the previous screen index so we can animate the transition 243 setPreviousOrder(index); 244 })(); 245 }, [index, didMount]); // eslint-disable-line react-hooks/exhaustive-deps 246 247 const [flowParams, setFlowParams] = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)(null); 248 const { 249 metricsFlowUri 250 } = props; 251 (0,react__WEBPACK_IMPORTED_MODULE_0__.useEffect)(() => { 252 (async () => { 253 if (metricsFlowUri) { 254 setFlowParams(await _lib_aboutwelcome_utils_mjs__WEBPACK_IMPORTED_MODULE_2__.AboutWelcomeUtils.fetchFlowParams(metricsFlowUri)); 255 } 256 })(); 257 }, [metricsFlowUri]); 258 259 // Allow "in" style to render to actually transition towards regular state, 260 // which also makes using browser back/forward navigation skip transitions. 261 const [transition, setTransition] = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)(props.transitions ? "in" : ""); 262 (0,react__WEBPACK_IMPORTED_MODULE_0__.useEffect)(() => { 263 if (transition === "in") { 264 requestAnimationFrame(() => requestAnimationFrame(() => setTransition(""))); 265 } 266 }, [transition]); 267 268 // Transition to next screen, opening about:home on last screen button CTA 269 const handleTransition = goBack => { 270 // Only handle transitioning out from a screen once. 271 if (transition === "out") { 272 return; 273 } 274 275 // Start transitioning things "out" immediately when moving forwards. 276 setTransition(props.transitions ? "out" : ""); 277 278 // Actually move forwards after all transitions finish. 279 setTimeout(() => { 280 if (goBack) { 281 setTransition(props.transitions ? "in" : ""); 282 setScreenIndex(prevState => prevState - 1); 283 } else if (index < screens.length - 1) { 284 setTransition(props.transitions ? "in" : ""); 285 setScreenIndex(prevState => prevState + 1); 286 } else { 287 window.AWFinish(); 288 } 289 }, props.transitions ? TRANSITION_OUT_TIME : 0); 290 }; 291 (0,react__WEBPACK_IMPORTED_MODULE_0__.useEffect)(() => { 292 // When about:welcome loads (on refresh or pressing back button 293 // from about:home), ensure history state usEffect runs before 294 // useEffect hook that send impression telemetry 295 setDidMount(true); 296 if (props.updateHistory) { 297 // Switch to the screen tracked in state (null for initial state) 298 // or last screen index if a user navigates by pressing back 299 // button from about:home 300 const handler = ({ 301 state 302 }) => { 303 if (transition === "out") { 304 return; 305 } 306 setTransition(props.transitions ? "out" : ""); 307 setTimeout(() => { 308 setTransition(props.transitions ? "in" : ""); 309 setScreenIndex(Math.min(state, screens.length - 1)); 310 }, props.transitions ? TRANSITION_OUT_TIME : 0); 311 }; 312 313 // Handle page load, e.g., going back to about:welcome from about:home 314 const { 315 state 316 } = window.history; 317 if (state) { 318 setScreenIndex(Math.min(state, screens.length - 1)); 319 setPreviousOrder(Math.min(state, screens.length - 1)); 320 } 321 322 // Watch for browser back/forward button navigation events 323 window.addEventListener("popstate", handler); 324 return () => window.removeEventListener("popstate", handler); 325 } 326 return false; 327 }, []); // eslint-disable-line react-hooks/exhaustive-deps 328 329 const [multiSelects, setMultiSelects] = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)({}); 330 331 // Save the active multi select state for each screen as an object keyed by 332 // screen id. Each screen id has an array containing checkbox ids used in 333 // handleAction to update MULTI_ACTION data. This allows us to remember the 334 // state of each screen's multi select checkboxes when navigating back and 335 // forth between screens, while also allowing a message to have more than one 336 // multi select screen. 337 const [activeMultiSelects, setActiveMultiSelects] = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)({}); 338 339 // Save the active single select state for each screen as an object keyed 340 // by screen id. Similar to above, this allows us to remember the state of 341 // each screen's single select picker when navigating back and forth between 342 // screens, and allows us to have multiple single selects on a screen. 343 const [activeSingleSelectSelections, setActiveSingleSelectSelections] = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)({}); 344 345 // Get the active theme so the rendering code can make it selected 346 // by default. 347 const [activeTheme, setActiveTheme] = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)(null); 348 const [initialTheme, setInitialTheme] = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)(null); 349 (0,react__WEBPACK_IMPORTED_MODULE_0__.useEffect)(() => { 350 (async () => { 351 let theme = await window.AWGetSelectedTheme(); 352 setInitialTheme(theme); 353 setActiveTheme(theme); 354 })(); 355 }, []); 356 const { 357 negotiatedLanguage, 358 langPackInstallPhase, 359 languageFilteredScreens 360 } = (0,_LanguageSwitcher__WEBPACK_IMPORTED_MODULE_4__.useLanguageSwitcher)(props.appAndSystemLocaleInfo, screens, index, setScreenIndex); 361 (0,react__WEBPACK_IMPORTED_MODULE_0__.useEffect)(() => { 362 setScreens(languageFilteredScreens); 363 }, [languageFilteredScreens]); 364 const [installedAddons, setInstalledAddons] = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)(null); 365 (0,react__WEBPACK_IMPORTED_MODULE_0__.useEffect)(() => { 366 (async () => { 367 let addons = await window.AWGetInstalledAddons(); 368 setInstalledAddons(addons); 369 })(); 370 }, [index]); 371 372 // Do not render anything until the first filtering pass completes if gating 373 // initial paint is enabled. 374 if (gateInitialPaint && !ready) { 375 return null; 376 } 377 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", { 378 className: `outer-wrapper onboardingContainer proton transition-${transition}`, 379 style: props.backdrop ? { 380 background: props.backdrop 381 } : {} 382 }, screens.map((currentScreen, order) => { 383 const isFirstScreen = currentScreen === screens[0]; 384 const isLastScreen = currentScreen === screens[screens.length - 1]; 385 const totalNumberOfScreens = screens.length; 386 const isSingleScreen = totalNumberOfScreens === 1; 387 const setActiveMultiSelect = (valueOrFn, multiSelectId) => { 388 setActiveMultiSelects(prevState => { 389 const currentScreenSelections = prevState[currentScreen.id] || {}; 390 return { 391 ...prevState, 392 [currentScreen.id]: { 393 ...currentScreenSelections, 394 [multiSelectId]: typeof valueOrFn === "function" ? valueOrFn(currentScreenSelections[multiSelectId]) : valueOrFn 395 } 396 }; 397 }); 398 }; 399 const setScreenMultiSelects = (valueOrFn, multiSelectId) => { 400 setMultiSelects(prevState => { 401 const currentMultiSelects = prevState[currentScreen.id] || {}; 402 return { 403 ...prevState, 404 [currentScreen.id]: { 405 ...currentMultiSelects, 406 [multiSelectId]: typeof valueOrFn === "function" ? valueOrFn(currentMultiSelects[multiSelectId]) : valueOrFn 407 } 408 }; 409 }); 410 }; 411 const setActiveSingleSelectSelection = (valueOrFn, singleSelectId) => { 412 setActiveSingleSelectSelections(prevState => { 413 const currentScreenSelections = prevState[currentScreen.id] || {}; 414 return { 415 ...prevState, 416 [currentScreen.id]: { 417 ...currentScreenSelections, 418 [singleSelectId]: typeof valueOrFn === "function" ? valueOrFn(prevState[currentScreen.id]) : valueOrFn 419 } 420 }; 421 }); 422 }; 423 return index === order ? /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(WelcomeScreen, { 424 key: currentScreen.id + order, 425 id: currentScreen.id, 426 totalNumberOfScreens: totalNumberOfScreens, 427 isFirstScreen: isFirstScreen, 428 isLastScreen: isLastScreen, 429 isSingleScreen: isSingleScreen, 430 order: order, 431 previousOrder: previousOrder, 432 content: currentScreen.content, 433 navigate: handleTransition, 434 messageId: `${props.message_id}_${order}_${currentScreen.id}`, 435 UTMTerm: props.utm_term, 436 flowParams: flowParams, 437 activeTheme: activeTheme, 438 initialTheme: initialTheme, 439 setActiveTheme: setActiveTheme, 440 setInitialTheme: setInitialTheme, 441 screenMultiSelects: multiSelects[currentScreen.id], 442 setScreenMultiSelects: setScreenMultiSelects, 443 activeMultiSelect: activeMultiSelects[currentScreen.id], 444 setActiveMultiSelect: setActiveMultiSelect, 445 autoAdvance: currentScreen.auto_advance, 446 activeSingleSelectSelections: activeSingleSelectSelections[currentScreen.id], 447 setActiveSingleSelectSelection: setActiveSingleSelectSelection, 448 negotiatedLanguage: negotiatedLanguage, 449 langPackInstallPhase: langPackInstallPhase, 450 forceHideStepsIndicator: currentScreen.force_hide_steps_indicator, 451 ariaRole: props.ariaRole, 452 aboveButtonStepsIndicator: currentScreen.above_button_steps_indicator, 453 installedAddons: installedAddons, 454 setInstalledAddons: setInstalledAddons, 455 addonId: props.addonId, 456 addonType: props.addonType, 457 addonName: props.addonName, 458 addonURL: props.addonURL, 459 addonIconURL: props.addonIconURL, 460 themeScreenshots: props.themeScreenshots, 461 isRtamo: currentScreen.content.isRtamo 462 }) : null; 463 }))); 464 }; 465 const renderSingleSecondaryCTAButton = ({ 466 content, 467 button, 468 targetElement, 469 position, 470 handleAction, 471 activeMultiSelect, 472 isArrayItem, 473 index = null 474 }) => { 475 let buttonStyling = button?.has_arrow_icon ? `secondary arrow-icon` : `secondary`; 476 const isPrimary = button?.style === "primary"; 477 const isTextLink = !["split", "callout"].includes(content.position) && content.tiles?.type !== "addons-picker" && !isPrimary; 478 const isSplitButton = content.submenu_button?.attached_to === targetElement; 479 let className = "secondary-cta"; 480 if (position) { 481 className += ` ${position}`; 482 } 483 if (isSplitButton) { 484 className += " split-button-container"; 485 } 486 const computeDisabled = disabledValue => { 487 if (disabledValue === "hasActiveMultiSelect") { 488 if (!activeMultiSelect) { 489 return true; 490 } 491 for (const key in activeMultiSelect) { 492 if (activeMultiSelect[key]?.length > 0) { 493 return false; 494 } 495 } 496 return true; 497 } 498 return disabledValue; 499 }; 500 if (isTextLink) { 501 buttonStyling += " text-link"; 502 } 503 if (isPrimary) { 504 buttonStyling = button?.has_arrow_icon ? `primary arrow-icon` : `primary`; 505 } 506 507 // We have to provide handleAction with the expected action here, 508 // since the data doesn't actually exist in JSON content 509 const shimmedHandleAction = event => { 510 if (isArrayItem && button?.action) { 511 return handleAction(event, button.action); 512 } 513 return handleAction(event); 514 }; 515 let buttonId = "secondary_button"; 516 buttonId += index !== null ? `_${index}` : ""; 517 return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { 518 className: className, 519 key: targetElement 520 }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_MSLocalized__WEBPACK_IMPORTED_MODULE_1__.Localized, { 521 text: button?.text 522 }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("span", null)), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_MSLocalized__WEBPACK_IMPORTED_MODULE_1__.Localized, { 523 text: button?.label 524 }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("button", { 525 id: buttonId, 526 className: buttonStyling, 527 value: targetElement, 528 disabled: computeDisabled(button?.disabled), 529 onClick: shimmedHandleAction 530 })), isSplitButton ? /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_SubmenuButton__WEBPACK_IMPORTED_MODULE_5__.SubmenuButton, { 531 content: content, 532 handleAction: handleAction 533 }) : null); 534 }; 535 const SecondaryCTA = props => { 536 const { 537 content, 538 position 539 } = props; 540 const targetElement = position ? `secondary_button_${position}` : "secondary_button"; 541 const buttonData = content[targetElement]; 542 if (!buttonData) { 543 return null; 544 } 545 const buttons = react__WEBPACK_IMPORTED_MODULE_0___default().useMemo(() => Array.isArray(buttonData) ? buttonData : [buttonData], [buttonData]); 546 const [visibleButtons, setVisibleButtons] = react__WEBPACK_IMPORTED_MODULE_0___default().useState([]); 547 react__WEBPACK_IMPORTED_MODULE_0___default().useEffect(() => { 548 (async () => { 549 const filteredButtons = []; 550 for (const button of buttons) { 551 // No targeting, show by default for backwards compatibility 552 if (!button?.targeting) { 553 filteredButtons.push(button); 554 continue; 555 } 556 try { 557 const shouldShowButton = await window.AWEvaluateAttributeTargeting(button.targeting); 558 if (shouldShowButton) { 559 filteredButtons.push(button); 560 } 561 } catch (e) { 562 console.error("SecondaryCTA targeting failed:", button.targeting, e); 563 } 564 } 565 setVisibleButtons(filteredButtons); 566 })(); 567 }, [buttons]); 568 if (!visibleButtons.length) { 569 return null; 570 } 571 if (Array.isArray(buttonData)) { 572 return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { 573 className: "secondary-buttons-top-container" 574 }, visibleButtons.map((button, index) => renderSingleSecondaryCTAButton({ 575 content, 576 button, 577 targetElement: `${targetElement}_${index}`, 578 position, 579 handleAction: props.handleAction, 580 activeMultiSelect: props.activeMultiSelect, 581 isArrayItem: true, 582 index 583 }))); 584 } 585 return renderSingleSecondaryCTAButton({ 586 content, 587 button: visibleButtons[0], 588 targetElement, 589 position, 590 handleAction: props.handleAction, 591 activeMultiSelect: props.activeMultiSelect, 592 isArrayItem: false 593 }); 594 }; 595 const StepsIndicator = props => { 596 let steps = []; 597 for (let i = 0; i < props.totalNumberOfScreens; i++) { 598 let className = `${i === props.order ? "current" : ""} ${i < props.order ? "complete" : ""}`; 599 steps.push(/*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { 600 key: i, 601 className: `indicator ${className}`, 602 role: "presentation" 603 })); 604 } 605 return steps; 606 }; 607 const ProgressBar = ({ 608 step, 609 previousStep, 610 totalNumberOfScreens 611 }) => { 612 const [progress, setProgress] = react__WEBPACK_IMPORTED_MODULE_0___default().useState(previousStep / totalNumberOfScreens); 613 (0,react__WEBPACK_IMPORTED_MODULE_0__.useEffect)(() => { 614 // We don't need to hook any dependencies because any time the step changes, 615 // the screen's entire DOM tree will be re-rendered. 616 setProgress(step / totalNumberOfScreens); 617 }, []); // eslint-disable-line react-hooks/exhaustive-deps 618 return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { 619 className: "indicator", 620 role: "presentation", 621 style: { 622 "--progress-bar-progress": `${progress * 100}%` 623 } 624 }); 625 }; 626 class WelcomeScreen extends (react__WEBPACK_IMPORTED_MODULE_0___default().PureComponent) { 627 constructor(props) { 628 super(props); 629 this.handleAction = this.handleAction.bind(this); 630 } 631 handleOpenURL(action, flowParams, UTMTerm) { 632 let { 633 type, 634 data 635 } = action; 636 if (type === "SHOW_FIREFOX_ACCOUNTS") { 637 let params = { 638 ..._lib_addUtmParams_mjs__WEBPACK_IMPORTED_MODULE_6__.BASE_PARAMS, 639 utm_term: `${UTMTerm}-screen` 640 }; 641 if (action.addFlowParams && flowParams) { 642 params = { 643 ...params, 644 ...flowParams 645 }; 646 } 647 data = { 648 ...data, 649 extraParams: { 650 ...params, 651 ...data?.extraParams 652 } 653 }; 654 } else if (type === "OPEN_URL") { 655 let url = new URL(data.args); 656 (0,_lib_addUtmParams_mjs__WEBPACK_IMPORTED_MODULE_6__.addUtmParams)(url, `${UTMTerm}-screen`); 657 if (action.addFlowParams && flowParams) { 658 url.searchParams.append("device_id", flowParams.deviceId); 659 url.searchParams.append("flow_id", flowParams.flowId); 660 url.searchParams.append("flow_begin_time", flowParams.flowBeginTime); 661 } 662 data = { 663 ...data, 664 args: url.toString() 665 }; 666 } 667 return _lib_aboutwelcome_utils_mjs__WEBPACK_IMPORTED_MODULE_2__.AboutWelcomeUtils.handleUserAction({ 668 type, 669 data 670 }); 671 } 672 logTelemetry({ 673 value, 674 event, 675 source, 676 props 677 }) { 678 _lib_aboutwelcome_utils_mjs__WEBPACK_IMPORTED_MODULE_2__.AboutWelcomeUtils.sendActionTelemetry(props.messageId, source, event.name); 679 680 // Send additional telemetry if a messaging surface like feature callout is 681 // dismissed via the dismiss button. Other causes of dismissal will be 682 // handled separately by the messaging surface's own code. 683 if (value === "dismiss_button" && !event.name) { 684 _lib_aboutwelcome_utils_mjs__WEBPACK_IMPORTED_MODULE_2__.AboutWelcomeUtils.sendDismissTelemetry(props.messageId, source); 685 } 686 } 687 async handleMigrationIfNeeded(action, props) { 688 const hasMigrate = a => a.type === "SHOW_MIGRATION_WIZARD" || a.type === "MULTI_ACTION" && a.data?.actions?.some(hasMigrate); 689 if (hasMigrate(action)) { 690 await window.AWWaitForMigrationClose(); 691 _lib_aboutwelcome_utils_mjs__WEBPACK_IMPORTED_MODULE_2__.AboutWelcomeUtils.sendActionTelemetry(props.messageId, "migrate_close"); 692 } 693 } 694 applyThemeIfNeeded(action, event) { 695 if (!action.theme) { 696 return; 697 } 698 const themeToUse = action.theme === "<event>" ? event.currentTarget.value : this.props.initialTheme || action.theme; 699 this.props.setActiveTheme(themeToUse); 700 window.AWSelectTheme(themeToUse); 701 } 702 handlePickerAction(value) { 703 const tileGroups = Array.isArray(this.props.content.tiles) ? this.props.content.tiles : [this.props.content.tiles]; 704 for (const tile of tileGroups) { 705 if (!tile?.data) { 706 continue; 707 } 708 for (const opt of tile.data) { 709 if (opt.id === value) { 710 _lib_aboutwelcome_utils_mjs__WEBPACK_IMPORTED_MODULE_2__.AboutWelcomeUtils.handleUserAction(opt.action); 711 return; 712 } 713 } 714 } 715 } 716 resolveActionFromContent(value, event, props) { 717 if ((value === "submenu_button" || value === "tile_button") && event.action) { 718 return event.action; 719 } 720 const { 721 content 722 } = props; 723 const targetContent = content[value] || content.tiles || content.languageSwitcher; 724 if (!targetContent) { 725 return null; 726 } 727 if (Array.isArray(targetContent)) { 728 for (const tile of targetContent) { 729 const matchedTile = tile.data.find(t => t.id === value); 730 if (matchedTile?.action) { 731 return matchedTile.action; 732 } 733 } 734 return null; 735 } 736 return targetContent.action ?? null; 737 } 738 async handleAction(event, providedAction = null) { 739 const { 740 props 741 } = this; 742 const value = event.currentTarget.value ?? event.currentTarget.getAttribute("value"); 743 const source = event.source || value; 744 let action = providedAction || this.resolveActionFromContent(value, event, props); 745 if (!action) { 746 console.error("Failed to resolve action"); 747 return; 748 } 749 750 // Send telemetry before waiting on actions 751 this.logTelemetry({ 752 value, 753 event, 754 source, 755 props 756 }); 757 action = JSON.parse(JSON.stringify(action)); 758 if (action.collectSelect) { 759 this.setMultiSelectActions(action); 760 } 761 let actionResult; 762 if (["OPEN_URL", "SHOW_FIREFOX_ACCOUNTS"].includes(action.type)) { 763 this.handleOpenURL(action, props.flowParams, props.UTMTerm); 764 } else if (action.type) { 765 let actionPromise = _lib_aboutwelcome_utils_mjs__WEBPACK_IMPORTED_MODULE_2__.AboutWelcomeUtils.handleUserAction(action); 766 if (action.needsAwait) { 767 actionResult = await actionPromise; 768 } 769 if (action.type === "FXA_SIGNIN_FLOW") { 770 _lib_aboutwelcome_utils_mjs__WEBPACK_IMPORTED_MODULE_2__.AboutWelcomeUtils.sendActionTelemetry(props.messageId, actionResult ? "sign_in" : "sign_in_cancel", "FXA_SIGNIN_FLOW"); 771 } 772 if (action.type === "INSTALL_ADDON_FROM_URL") { 773 const url = props.addonURL; 774 if (!action.data) { 775 return; 776 } 777 // Set add-on url in action.data.url property from JSON 778 action.data = { 779 ...action.data, 780 url 781 }; 782 _lib_aboutwelcome_utils_mjs__WEBPACK_IMPORTED_MODULE_2__.AboutWelcomeUtils.handleUserAction(action); 783 } 784 // Wait until migration closes to complete the action 785 await this.handleMigrationIfNeeded(action, props); 786 } 787 788 // A special tiles.action.theme value indicates we should use the event's value vs provided value. 789 this.applyThemeIfNeeded(action, event); 790 if (action.picker) { 791 this.handlePickerAction(value); 792 } 793 794 // If the action has persistActiveTheme: true, we set the initial theme to the currently active theme 795 // so that it can be reverted to in the event that the user navigates away from the screen 796 if (action.persistActiveTheme) { 797 this.props.setInitialTheme(this.props.activeTheme); 798 } 799 800 // `navigate`, `goBack` and `dismiss` can be true/false/undefined, or they can be a 801 // string "actionResult" in which case we should use the actionResult 802 // (boolean resolved by handleUserAction) 803 const shouldDoBehavior = behavior => { 804 if (behavior !== "actionResult") { 805 return behavior; 806 } 807 if (action.needsAwait) { 808 return actionResult; 809 } 810 console.error("actionResult is only supported for actions with needsAwait"); 811 return false; 812 }; 813 if (shouldDoBehavior(action.navigate)) { 814 props.navigate(action.goBack); 815 } 816 817 // Used by FeatureCallout to advance screens by re-rendering the whole 818 // wrapper, updating anchor, page_event_listeners, etc. `navigate` only 819 // updates the inner content. Only implemented by FeatureCallout. 820 if (action.advance_screens) { 821 if (shouldDoBehavior(action.advance_screens.behavior ?? true)) { 822 window.AWAdvanceScreens?.(action.advance_screens); 823 } 824 } 825 if (shouldDoBehavior(action.dismiss)) { 826 window.AWFinish(); 827 } 828 } 829 setMultiSelectActions(action) { 830 let { 831 props 832 } = this; 833 // Populate MULTI_ACTION data actions property with selected checkbox 834 // actions from tiles data 835 if (action.type !== "MULTI_ACTION") { 836 console.error("collectSelect is only supported for MULTI_ACTION type actions"); 837 action.type = "MULTI_ACTION"; 838 } 839 if (!Array.isArray(action.data?.actions)) { 840 console.error("collectSelect is only supported for MULTI_ACTION type actions with an array of actions"); 841 action.data = { 842 actions: [] 843 }; 844 } 845 846 // Prepend the multi-select actions to the CTA's actions array, but keep 847 // the actions in the same order they appear in. This way the CTA action 848 // can go last, after the multi-select actions are processed. For example, 849 // 1. checkbox action 1 850 // 2. checkbox action 2 851 // 3. radio action 852 // 4. CTA action (which perhaps depends on the radio action) 853 // Note, this order is only guaranteed if action.data has the 854 // `orderedExecution` flag set to true. 855 let multiSelectActions = []; 856 const processTile = (tile, tileIndex) => { 857 if (tile?.type !== "multiselect" || !Array.isArray(tile.data)) { 858 return; 859 } 860 const multiSelectId = `tile-${tileIndex}`; 861 const activeSelections = props.activeMultiSelect[multiSelectId] || []; 862 for (const checkbox of tile.data) { 863 let checkboxAction; 864 if (activeSelections.includes(checkbox.id)) { 865 checkboxAction = checkbox.checkedAction ?? checkbox.action; 866 } else { 867 checkboxAction = checkbox.uncheckedAction; 868 } 869 if (checkboxAction) { 870 multiSelectActions.push(checkboxAction); 871 } 872 } 873 }; 874 875 // Process tiles (this may be a single tile object or an array consisting of 876 // tile objects) 877 if (props.content?.tiles) { 878 if (Array.isArray(props.content.tiles)) { 879 props.content.tiles.forEach(processTile); 880 } else { 881 // Handle case where tiles is a single tile object 882 processTile(props.content.tiles, 0); 883 } 884 } 885 886 // Prepend the collected multi-select actions to the CTA's actions array 887 action.data.actions.unshift(...multiSelectActions); 888 for (const value of Object.values(props.activeMultiSelect)) { 889 // Send telemetry with selected checkbox ids 890 _lib_aboutwelcome_utils_mjs__WEBPACK_IMPORTED_MODULE_2__.AboutWelcomeUtils.sendActionTelemetry(props.messageId, value.flat(), "SELECT_CHECKBOX"); 891 } 892 } 893 render() { 894 return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_MultiStageProtonScreen__WEBPACK_IMPORTED_MODULE_3__.MultiStageProtonScreen, { 895 content: this.props.content, 896 id: this.props.id, 897 order: this.props.order, 898 previousOrder: this.props.previousOrder, 899 activeTheme: this.props.activeTheme, 900 installedAddons: this.props.installedAddons, 901 screenMultiSelects: this.props.screenMultiSelects, 902 setScreenMultiSelects: this.props.setScreenMultiSelects, 903 activeMultiSelect: this.props.activeMultiSelect, 904 setActiveMultiSelect: this.props.setActiveMultiSelect, 905 activeSingleSelectSelections: this.props.activeSingleSelectSelections, 906 setActiveSingleSelectSelection: this.props.setActiveSingleSelectSelection, 907 totalNumberOfScreens: this.props.totalNumberOfScreens, 908 appAndSystemLocaleInfo: this.props.appAndSystemLocaleInfo, 909 negotiatedLanguage: this.props.negotiatedLanguage, 910 langPackInstallPhase: this.props.langPackInstallPhase, 911 handleAction: this.handleAction, 912 messageId: this.props.messageId, 913 isFirstScreen: this.props.isFirstScreen, 914 isLastScreen: this.props.isLastScreen, 915 isSingleScreen: this.props.isSingleScreen, 916 startsWithCorner: this.props.startsWithCorner, 917 autoAdvance: this.props.autoAdvance, 918 forceHideStepsIndicator: this.props.forceHideStepsIndicator, 919 ariaRole: this.props.ariaRole, 920 aboveButtonStepsIndicator: this.props.aboveButtonStepsIndicator, 921 addonId: this.props.addonId, 922 addonType: this.props.addonType, 923 addonName: this.props.addonName, 924 addonURL: this.props.addonURL, 925 addonIconURL: this.props.addonIconURL, 926 themeScreenshots: this.props.themeScreenshots, 927 isRtamo: this.props.content.isRtamo 928 }); 929 } 930 } 931 932 /***/ }), 933 /* 5 */ 934 /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { 935 936 __webpack_require__.r(__webpack_exports__); 937 /* harmony export */ __webpack_require__.d(__webpack_exports__, { 938 /* harmony export */ CONFIGURABLE_STYLES: () => (/* binding */ CONFIGURABLE_STYLES), 939 /* harmony export */ Localized: () => (/* binding */ Localized) 940 /* harmony export */ }); 941 /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(1); 942 /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_0__); 943 /* This Source Code Form is subject to the terms of the Mozilla Public 944 * License, v. 2.0. If a copy of the MPL was not distributed with this file, 945 * You can obtain one at http://mozilla.org/MPL/2.0/. */ 946 947 948 const CONFIGURABLE_STYLES = ["color", "display", "fontSize", "fontWeight", "letterSpacing", "lineHeight", "marginBlock", "marginBlockStart", "marginBlockEnd", "marginInline", "paddingBlock", "paddingBlockStart", "paddingBlockEnd", "paddingInline", "paddingInlineStart", "paddingInlineEnd", "textAlign", "whiteSpace", "width", "borderBlockStart", "borderBlockEnd", "top", "bottom", "left", "right", "inset", "insetBlock", "insetInline", "minHeight", "minWidth"]; 949 const ZAP_SIZE_THRESHOLD = 160; 950 951 /** 952 * Based on the .text prop, localizes an inner element if a string_id 953 * is provided, OR renders plain text, OR hides it if nothing is provided. 954 * Allows configuring of some styles including zap underline and color. 955 * 956 * Examples: 957 * 958 * Localized text 959 * ftl: 960 * title = Welcome 961 * jsx: 962 * <Localized text={{string_id: "title"}}><h1 /></Localized> 963 * output: 964 * <h1 data-l10n-id="title">Welcome</h1> 965 * 966 * Unlocalized text 967 * jsx: 968 * <Localized text="Welcome"><h1 /></Localized> 969 * <Localized text={{raw: "Welcome"}}><h1 /></Localized> 970 * output: 971 * <h1>Welcome</h1> 972 */ 973 974 const Localized = ({ 975 text, 976 children 977 }) => { 978 // Dynamically determine the size of the zap style. 979 const zapRef = /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createRef(); 980 (0,react__WEBPACK_IMPORTED_MODULE_0__.useEffect)(() => { 981 const { 982 current 983 } = zapRef; 984 if (current) { 985 requestAnimationFrame(() => current?.classList.replace("short", current.getBoundingClientRect().width > ZAP_SIZE_THRESHOLD ? "long" : "short")); 986 } 987 }); 988 989 // Skip rendering of children with no text. 990 if (!text) { 991 return null; 992 } 993 994 // Allow augmenting existing child container properties. 995 const props = { 996 children: [], 997 className: "", 998 style: {}, 999 ...children?.props 1000 }; 1001 // Support nested Localized by starting with their children. 1002 const textNodes = Array.isArray(props.children) ? props.children : [props.children]; 1003 1004 // Pick desired fluent or raw/plain text to render. 1005 if (text.string_id) { 1006 // Set the key so React knows not to reuse when switching to plain text. 1007 props.key = text.string_id; 1008 props["data-l10n-id"] = text.string_id; 1009 if (text.args) { 1010 props["data-l10n-args"] = JSON.stringify(text.args); 1011 } 1012 } else if (text.raw) { 1013 textNodes.push(text.raw); 1014 } else if (typeof text === "string") { 1015 textNodes.push(text); 1016 } 1017 1018 // Add zap style and content in a way that allows fluent to insert too. 1019 if (text.zap) { 1020 props.className += " welcomeZap"; 1021 textNodes.push(/*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("span", { 1022 className: "short zap", 1023 "data-l10n-name": "zap", 1024 ref: zapRef 1025 }, text.zap)); 1026 } 1027 if (text.aria_label) { 1028 props["aria-label"] = text.aria_label; 1029 } 1030 1031 // Apply certain configurable styles. 1032 CONFIGURABLE_STYLES.forEach(style => { 1033 if (text[style] !== undefined) { 1034 props.style[style] = text[style]; 1035 } 1036 }); 1037 return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().cloneElement( 1038 // Provide a default container for the text if necessary. 1039 children ?? /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("span", null), props, 1040 // Conditionally pass in as void elements can't accept empty array. 1041 textNodes.length ? textNodes : null); 1042 }; 1043 1044 /***/ }), 1045 /* 6 */ 1046 /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { 1047 1048 __webpack_require__.r(__webpack_exports__); 1049 /* harmony export */ __webpack_require__.d(__webpack_exports__, { 1050 /* harmony export */ MultiStageProtonScreen: () => (/* binding */ MultiStageProtonScreen), 1051 /* harmony export */ ProtonScreen: () => (/* binding */ ProtonScreen), 1052 /* harmony export */ ProtonScreenActionButtons: () => (/* binding */ ProtonScreenActionButtons) 1053 /* harmony export */ }); 1054 /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(1); 1055 /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_0__); 1056 /* harmony import */ var _MSLocalized__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(5); 1057 /* harmony import */ var _lib_aboutwelcome_utils_mjs__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(3); 1058 /* harmony import */ var _MultiStageAboutWelcome__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(4); 1059 /* harmony import */ var _LanguageSwitcher__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(7); 1060 /* harmony import */ var _CTAParagraph__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(8); 1061 /* harmony import */ var _HeroImage__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(9); 1062 /* harmony import */ var _OnboardingVideo__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(10); 1063 /* harmony import */ var _AdditionalCTA__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(11); 1064 /* harmony import */ var _LinkParagraph__WEBPACK_IMPORTED_MODULE_9__ = __webpack_require__(13); 1065 /* harmony import */ var _ContentTiles__WEBPACK_IMPORTED_MODULE_10__ = __webpack_require__(14); 1066 /* harmony import */ var _InstallButton__WEBPACK_IMPORTED_MODULE_11__ = __webpack_require__(16); 1067 /* This Source Code Form is subject to the terms of the Mozilla Public 1068 * License, v. 2.0. If a copy of the MPL was not distributed with this file, 1069 * You can obtain one at http://mozilla.org/MPL/2.0/. */ 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 const DEFAULT_AUTO_ADVANCE_MS = 20000; 1084 const MultiStageProtonScreen = props => { 1085 const { 1086 autoAdvance, 1087 handleAction, 1088 order 1089 } = props; 1090 (0,react__WEBPACK_IMPORTED_MODULE_0__.useEffect)(() => { 1091 if (autoAdvance) { 1092 const value = autoAdvance?.actionEl ?? autoAdvance; 1093 const timeout = autoAdvance?.actionTimeMS ?? DEFAULT_AUTO_ADVANCE_MS; 1094 const timer = setTimeout(() => { 1095 handleAction({ 1096 currentTarget: { 1097 value 1098 }, 1099 name: "AUTO_ADVANCE" 1100 }); 1101 }, timeout); 1102 return () => clearTimeout(timer); 1103 } 1104 return () => {}; 1105 }, [autoAdvance, handleAction, order]); 1106 1107 // Set narrow on an outer element to allow for use of SCSS outer selector and 1108 // consolidation of styles for small screen widths with those for messages 1109 // configured to always be narrow 1110 if (props.content.narrow) { 1111 document.querySelector("#multi-stage-message-root")?.setAttribute("narrow", ""); 1112 } else { 1113 // Clear narrow attribute in case it was set by a previous screen 1114 document.querySelector("#multi-stage-message-root")?.removeAttribute("narrow"); 1115 } 1116 function useMediaQuery(query) { 1117 const [doesMatch, setDoesMatch] = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)(() => window.matchMedia(query).matches); 1118 (0,react__WEBPACK_IMPORTED_MODULE_0__.useEffect)(() => { 1119 const mediaQueryList = window.matchMedia(query); 1120 const onChange = event => setDoesMatch(event.matches); 1121 mediaQueryList.addEventListener("change", onChange); 1122 return () => mediaQueryList.removeEventListener("change", onChange); 1123 }, [query]); 1124 return doesMatch; 1125 } 1126 const isWideScreen = useMediaQuery("(min-width: 800px)"); 1127 return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(ProtonScreen, { 1128 content: props.content, 1129 id: props.id, 1130 order: props.order, 1131 activeTheme: props.activeTheme, 1132 installedAddons: props.installedAddons, 1133 screenMultiSelects: props.screenMultiSelects, 1134 setScreenMultiSelects: props.setScreenMultiSelects, 1135 activeMultiSelect: props.activeMultiSelect, 1136 setActiveMultiSelect: props.setActiveMultiSelect, 1137 activeSingleSelectSelections: props.activeSingleSelectSelections, 1138 setActiveSingleSelectSelection: props.setActiveSingleSelectSelection, 1139 totalNumberOfScreens: props.totalNumberOfScreens, 1140 handleAction: props.handleAction, 1141 isFirstScreen: props.isFirstScreen, 1142 isLastScreen: props.isLastScreen, 1143 isSingleScreen: props.isSingleScreen, 1144 previousOrder: props.previousOrder, 1145 autoAdvance: props.autoAdvance, 1146 isRtamo: props.isRtamo, 1147 addonId: props.addonId, 1148 addonType: props.addonType, 1149 addonName: props.addonName, 1150 addonURL: props.addonURL, 1151 addonIconURL: props.addonIconURL, 1152 themeScreenshots: props.themeScreenshots, 1153 messageId: props.messageId, 1154 negotiatedLanguage: props.negotiatedLanguage, 1155 langPackInstallPhase: props.langPackInstallPhase, 1156 forceHideStepsIndicator: props.forceHideStepsIndicator, 1157 ariaRole: props.ariaRole, 1158 aboveButtonStepsIndicator: props.aboveButtonStepsIndicator, 1159 isWideScreen: isWideScreen 1160 }); 1161 }; 1162 const ProtonScreenActionButtons = props => { 1163 const { 1164 content, 1165 isRtamo, 1166 addonId, 1167 addonType, 1168 addonName, 1169 activeMultiSelect, 1170 installedAddons 1171 } = props; 1172 const defaultValue = content.checkbox?.defaultValue; 1173 const [isChecked, setIsChecked] = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)(defaultValue || false); 1174 const buttonRef = react__WEBPACK_IMPORTED_MODULE_0___default().useRef(null); 1175 const shouldFocusButton = content?.primary_button?.should_focus_button; 1176 (0,react__WEBPACK_IMPORTED_MODULE_0__.useEffect)(() => { 1177 if (shouldFocusButton) { 1178 buttonRef.current?.focus(); 1179 } 1180 }, [shouldFocusButton]); 1181 if (!content.primary_button && !content.secondary_button && !content.additional_button) { 1182 return null; 1183 } 1184 if (isRtamo) { 1185 content.primary_button.label.string_id = addonType?.includes("theme") ? "return-to-amo-add-theme-label" : "mr1-return-to-amo-add-extension-label"; 1186 } 1187 1188 // If we have a multi-select screen, we want to disable the primary button 1189 // until the user has selected at least one item. 1190 const isPrimaryDisabled = primaryDisabledValue => { 1191 if (primaryDisabledValue === "hasActiveMultiSelect") { 1192 if (!activeMultiSelect) { 1193 return true; 1194 } 1195 1196 // Check if there's at least one selection in any of the multiselects 1197 for (const selectKey in activeMultiSelect) { 1198 if (activeMultiSelect[selectKey]?.length > 0) { 1199 return false; 1200 } 1201 } 1202 return true; 1203 } 1204 return primaryDisabledValue; 1205 }; 1206 return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { 1207 className: `action-buttons ${content.additional_button ? "additional-cta-container" : ""}`, 1208 flow: content.additional_button?.flow, 1209 alignment: content.additional_button?.alignment 1210 }, isRtamo ? /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_InstallButton__WEBPACK_IMPORTED_MODULE_11__.InstallButton, { 1211 key: addonId, 1212 addonId: addonId, 1213 addonType: addonType, 1214 addonName: addonName, 1215 index: "primary_button", 1216 handleAction: props.handleAction, 1217 installedAddons: installedAddons, 1218 install_label: content.primary_button.label, 1219 install_complete_label: content.primary_button.install_complete_label 1220 }) : /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_MSLocalized__WEBPACK_IMPORTED_MODULE_1__.Localized, { 1221 text: content.primary_button?.label 1222 }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("button", { 1223 ref: buttonRef, 1224 className: `${content.primary_button?.style ?? "primary"}${content.primary_button?.has_arrow_icon ? " arrow-icon" : ""}` 1225 // Whether or not the checkbox is checked determines which action 1226 // should be handled. By setting value here, we indicate to 1227 // this.handleAction() where in the content tree it should take 1228 // the action to execute from. 1229 , 1230 value: isChecked ? "checkbox" : "primary_button", 1231 disabled: isPrimaryDisabled(content.primary_button?.disabled), 1232 onClick: props.handleAction, 1233 "data-l10n-args": addonName ? JSON.stringify({ 1234 "addon-name": addonName 1235 }) : "" 1236 })), content.additional_button ? /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_AdditionalCTA__WEBPACK_IMPORTED_MODULE_8__.AdditionalCTA, { 1237 content: content, 1238 handleAction: props.handleAction 1239 }) : null, content.checkbox ? /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { 1240 className: "checkbox-container" 1241 }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("input", { 1242 type: "checkbox", 1243 id: "action-checkbox", 1244 checked: isChecked, 1245 onChange: () => { 1246 setIsChecked(!isChecked); 1247 } 1248 }), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_MSLocalized__WEBPACK_IMPORTED_MODULE_1__.Localized, { 1249 text: content.checkbox.label 1250 }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("label", { 1251 htmlFor: "action-checkbox" 1252 }))) : null, content.secondary_button ? /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_MultiStageAboutWelcome__WEBPACK_IMPORTED_MODULE_3__.SecondaryCTA, { 1253 content: content, 1254 handleAction: props.handleAction, 1255 activeMultiSelect: activeMultiSelect 1256 }) : null); 1257 }; 1258 class ProtonScreen extends (react__WEBPACK_IMPORTED_MODULE_0___default().PureComponent) { 1259 componentDidMount() { 1260 // Don't focus on main content if it is a feature callout 1261 // See Bug 1985939 1262 if (this.props.content?.position === "callout") { 1263 return; 1264 } 1265 this.mainContentHeader.focus(); 1266 } 1267 getScreenClassName(includeNoodles, isVideoOnboarding, isAddonsPicker) { 1268 if (isVideoOnboarding) { 1269 return "with-video"; 1270 } 1271 if (isAddonsPicker) { 1272 return "addons-picker"; 1273 } 1274 const screenClass = `screen-${this.props.order % 2 !== 0 ? 1 : 2}`; 1275 const dialogInitial = this.props.isFirstScreen && this.props.previousOrder < 0 ? `dialog-initial` : ``; 1276 const dialogLast = this.props.isLastScreen ? `dialog-last` : ``; 1277 return `${screenClass} ${dialogInitial} ${dialogLast} ${includeNoodles ? `with-noodles` : ``}`; 1278 } 1279 renderTitle({ 1280 title, 1281 title_logo 1282 }) { 1283 if (title_logo) { 1284 const { 1285 alignment, 1286 ...rest 1287 } = title_logo; 1288 return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { 1289 className: "inline-icon-container", 1290 alignment: alignment ?? "center" 1291 }, this.renderPicture({ 1292 ...rest 1293 }), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_MSLocalized__WEBPACK_IMPORTED_MODULE_1__.Localized, { 1294 text: title 1295 }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("h1", { 1296 id: "mainContentHeader" 1297 }))); 1298 } 1299 return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_MSLocalized__WEBPACK_IMPORTED_MODULE_1__.Localized, { 1300 text: title 1301 }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("h1", { 1302 id: "mainContentHeader" 1303 })); 1304 } 1305 renderPicture({ 1306 imageURL = "chrome://branding/content/about-logo.svg", 1307 darkModeImageURL, 1308 reducedMotionImageURL, 1309 darkModeReducedMotionImageURL, 1310 alt = "", 1311 width, 1312 height, 1313 marginBlock, 1314 marginInline, 1315 className = "logo-container" 1316 }) { 1317 function getLoadingStrategy() { 1318 for (let url of [imageURL, darkModeImageURL, reducedMotionImageURL, darkModeReducedMotionImageURL]) { 1319 if (_lib_aboutwelcome_utils_mjs__WEBPACK_IMPORTED_MODULE_2__.AboutWelcomeUtils.getLoadingStrategyFor(url) === "lazy") { 1320 return "lazy"; 1321 } 1322 } 1323 return "eager"; 1324 } 1325 return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("picture", { 1326 className: className, 1327 style: { 1328 marginInline, 1329 marginBlock 1330 } 1331 }, darkModeReducedMotionImageURL ? /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("source", { 1332 srcset: darkModeReducedMotionImageURL, 1333 media: "(prefers-color-scheme: dark) and (prefers-reduced-motion: reduce)" 1334 }) : null, darkModeImageURL ? /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("source", { 1335 srcset: darkModeImageURL, 1336 media: "(prefers-color-scheme: dark)" 1337 }) : null, reducedMotionImageURL ? /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("source", { 1338 srcset: reducedMotionImageURL, 1339 media: "(prefers-reduced-motion: reduce)" 1340 }) : null, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_MSLocalized__WEBPACK_IMPORTED_MODULE_1__.Localized, { 1341 text: alt 1342 }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { 1343 className: "sr-only logo-alt" 1344 })), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("img", { 1345 className: "brand-logo", 1346 style: { 1347 height, 1348 width 1349 }, 1350 src: imageURL, 1351 alt: "", 1352 loading: getLoadingStrategy(), 1353 role: alt ? null : "presentation" 1354 })); 1355 } 1356 renderNoodles() { 1357 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", { 1358 className: "noodle orange-L" 1359 }), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { 1360 className: "noodle purple-C" 1361 }), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { 1362 className: "noodle solid-L" 1363 }), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { 1364 className: "noodle outline-L" 1365 }), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { 1366 className: "noodle yellow-circle" 1367 })); 1368 } 1369 renderLanguageSwitcher() { 1370 return this.props.content.languageSwitcher ? /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_LanguageSwitcher__WEBPACK_IMPORTED_MODULE_4__.LanguageSwitcher, { 1371 content: this.props.content, 1372 handleAction: this.props.handleAction, 1373 negotiatedLanguage: this.props.negotiatedLanguage, 1374 langPackInstallPhase: this.props.langPackInstallPhase, 1375 messageId: this.props.messageId 1376 }) : null; 1377 } 1378 renderDismissButton() { 1379 const { 1380 size, 1381 marginBlock, 1382 marginInline, 1383 label, 1384 background 1385 } = this.props.content.dismiss_button; 1386 return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("button", { 1387 className: `dismiss-button ${background ? "with-background" : ""}`, 1388 onClick: this.props.handleAction, 1389 value: "dismiss_button", 1390 "data-l10n-id": label?.string_id || "spotlight-dialog-close-button", 1391 "button-size": size, 1392 style: { 1393 marginBlock, 1394 marginInline 1395 } 1396 }); 1397 } 1398 renderStepsIndicator() { 1399 const { 1400 order, 1401 previousOrder, 1402 content, 1403 totalNumberOfScreens: total, 1404 aboveButtonStepsIndicator 1405 } = this.props; 1406 const currentStep = (order ?? 0) + 1; 1407 const previousStep = (previousOrder ?? -1) + 1; 1408 return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { 1409 id: "steps", 1410 className: `steps${content.progress_bar ? " progress-bar" : ""}`, 1411 "above-button": aboveButtonStepsIndicator ? "" : null, 1412 "data-l10n-id": content.steps_indicator?.string_id || "onboarding-welcome-steps-indicator-label", 1413 "data-l10n-args": JSON.stringify({ 1414 current: currentStep, 1415 total: total ?? 0 1416 }), 1417 "data-l10n-attrs": "aria-label", 1418 role: "progressbar", 1419 "aria-valuenow": currentStep, 1420 "aria-valuemin": 1, 1421 "aria-valuemax": total 1422 }, content.progress_bar ? /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_MultiStageAboutWelcome__WEBPACK_IMPORTED_MODULE_3__.ProgressBar, { 1423 step: currentStep, 1424 previousStep: previousStep, 1425 totalNumberOfScreens: total 1426 }) : /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_MultiStageAboutWelcome__WEBPACK_IMPORTED_MODULE_3__.StepsIndicator, { 1427 order: order, 1428 totalNumberOfScreens: total 1429 })); 1430 } 1431 renderSecondarySection(content) { 1432 return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { 1433 className: `section-secondary ${content.hide_secondary_section ? "with-secondary-section-hidden" : ""}`, 1434 style: content.background ? { 1435 background: content.background, 1436 "--mr-secondary-background-position-y": content.split_narrow_bkg_position 1437 } : {} 1438 }, content.dismiss_button && content.reverse_split ? this.renderDismissButton() : null, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_MSLocalized__WEBPACK_IMPORTED_MODULE_1__.Localized, { 1439 text: content.image_alt_text 1440 }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { 1441 className: "sr-only image-alt", 1442 role: "img" 1443 })), content.hero_image ? /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_HeroImage__WEBPACK_IMPORTED_MODULE_6__.HeroImage, { 1444 url: content.hero_image.url 1445 }) : this.renderHeroText(content.hero_text)); 1446 } 1447 renderHeroText(hero_text) { 1448 if (!hero_text) { 1449 return null; 1450 } 1451 1452 // Check if hero_text is a string or an object with string_id property 1453 // essentially checking if we're using old or new design 1454 const isSimpleText = typeof hero_text === "string" || typeof hero_text === "object" && hero_text !== null && "string_id" in hero_text; 1455 const HeroTextWrapper = ({ 1456 children, 1457 className = "" 1458 }) => /*#__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", { 1459 className: `message-text ${className}` 1460 }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { 1461 className: "spacer-top" 1462 }), children, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { 1463 className: "spacer-bottom" 1464 }))); 1465 if (isSimpleText) { 1466 return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(HeroTextWrapper, null, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_MSLocalized__WEBPACK_IMPORTED_MODULE_1__.Localized, { 1467 text: hero_text 1468 }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("h1", null))); 1469 } 1470 return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(HeroTextWrapper, { 1471 className: "hero-text" 1472 }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_MSLocalized__WEBPACK_IMPORTED_MODULE_1__.Localized, { 1473 text: hero_text.title 1474 }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("h1", null)), hero_text.subtitle && /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_MSLocalized__WEBPACK_IMPORTED_MODULE_1__.Localized, { 1475 text: hero_text.subtitle 1476 }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("h2", null))); 1477 } 1478 renderOrderedContent(content) { 1479 const elements = []; 1480 for (const item of content) { 1481 switch (item.type) { 1482 case "text": 1483 elements.push(/*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_LinkParagraph__WEBPACK_IMPORTED_MODULE_9__.LinkParagraph, { 1484 text_content: item, 1485 handleAction: this.props.handleAction 1486 })); 1487 break; 1488 case "image": 1489 elements.push(this.renderPicture({ 1490 imageURL: item.url, 1491 darkModeImageURL: item.darkModeImageURL, 1492 height: item.height, 1493 width: item.width, 1494 alt: item.alt_text, 1495 marginInline: item.marginInline, 1496 className: "inline-image" 1497 })); 1498 } 1499 } 1500 return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement((react__WEBPACK_IMPORTED_MODULE_0___default().Fragment), null, elements); 1501 } 1502 renderRTAMOIcon(addonType, themeScreenshots, addonIconURL) { 1503 return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { 1504 className: "rtamo-icon" 1505 }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("img", { 1506 className: `${addonType?.includes("theme") ? "rtamo-theme-icon" : "brand-logo"}`, 1507 src: addonType?.includes("theme") ? themeScreenshots[0].url : addonIconURL, 1508 loading: _lib_aboutwelcome_utils_mjs__WEBPACK_IMPORTED_MODULE_2__.AboutWelcomeUtils.getLoadingStrategyFor(addonIconURL), 1509 alt: "", 1510 role: "presentation" 1511 })); 1512 } 1513 getCombinedInnerStyles(content, isWideScreen) { 1514 const CONFIGURABLE_STYLES = ["overflow", "display", "paddingInline", "paddingInlineStart", "paddingInlineEnd", "paddingBlock", "paddingBlockStart", "paddingBlockEnd"]; 1515 const innerContentStyles = isWideScreen ? content.main_content_style || {} : content.main_content_style_narrow || {}; 1516 const validInnerStyles = _lib_aboutwelcome_utils_mjs__WEBPACK_IMPORTED_MODULE_2__.AboutWelcomeUtils.getValidStyle(innerContentStyles, CONFIGURABLE_STYLES) || {}; 1517 return { 1518 ...validInnerStyles, 1519 justifyContent: content.split_content_justify_content 1520 }; 1521 } 1522 getActionButtonsPosition(content) { 1523 const VALID_POSITIONS = ["after_subtitle", "after_supporting_content", "end"]; 1524 if (VALID_POSITIONS.includes(content.action_buttons_position)) { 1525 return content.action_buttons_position; 1526 } 1527 // Legacy mapping 1528 if (content.action_buttons_above_content) { 1529 return "after_subtitle"; 1530 } 1531 // Default 1532 return "end"; 1533 } 1534 renderActionButtons(position, content) { 1535 return this.getActionButtonsPosition(content) === position ? /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(ProtonScreenActionButtons, { 1536 content: content, 1537 isRtamo: this.props.isRtamo, 1538 installedAddons: this.props.installedAddons, 1539 addonId: this.props.addonId, 1540 addonName: this.props.addonName, 1541 addonType: this.props.addonType, 1542 handleAction: this.props.handleAction, 1543 activeMultiSelect: this.props.activeMultiSelect 1544 }) : null; 1545 } 1546 1547 // eslint-disable-next-line complexity 1548 render() { 1549 const { 1550 autoAdvance, 1551 content, 1552 isRtamo, 1553 addonType, 1554 isSingleScreen, 1555 forceHideStepsIndicator, 1556 ariaRole, 1557 aboveButtonStepsIndicator, 1558 isWideScreen 1559 } = this.props; 1560 const includeNoodles = content.has_noodles; 1561 // The default screen position is "center" 1562 const isCenterPosition = content.position === "center" || !content.position; 1563 const hideStepsIndicator = autoAdvance || content?.video_container || isSingleScreen || forceHideStepsIndicator; 1564 const textColorClass = content.text_color ? `${content.text_color}-text` : ""; 1565 // Assign proton screen style 'screen-1' or 'screen-2' to centered screens 1566 // by checking if screen order is even or odd. 1567 const screenClassName = isCenterPosition ? this.getScreenClassName(includeNoodles, content?.video_container, content.tiles?.type === "addons-picker") : ""; 1568 const isEmbeddedMigration = content.tiles?.type === "migration-wizard"; 1569 const isSystemPromptStyleSpotlight = content.isSystemPromptStyleSpotlight === true; 1570 const combinedStyles = this.getCombinedInnerStyles(content, isWideScreen); 1571 return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("main", { 1572 className: `screen ${this.props.id || ""} 1573 ${screenClassName} ${textColorClass}`, 1574 "reverse-split": content.reverse_split ? "" : null, 1575 fullscreen: content.fullscreen ? "" : null, 1576 style: content.screen_style && _lib_aboutwelcome_utils_mjs__WEBPACK_IMPORTED_MODULE_2__.AboutWelcomeUtils.getValidStyle(content.screen_style, ["overflow", "display"]), 1577 role: ariaRole ?? "alertdialog", 1578 layout: content.layout, 1579 pos: content.position || "center", 1580 tabIndex: "-1", 1581 "aria-labelledby": "mainContentHeader", 1582 ref: input => { 1583 this.mainContentHeader = input; 1584 }, 1585 "no-rdm": content.no_rdm ? "" : null 1586 }, isCenterPosition ? null : this.renderSecondarySection(content), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { 1587 className: `section-main ${isEmbeddedMigration ? "embedded-migration" : ""}${isSystemPromptStyleSpotlight ? "system-prompt-spotlight" : ""}`, 1588 "hide-secondary-section": content.hide_secondary_section ? String(content.hide_secondary_section) : null, 1589 role: "document", 1590 style: content.screen_style && _lib_aboutwelcome_utils_mjs__WEBPACK_IMPORTED_MODULE_2__.AboutWelcomeUtils.getValidStyle(content.screen_style, ["width", "padding", "height"]) 1591 }, content.secondary_button_top ? /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_MultiStageAboutWelcome__WEBPACK_IMPORTED_MODULE_3__.SecondaryCTA, { 1592 content: content, 1593 handleAction: this.props.handleAction, 1594 position: "top" 1595 }) : null, includeNoodles ? this.renderNoodles() : null, content.dismiss_button && !content.reverse_split ? this.renderDismissButton() : null, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { 1596 className: `main-content ${hideStepsIndicator ? "no-steps" : ""}`, 1597 style: { 1598 background: content.background && isCenterPosition ? content.background : null, 1599 width: content.width && content.position !== "split" ? content.width : null, 1600 paddingBlock: content.split_content_padding_block ? content.split_content_padding_block : null, 1601 paddingInline: content.split_content_padding_inline ? content.split_content_padding_inline : null 1602 } 1603 }, content.logo && !content.fullscreen ? this.renderPicture(content.logo) : null, isRtamo && !content.fullscreen ? this.renderRTAMOIcon(addonType, this.props.themeScreenshots, this.props.addonIconURL) : null, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { 1604 className: "main-content-inner", 1605 style: combinedStyles 1606 }, content.logo && content.fullscreen ? this.renderPicture(content.logo) : null, isRtamo && content.fullscreen ? this.renderRTAMOIcon(addonType, this.props.themeScreenshots, this.props.addonIconURL) : null, content.title || content.subtitle ? /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { 1607 id: "multi-stage-message-welcome-text", 1608 className: `welcome-text ${content.title_style || ""}` 1609 }, content.title ? this.renderTitle(content) : null, content.subtitle ? /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_MSLocalized__WEBPACK_IMPORTED_MODULE_1__.Localized, { 1610 text: content.subtitle 1611 }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("h2", { 1612 "data-l10n-args": JSON.stringify({ 1613 "addon-name": this.props.addonName, 1614 ...this.props.appAndSystemLocaleInfo?.displayNames 1615 }), 1616 "aria-flowto": this.props.messageId?.includes("FEATURE_TOUR") ? "steps" : "", 1617 id: "mainContentSubheader" 1618 })) : null, this.renderActionButtons("after_subtitle", content), content.cta_paragraph ? /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_CTAParagraph__WEBPACK_IMPORTED_MODULE_5__.CTAParagraph, { 1619 content: content.cta_paragraph, 1620 handleAction: this.props.handleAction 1621 }) : null) : null, content.video_container ? /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_OnboardingVideo__WEBPACK_IMPORTED_MODULE_7__.OnboardingVideo, { 1622 content: content.video_container, 1623 handleAction: this.props.handleAction 1624 }) : null, this.renderLanguageSwitcher(), content?.tiles_container?.position !== "after_supporting_content" ? /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_ContentTiles__WEBPACK_IMPORTED_MODULE_10__.ContentTiles, this.props) : null, content.above_button_content ? this.renderOrderedContent(content.above_button_content) : null, this.renderActionButtons("after_supporting_content", content), content?.tiles_container?.position === "after_supporting_content" ? /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_ContentTiles__WEBPACK_IMPORTED_MODULE_10__.ContentTiles, this.props) : null, !hideStepsIndicator && aboveButtonStepsIndicator ? this.renderStepsIndicator() : null, this.renderActionButtons("end", content), 1625 /* Fullscreen dot-style step indicator should sit inside the 1626 main inner content to share its padding, which will be 1627 configurable with Bug 1956042 */ 1628 !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, { 1629 text: content.info_text 1630 }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("span", { 1631 className: "info-text" 1632 }))); 1633 } 1634 } 1635 1636 /***/ }), 1637 /* 7 */ 1638 /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { 1639 1640 __webpack_require__.r(__webpack_exports__); 1641 /* harmony export */ __webpack_require__.d(__webpack_exports__, { 1642 /* harmony export */ LanguageSwitcher: () => (/* binding */ LanguageSwitcher), 1643 /* harmony export */ useLanguageSwitcher: () => (/* binding */ useLanguageSwitcher) 1644 /* harmony export */ }); 1645 /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(1); 1646 /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_0__); 1647 /* harmony import */ var _MSLocalized__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(5); 1648 /* harmony import */ var _lib_aboutwelcome_utils_mjs__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(3); 1649 /* This Source Code Form is subject to the terms of the Mozilla Public 1650 * License, v. 2.0. If a copy of the MPL was not distributed with this file, 1651 * You can obtain one at http://mozilla.org/MPL/2.0/. */ 1652 1653 1654 1655 1656 1657 /** 1658 * The language switcher implements a hook that should be placed at a higher level 1659 * than the actual language switcher component, as it needs to preemptively fetch 1660 * and install langpacks for the user if there is a language mismatch screen. 1661 */ 1662 function useLanguageSwitcher(appAndSystemLocaleInfo, screens, screenIndex, setScreenIndex) { 1663 const languageMismatchScreenIndex = screens.findIndex(({ 1664 id 1665 }) => id === "AW_LANGUAGE_MISMATCH"); 1666 const mismatchScreen = screens[languageMismatchScreenIndex]; 1667 1668 // Ensure fluent messages have the negotiatedLanguage args set, as they are rendered 1669 // before the negotiatedLanguage is known. If the arg isn't present then Firefox will 1670 // crash in development mode. 1671 (0,react__WEBPACK_IMPORTED_MODULE_0__.useEffect)(() => { 1672 if (mismatchScreen?.content?.languageSwitcher) { 1673 for (const text of Object.values(mismatchScreen.content.languageSwitcher)) { 1674 if (text?.args && text.args.negotiatedLanguage === undefined) { 1675 text.args.negotiatedLanguage = ""; 1676 } 1677 } 1678 } 1679 }, [mismatchScreen]); 1680 1681 // If there is a mismatch, then Firefox can negotiate a better langpack to offer 1682 // the user. 1683 const [negotiatedLanguage, setNegotiatedLanguage] = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)(null); 1684 (0,react__WEBPACK_IMPORTED_MODULE_0__.useEffect)(function getNegotiatedLanguage() { 1685 if (!appAndSystemLocaleInfo) { 1686 return; 1687 } 1688 if (appAndSystemLocaleInfo.matchType !== "language-mismatch") { 1689 // There is no language mismatch, so there is no need to negotiate a langpack. 1690 return; 1691 } 1692 (async () => { 1693 const { 1694 langPack, 1695 langPackDisplayName 1696 } = await window.AWNegotiateLangPackForLanguageMismatch(appAndSystemLocaleInfo); 1697 if (langPack) { 1698 setNegotiatedLanguage({ 1699 langPackDisplayName, 1700 appDisplayName: appAndSystemLocaleInfo.displayNames.appLanguage, 1701 langPack, 1702 requestSystemLocales: [langPack.target_locale, appAndSystemLocaleInfo.appLocaleRaw], 1703 originalAppLocales: [appAndSystemLocaleInfo.appLocaleRaw] 1704 }); 1705 } else { 1706 setNegotiatedLanguage({ 1707 langPackDisplayName: null, 1708 appDisplayName: null, 1709 langPack: null, 1710 requestSystemLocales: null 1711 }); 1712 } 1713 })(); 1714 }, [appAndSystemLocaleInfo]); 1715 1716 /** 1717 * @type { 1718 * "before-installation" 1719 * | "installing" 1720 * | "installed" 1721 * | "installation-error" 1722 * | "none-available" 1723 * } 1724 */ 1725 const [langPackInstallPhase, setLangPackInstallPhase] = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)("before-installation"); 1726 (0,react__WEBPACK_IMPORTED_MODULE_0__.useEffect)(function ensureLangPackInstalled() { 1727 if (!negotiatedLanguage) { 1728 // There are no negotiated languages to download yet. 1729 return; 1730 } 1731 setLangPackInstallPhase("installing"); 1732 window.AWEnsureLangPackInstalled(negotiatedLanguage, mismatchScreen?.content).then(content => { 1733 // Update screen content with strings that might have changed. 1734 mismatchScreen.content = content; 1735 setLangPackInstallPhase("installed"); 1736 }, error => { 1737 console.error(error); 1738 setLangPackInstallPhase("installation-error"); 1739 }); 1740 }, [negotiatedLanguage, mismatchScreen]); 1741 const [languageFilteredScreens, setLanguageFilteredScreens] = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)(screens); 1742 (0,react__WEBPACK_IMPORTED_MODULE_0__.useEffect)(function filterScreen() { 1743 // Remove the language screen if it exists (already removed for no live 1744 // reload) and we either don't-need-to or can't switch. 1745 if (mismatchScreen && (appAndSystemLocaleInfo?.matchType !== "language-mismatch" || negotiatedLanguage?.langPack === null)) { 1746 if (screenIndex > languageMismatchScreenIndex) { 1747 setScreenIndex(screenIndex - 1); 1748 } 1749 setLanguageFilteredScreens(screens.filter(s => s.id !== "AW_LANGUAGE_MISMATCH")); 1750 } else { 1751 setLanguageFilteredScreens(screens); 1752 } 1753 }, 1754 // Removing screenIndex as a dependency as it's causing infinite re-renders (1873019) 1755 // eslint-disable-next-line react-hooks/exhaustive-deps 1756 [appAndSystemLocaleInfo?.matchType, languageMismatchScreenIndex, negotiatedLanguage, mismatchScreen, screens, setScreenIndex]); 1757 return { 1758 negotiatedLanguage, 1759 langPackInstallPhase, 1760 languageFilteredScreens 1761 }; 1762 } 1763 1764 /** 1765 * The language switcher is a separate component as it needs to perform some asynchronous 1766 * network actions such as retrieving the list of langpacks available, and downloading 1767 * a new langpack. On a fast connection, this won't be noticeable, but on slow or unreliable 1768 * internet this may fail for a user. 1769 */ 1770 function LanguageSwitcher(props) { 1771 const { 1772 content, 1773 handleAction, 1774 negotiatedLanguage, 1775 langPackInstallPhase, 1776 messageId 1777 } = props; 1778 const [isAwaitingLangpack, setIsAwaitingLangpack] = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)(false); 1779 1780 // Determine the status of the langpack installation. 1781 (0,react__WEBPACK_IMPORTED_MODULE_0__.useEffect)(() => { 1782 if (isAwaitingLangpack && langPackInstallPhase !== "installing") { 1783 window.AWSetRequestedLocales(negotiatedLanguage.requestSystemLocales); 1784 requestAnimationFrame(() => { 1785 handleAction( 1786 // Simulate the click event. 1787 { 1788 currentTarget: { 1789 value: "download_complete" 1790 } 1791 }); 1792 }); 1793 } 1794 }, [handleAction, isAwaitingLangpack, langPackInstallPhase, negotiatedLanguage?.requestSystemLocales]); 1795 let showWaitingScreen = false; 1796 let showPreloadingScreen = false; 1797 let showReadyScreen = false; 1798 if (isAwaitingLangpack && langPackInstallPhase !== "installed") { 1799 showWaitingScreen = true; 1800 } else if (langPackInstallPhase === "before-installation") { 1801 showPreloadingScreen = true; 1802 } else { 1803 showReadyScreen = true; 1804 } 1805 1806 // Use {display: "none"} rather than if statements to prevent layout thrashing with 1807 // the localized text elements rendering as blank, then filling in the text. 1808 return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { 1809 className: "action-buttons language-switcher-container" 1810 }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { 1811 style: { 1812 display: showPreloadingScreen ? "block" : "none" 1813 } 1814 }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("button", { 1815 className: "primary", 1816 value: "primary_button", 1817 disabled: true, 1818 type: "button" 1819 }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("img", { 1820 className: "language-loader", 1821 src: "chrome://global/skin/icons/loading.svg", 1822 alt: "" 1823 }), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_MSLocalized__WEBPACK_IMPORTED_MODULE_1__.Localized, { 1824 text: content.languageSwitcher.waiting 1825 })), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { 1826 className: "secondary-cta" 1827 }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_MSLocalized__WEBPACK_IMPORTED_MODULE_1__.Localized, { 1828 text: content.languageSwitcher.skip 1829 }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("button", { 1830 value: "decline_waiting", 1831 type: "button", 1832 className: "secondary text-link arrow-icon", 1833 onClick: handleAction 1834 })))), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { 1835 style: { 1836 display: showWaitingScreen ? "block" : "none" 1837 } 1838 }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("button", { 1839 className: "primary", 1840 value: "primary_button", 1841 disabled: true, 1842 type: "button" 1843 }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("img", { 1844 className: "language-loader", 1845 src: "chrome://global/skin/icons/loading.svg", 1846 alt: "" 1847 }), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_MSLocalized__WEBPACK_IMPORTED_MODULE_1__.Localized, { 1848 text: content.languageSwitcher.downloading 1849 })), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { 1850 className: "secondary-cta" 1851 }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_MSLocalized__WEBPACK_IMPORTED_MODULE_1__.Localized, { 1852 text: content.languageSwitcher.cancel 1853 }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("button", { 1854 type: "button", 1855 className: "secondary text-link", 1856 onClick: () => { 1857 setIsAwaitingLangpack(false); 1858 handleAction({ 1859 currentTarget: { 1860 value: "cancel_waiting" 1861 } 1862 }); 1863 } 1864 })))), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { 1865 style: { 1866 display: showReadyScreen ? "block" : "none" 1867 } 1868 }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", null, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("button", { 1869 className: "primary", 1870 value: "primary_button", 1871 onClick: () => { 1872 _lib_aboutwelcome_utils_mjs__WEBPACK_IMPORTED_MODULE_2__.AboutWelcomeUtils.sendActionTelemetry(messageId, "download_langpack"); 1873 setIsAwaitingLangpack(true); 1874 } 1875 }, content.languageSwitcher.switch ? /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_MSLocalized__WEBPACK_IMPORTED_MODULE_1__.Localized, { 1876 text: content.languageSwitcher.switch 1877 }) : 1878 // This is the localized name from the Intl.DisplayNames API. 1879 negotiatedLanguage?.langPackDisplayName)), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", null, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("button", { 1880 type: "button", 1881 className: "primary", 1882 value: "decline", 1883 onClick: event => { 1884 window.AWSetRequestedLocales(negotiatedLanguage.originalAppLocales); 1885 handleAction(event); 1886 } 1887 }, content.languageSwitcher.continue ? /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_MSLocalized__WEBPACK_IMPORTED_MODULE_1__.Localized, { 1888 text: content.languageSwitcher.continue 1889 }) : 1890 // This is the localized name from the Intl.DisplayNames API. 1891 negotiatedLanguage?.appDisplayName)))); 1892 } 1893 1894 /***/ }), 1895 /* 8 */ 1896 /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { 1897 1898 __webpack_require__.r(__webpack_exports__); 1899 /* harmony export */ __webpack_require__.d(__webpack_exports__, { 1900 /* harmony export */ CTAParagraph: () => (/* binding */ CTAParagraph) 1901 /* harmony export */ }); 1902 /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(1); 1903 /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_0__); 1904 /* harmony import */ var _MSLocalized__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(5); 1905 /* harmony import */ var _lib_aboutwelcome_utils_mjs__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(3); 1906 /* This Source Code Form is subject to the terms of the Mozilla Public 1907 * License, v. 2.0. If a copy of the MPL was not distributed with this file, 1908 * You can obtain one at http://mozilla.org/MPL/2.0/. */ 1909 1910 1911 1912 1913 const CTAParagraph = props => { 1914 const { 1915 content, 1916 handleAction 1917 } = props; 1918 if (!content?.text) { 1919 return null; 1920 } 1921 const onClick = react__WEBPACK_IMPORTED_MODULE_0___default().useCallback(event => { 1922 handleAction(event); 1923 event.preventDefault(); 1924 }, [handleAction]); 1925 return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("h2", { 1926 className: "cta-paragraph", 1927 style: { 1928 ..._lib_aboutwelcome_utils_mjs__WEBPACK_IMPORTED_MODULE_2__.AboutWelcomeUtils.getValidStyle(content?.style, _MSLocalized__WEBPACK_IMPORTED_MODULE_1__.CONFIGURABLE_STYLES) 1929 } 1930 }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_MSLocalized__WEBPACK_IMPORTED_MODULE_1__.Localized, { 1931 text: content.text 1932 }, content.text.string_name && typeof handleAction === "function" ? /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("span", { 1933 "data-l10n-id": content.text.string_id, 1934 onClick: onClick, 1935 onKeyUp: event => ["Enter", " "].includes(event.key) ? onClick(event) : null, 1936 value: "cta_paragraph" 1937 }, " ", /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("a", { 1938 "data-l10n-name": content.text.string_name, 1939 tabIndex: "0", 1940 role: "link" 1941 })) : null)); 1942 }; 1943 1944 /***/ }), 1945 /* 9 */ 1946 /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { 1947 1948 __webpack_require__.r(__webpack_exports__); 1949 /* harmony export */ __webpack_require__.d(__webpack_exports__, { 1950 /* harmony export */ HeroImage: () => (/* binding */ HeroImage) 1951 /* harmony export */ }); 1952 /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(1); 1953 /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_0__); 1954 /* harmony import */ var _lib_aboutwelcome_utils_mjs__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(3); 1955 /* This Source Code Form is subject to the terms of the Mozilla Public 1956 * License, v. 2.0. If a copy of the MPL was not distributed with this file, 1957 * You can obtain one at http://mozilla.org/MPL/2.0/. */ 1958 1959 1960 1961 const HeroImage = props => { 1962 const { 1963 height, 1964 url, 1965 alt 1966 } = props; 1967 if (!url) { 1968 return null; 1969 } 1970 return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { 1971 className: "hero-image" 1972 }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("img", { 1973 style: height ? { 1974 height 1975 } : null, 1976 src: url, 1977 loading: _lib_aboutwelcome_utils_mjs__WEBPACK_IMPORTED_MODULE_1__.AboutWelcomeUtils.getLoadingStrategyFor(url), 1978 alt: alt || "", 1979 role: alt ? null : "presentation" 1980 })); 1981 }; 1982 1983 /***/ }), 1984 /* 10 */ 1985 /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { 1986 1987 __webpack_require__.r(__webpack_exports__); 1988 /* harmony export */ __webpack_require__.d(__webpack_exports__, { 1989 /* harmony export */ OnboardingVideo: () => (/* binding */ OnboardingVideo) 1990 /* harmony export */ }); 1991 /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(1); 1992 /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_0__); 1993 /* This Source Code Form is subject to the terms of the Mozilla Public 1994 * License, v. 2.0. If a copy of the MPL was not distributed with this file, 1995 * You can obtain one at http://mozilla.org/MPL/2.0/. */ 1996 1997 1998 const OnboardingVideo = props => { 1999 const vidUrl = props.content.video_url; 2000 const autoplay = props.content.autoPlay; 2001 const handleVideoAction = event => { 2002 props.handleAction({ 2003 currentTarget: { 2004 value: event 2005 } 2006 }); 2007 }; 2008 return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", null, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("video", { 2009 // eslint-disable-line jsx-a11y/media-has-caption 2010 controls: true, 2011 autoPlay: autoplay, 2012 src: vidUrl, 2013 width: "604px", 2014 height: "340px", 2015 onPlay: () => handleVideoAction("video_start"), 2016 onEnded: () => handleVideoAction("video_end") 2017 }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("source", { 2018 src: vidUrl 2019 }))); 2020 }; 2021 2022 /***/ }), 2023 /* 11 */ 2024 /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { 2025 2026 __webpack_require__.r(__webpack_exports__); 2027 /* harmony export */ __webpack_require__.d(__webpack_exports__, { 2028 /* harmony export */ AdditionalCTA: () => (/* binding */ AdditionalCTA) 2029 /* harmony export */ }); 2030 /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(1); 2031 /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_0__); 2032 /* harmony import */ var _MSLocalized__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(5); 2033 /* harmony import */ var _SubmenuButton__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(12); 2034 /* This Source Code Form is subject to the terms of the Mozilla Public 2035 * License, v. 2.0. If a copy of the MPL was not distributed with this file, 2036 * You can obtain one at http://mozilla.org/MPL/2.0/. */ 2037 2038 2039 2040 2041 const AdditionalCTA = ({ 2042 content, 2043 handleAction 2044 }) => { 2045 let buttonStyle = ""; 2046 const isSplitButton = content.submenu_button?.attached_to === "additional_button"; 2047 let className = "additional-cta-box"; 2048 if (isSplitButton) { 2049 className += " split-button-container"; 2050 } 2051 if (!content.additional_button?.style) { 2052 buttonStyle = "primary"; 2053 } else { 2054 buttonStyle = content.additional_button?.style === "link" ? "cta-link" : content.additional_button?.style; 2055 } 2056 return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { 2057 className: className 2058 }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_MSLocalized__WEBPACK_IMPORTED_MODULE_1__.Localized, { 2059 text: content.additional_button?.label 2060 }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("button", { 2061 id: "additional_button", 2062 className: `${buttonStyle} additional-cta`, 2063 onClick: handleAction, 2064 value: "additional_button", 2065 disabled: content.additional_button?.disabled === true 2066 })), isSplitButton ? /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_SubmenuButton__WEBPACK_IMPORTED_MODULE_2__.SubmenuButton, { 2067 content: content, 2068 handleAction: handleAction 2069 }) : null); 2070 }; 2071 2072 /***/ }), 2073 /* 12 */ 2074 /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { 2075 2076 __webpack_require__.r(__webpack_exports__); 2077 /* harmony export */ __webpack_require__.d(__webpack_exports__, { 2078 /* harmony export */ SubmenuButton: () => (/* binding */ SubmenuButton) 2079 /* harmony export */ }); 2080 /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(1); 2081 /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_0__); 2082 /* harmony import */ var _MSLocalized__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(5); 2083 /* This Source Code Form is subject to the terms of the Mozilla Public 2084 * License, v. 2.0. If a copy of the MPL was not distributed with this file, 2085 * You can obtain one at http://mozilla.org/MPL/2.0/. */ 2086 2087 2088 2089 const SubmenuButton = props => { 2090 return document.createXULElement ? /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(SubmenuButtonInner, props) : null; 2091 }; 2092 function translateMenuitem(item, element) { 2093 let { 2094 label 2095 } = item; 2096 if (!label) { 2097 return; 2098 } 2099 if (label.raw) { 2100 element.setAttribute("label", label.raw); 2101 } 2102 if (label.access_key) { 2103 element.setAttribute("accesskey", label.access_key); 2104 } 2105 if (label.aria_label) { 2106 element.setAttribute("aria-label", label.aria_label); 2107 } 2108 if (label.tooltip_text) { 2109 element.setAttribute("tooltiptext", label.tooltip_text); 2110 } 2111 if (label.string_id) { 2112 element.setAttribute("data-l10n-id", label.string_id); 2113 if (label.args) { 2114 element.setAttribute("data-l10n-args", JSON.stringify(label.args)); 2115 } 2116 } 2117 } 2118 function addMenuitems(items, popup) { 2119 for (let item of items) { 2120 switch (item.type) { 2121 case "separator": 2122 popup.appendChild(document.createXULElement("menuseparator")); 2123 break; 2124 case "menu": 2125 { 2126 let menu = document.createXULElement("menu"); 2127 menu.className = "fxms-multi-stage-menu"; 2128 translateMenuitem(item, menu); 2129 if (item.id) { 2130 menu.value = item.id; 2131 } 2132 if (item.icon) { 2133 menu.classList.add("menu-iconic"); 2134 menu.setAttribute("image", item.icon); 2135 } 2136 popup.appendChild(menu); 2137 let submenuPopup = document.createXULElement("menupopup"); 2138 menu.appendChild(submenuPopup); 2139 addMenuitems(item.submenu, submenuPopup); 2140 break; 2141 } 2142 case "action": 2143 { 2144 let menuitem = document.createXULElement("menuitem"); 2145 translateMenuitem(item, menuitem); 2146 menuitem.config = item; 2147 if (item.id) { 2148 menuitem.value = item.id; 2149 } 2150 if (item.icon) { 2151 menuitem.classList.add("menuitem-iconic"); 2152 menuitem.setAttribute("image", item.icon); 2153 } 2154 popup.appendChild(menuitem); 2155 break; 2156 } 2157 } 2158 } 2159 } 2160 const SubmenuButtonInner = ({ 2161 content, 2162 handleAction 2163 }) => { 2164 const ref = (0,react__WEBPACK_IMPORTED_MODULE_0__.useRef)(null); 2165 const [isSubmenuExpanded, setIsSubmenuExpanded] = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)(false); 2166 const isPrimary = content.submenu_button?.style === "primary"; 2167 const onCommand = (0,react__WEBPACK_IMPORTED_MODULE_0__.useCallback)(event => { 2168 let { 2169 config 2170 } = event.target; 2171 let mockEvent = { 2172 currentTarget: ref.current, 2173 source: config.id, 2174 name: "command", 2175 action: config.action 2176 }; 2177 handleAction(mockEvent); 2178 }, [handleAction]); 2179 const onClick = (0,react__WEBPACK_IMPORTED_MODULE_0__.useCallback)(() => { 2180 let button = ref.current; 2181 let submenu = button?.querySelector(".fxms-multi-stage-submenu"); 2182 if (submenu && !button.hasAttribute("open")) { 2183 submenu.openPopup(button, { 2184 position: "after_end" 2185 }); 2186 } 2187 }, []); 2188 (0,react__WEBPACK_IMPORTED_MODULE_0__.useEffect)(() => { 2189 let button = ref.current; 2190 if (!button || button.querySelector(".fxms-multi-stage-submenu")) { 2191 return null; 2192 } 2193 let menupopup = document.createXULElement("menupopup"); 2194 menupopup.className = "fxms-multi-stage-submenu"; 2195 addMenuitems(content.submenu_button.submenu, menupopup); 2196 button.appendChild(menupopup); 2197 let stylesheet; 2198 if (!document.head.querySelector(`link[href="chrome://global/content/widgets.css"], link[href="chrome://global/skin/global.css"]`)) { 2199 stylesheet = document.createElement("link"); 2200 stylesheet.rel = "stylesheet"; 2201 stylesheet.href = "chrome://global/content/widgets.css"; 2202 document.head.appendChild(stylesheet); 2203 } 2204 if (!menupopup.listenersRegistered) { 2205 menupopup.addEventListener("command", onCommand); 2206 menupopup.addEventListener("popupshowing", event => { 2207 if (event.target === menupopup && event.target.anchorNode) { 2208 event.target.anchorNode.toggleAttribute("open", true); 2209 setIsSubmenuExpanded(true); 2210 } 2211 }); 2212 menupopup.addEventListener("popuphiding", event => { 2213 if (event.target === menupopup && event.target.anchorNode) { 2214 event.target.anchorNode.toggleAttribute("open", false); 2215 setIsSubmenuExpanded(false); 2216 } 2217 }); 2218 menupopup.listenersRegistered = true; 2219 } 2220 return () => { 2221 menupopup?.remove(); 2222 stylesheet?.remove(); 2223 }; 2224 }, [onCommand]); // eslint-disable-line react-hooks/exhaustive-deps 2225 2226 return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_MSLocalized__WEBPACK_IMPORTED_MODULE_1__.Localized, { 2227 text: content.submenu_button.label ?? {} 2228 }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("button", { 2229 id: "submenu_button", 2230 className: `submenu-button ${isPrimary ? "primary" : "secondary"}`, 2231 value: "submenu_button", 2232 onClick: onClick, 2233 ref: ref, 2234 "aria-haspopup": "menu", 2235 "aria-expanded": isSubmenuExpanded, 2236 "aria-labelledby": `${content.submenu_button.attached_to} submenu_button` 2237 })); 2238 }; 2239 2240 /***/ }), 2241 /* 13 */ 2242 /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { 2243 2244 __webpack_require__.r(__webpack_exports__); 2245 /* harmony export */ __webpack_require__.d(__webpack_exports__, { 2246 /* harmony export */ LinkParagraph: () => (/* binding */ LinkParagraph) 2247 /* harmony export */ }); 2248 /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(1); 2249 /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_0__); 2250 /* harmony import */ var _MSLocalized__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(5); 2251 /* This Source Code Form is subject to the terms of the Mozilla Public 2252 * License, v. 2.0. If a copy of the MPL was not distributed with this file, 2253 * You can obtain one at http://mozilla.org/MPL/2.0/. */ 2254 2255 2256 2257 const LinkParagraph = props => { 2258 const { 2259 text_content, 2260 handleAction 2261 } = props; 2262 const handleParagraphAction = (0,react__WEBPACK_IMPORTED_MODULE_0__.useCallback)(event => { 2263 if (event.target.closest("a")) { 2264 handleAction({ 2265 ...event, 2266 currentTarget: event.target 2267 }); 2268 } 2269 }, [handleAction]); 2270 const onKeyPress = (0,react__WEBPACK_IMPORTED_MODULE_0__.useCallback)(event => { 2271 if (event.key === "Enter" && !event.repeat) { 2272 handleParagraphAction(event); 2273 } 2274 }, [handleParagraphAction]); 2275 return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_MSLocalized__WEBPACK_IMPORTED_MODULE_1__.Localized, { 2276 text: text_content.text 2277 }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("p", { 2278 className: text_content.font_styles === "legal" ? "legal-paragraph" : "link-paragraph", 2279 onClick: handleParagraphAction, 2280 value: "link_paragraph", 2281 onKeyPress: onKeyPress 2282 }, text_content.link_keys?.map(link => /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("a", { 2283 key: link, 2284 value: link, 2285 role: "link", 2286 className: "text-link", 2287 "data-l10n-name": link 2288 // must pass in tabIndex when no href is provided 2289 , 2290 tabIndex: "0" 2291 }, " ")))); 2292 }; 2293 2294 /***/ }), 2295 /* 14 */ 2296 /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { 2297 2298 __webpack_require__.r(__webpack_exports__); 2299 /* harmony export */ __webpack_require__.d(__webpack_exports__, { 2300 /* harmony export */ ContentTiles: () => (/* binding */ ContentTiles) 2301 /* harmony export */ }); 2302 /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(1); 2303 /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_0__); 2304 /* harmony import */ var _MSLocalized__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(5); 2305 /* harmony import */ var _AddonsPicker__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(15); 2306 /* harmony import */ var _SingleSelect__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(17); 2307 /* harmony import */ var _MobileDownloads__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(20); 2308 /* harmony import */ var _MultiSelect__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(21); 2309 /* harmony import */ var _EmbeddedMigrationWizard__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(22); 2310 /* harmony import */ var _EmbeddedFxBackupOptIn__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(23); 2311 /* harmony import */ var _ActionChecklist__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(24); 2312 /* harmony import */ var _EmbeddedBrowser__WEBPACK_IMPORTED_MODULE_9__ = __webpack_require__(25); 2313 /* harmony import */ var _ConfirmationChecklist__WEBPACK_IMPORTED_MODULE_10__ = __webpack_require__(26); 2314 /* harmony import */ var _lib_aboutwelcome_utils_mjs__WEBPACK_IMPORTED_MODULE_11__ = __webpack_require__(3); 2315 /* harmony import */ var _EmbeddedBackupRestore__WEBPACK_IMPORTED_MODULE_12__ = __webpack_require__(27); 2316 /* This Source Code Form is subject to the terms of the Mozilla Public 2317 * License, v. 2.0. If a copy of the MPL was not distributed with this file, 2318 * You can obtain one at http://mozilla.org/MPL/2.0/. */ 2319 2320 2321 2322 2323 2324 2325 2326 2327 2328 2329 2330 2331 2332 2333 const HEADER_STYLES = ["backgroundColor", "border", "padding", "margin", "width", "height"]; 2334 const TILE_STYLES = ["marginBlock", "marginInline", "paddingBlock", "paddingInline"]; 2335 const CONTAINER_STYLES = ["padding", "margin", "marginBlock", "marginInline", "paddingBlock", "paddingInline", "flexDirection", "flexWrap", "flexFlow", "flexGrow", "flexShrink", "justifyContent", "alignItems", "gap"]; 2336 const ContentTiles = props => { 2337 const { 2338 content 2339 } = props; 2340 const [expandedTileIndex, setExpandedTileIndex] = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)(null); 2341 // State for header that toggles showing and hiding all tiles, if applicable 2342 const [tilesHeaderExpanded, setTilesHeaderExpanded] = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)(false); 2343 const { 2344 tiles 2345 } = content; 2346 if (!tiles) { 2347 return null; 2348 } 2349 2350 // eslint-disable-next-line react-hooks/rules-of-hooks 2351 (0,react__WEBPACK_IMPORTED_MODULE_0__.useEffect)(() => { 2352 // Run once when ContentTiles mounts to prefill activeMultiSelect 2353 if (!props.activeMultiSelect) { 2354 const tilesArray = Array.isArray(tiles) ? tiles : [tiles]; 2355 tilesArray.forEach((tile, index) => { 2356 if (tile.type !== "multiselect" || !tile.data) { 2357 return; 2358 } 2359 const multiSelectId = `tile-${index}`; 2360 const newActiveMultiSelect = []; 2361 tile.data.forEach(({ 2362 id, 2363 defaultValue 2364 }) => { 2365 if (defaultValue && id) { 2366 newActiveMultiSelect.push(id); 2367 } 2368 }); 2369 if (newActiveMultiSelect.length) { 2370 props.setActiveMultiSelect(newActiveMultiSelect, multiSelectId); 2371 } 2372 }); 2373 } 2374 }, [tiles]); // eslint-disable-line react-hooks/exhaustive-deps 2375 2376 (0,react__WEBPACK_IMPORTED_MODULE_0__.useEffect)(() => { 2377 /** 2378 * In Spotlight dialogs, the VO cursor can move without changing DOM focus. 2379 * When a user lands on content tiles, or a tile re-renders, DOM focus often 2380 * stays on or "snaps back" to the dialog’s first tabbable control by 2381 * SubDialog’s focus enforcement. Pressing Space/Enter then activates that 2382 * outside control instead of the VO target. 2383 * 2384 * To address this, we remember the last real DOM-focused element inside 2385 * #content-tiles-container. If focus jumps outside tiles without a recent 2386 * tab, such as with a VO focus move, restore focus to that element on the 2387 * next rAF. Tab navigation is unaffected. 2388 */ 2389 const page = document.querySelector("#multi-stage-message-root.onboardingContainer[data-page]")?.dataset.page || document.location.href; 2390 if (page !== "spotlight") { 2391 return () => {}; 2392 } 2393 const tilesEl = document.getElementById("content-tiles-container"); 2394 const dialog = tilesEl?.closest('main[role="alertdialog"]') || null; 2395 if (!tilesEl || !dialog) { 2396 return () => {}; 2397 } 2398 2399 // We use 250ms to tell “intentional tab navigation” from a programmatic 2400 // snap. It’s long enough to cover a human Tab press (and a quick double-tab 2401 // / key repeat), but short enough that we don’t delay correcting unintended 2402 // focus jumps. 2403 const TAB_GRACE_WINDOW_MS = 250; 2404 let lastTilesEl = null; 2405 let lastTabAt = 0; 2406 let restoring = false; 2407 function onKeyDown(e) { 2408 if (e.key === "Tab") { 2409 lastTabAt = performance.now(); 2410 } 2411 } 2412 function onFocusIn(event) { 2413 const { 2414 target 2415 } = event; 2416 2417 // Track true DOM focus inside tiles. 2418 if (tilesEl.contains(target)) { 2419 lastTilesEl = target; 2420 return; 2421 } 2422 2423 // If focus left tiles without a recent tab, treat as a programmatic snap. 2424 const tabRecently = performance.now() - lastTabAt < TAB_GRACE_WINDOW_MS; 2425 if (tabRecently || !lastTilesEl || !document.contains(lastTilesEl) || restoring) { 2426 return; 2427 } 2428 2429 // Restore immediately (before paint) to avoid visible flicker. 2430 restoring = true; 2431 try { 2432 lastTilesEl.focus({ 2433 preventScroll: true 2434 }); 2435 } finally { 2436 restoring = false; 2437 } 2438 } 2439 2440 // Preempt other dialog handlers. 2441 dialog.addEventListener("keydown", onKeyDown, true); 2442 dialog.addEventListener("focusin", onFocusIn, true); 2443 return () => { 2444 dialog.removeEventListener("keydown", onKeyDown, true); 2445 dialog.removeEventListener("focusin", onFocusIn, true); 2446 }; 2447 }, []); 2448 const toggleTile = (index, tile) => { 2449 const tileId = `${tile.type}${tile.id ? "_" : ""}${tile.id ?? ""}_header`; 2450 setExpandedTileIndex(prevIndex => prevIndex === index ? null : index); 2451 _lib_aboutwelcome_utils_mjs__WEBPACK_IMPORTED_MODULE_11__.AboutWelcomeUtils.sendActionTelemetry(props.messageId, tileId); 2452 }; 2453 const toggleTiles = () => { 2454 setTilesHeaderExpanded(prev => !prev); 2455 _lib_aboutwelcome_utils_mjs__WEBPACK_IMPORTED_MODULE_11__.AboutWelcomeUtils.sendActionTelemetry(props.messageId, "content_tiles_header"); 2456 }; 2457 function getTileMultiSelects(screenMultiSelects, index) { 2458 return screenMultiSelects?.[`tile-${index}`]; 2459 } 2460 function getTileActiveMultiSelect(activeMultiSelect, index) { 2461 return activeMultiSelect?.[`tile-${index}`]; 2462 } 2463 const renderContentTile = (tile, index = 0) => { 2464 const isExpanded = expandedTileIndex === index; 2465 const { 2466 header, 2467 title, 2468 subtitle 2469 } = tile; 2470 return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { 2471 key: index, 2472 className: `content-tile ${header ? "has-header" : ""}`, 2473 style: _lib_aboutwelcome_utils_mjs__WEBPACK_IMPORTED_MODULE_11__.AboutWelcomeUtils.getTileStyle(tile, TILE_STYLES) 2474 }, header?.title && /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("button", { 2475 className: "tile-header secondary", 2476 onClick: () => toggleTile(index, tile), 2477 "aria-expanded": isExpanded, 2478 "aria-controls": `tile-content-${index}`, 2479 style: _lib_aboutwelcome_utils_mjs__WEBPACK_IMPORTED_MODULE_11__.AboutWelcomeUtils.getValidStyle(header.style, HEADER_STYLES) 2480 }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { 2481 className: "header-text-container" 2482 }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_MSLocalized__WEBPACK_IMPORTED_MODULE_1__.Localized, { 2483 text: header.title 2484 }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("span", { 2485 className: "header-title" 2486 })), header.subtitle && /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_MSLocalized__WEBPACK_IMPORTED_MODULE_1__.Localized, { 2487 text: header.subtitle 2488 }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("span", { 2489 className: "header-subtitle" 2490 }))), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { 2491 className: "arrow-icon" 2492 })), (title || subtitle) && /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { 2493 className: "tile-title-container", 2494 id: `tile-title-container-${index}` 2495 }, title && /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_MSLocalized__WEBPACK_IMPORTED_MODULE_1__.Localized, { 2496 text: title 2497 }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("h1", { 2498 className: "tile-title", 2499 id: `content-tile-title-${index}` 2500 })), subtitle && /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_MSLocalized__WEBPACK_IMPORTED_MODULE_1__.Localized, { 2501 text: subtitle 2502 }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("p", { 2503 className: "tile-subtitle" 2504 }))), isExpanded || !header ? /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { 2505 className: "tile-content", 2506 id: `tile-content-${index}` 2507 }, tile.type === "addons-picker" && tile.data && /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_AddonsPicker__WEBPACK_IMPORTED_MODULE_2__.AddonsPicker, { 2508 content: { 2509 tiles: tile 2510 }, 2511 installedAddons: props.installedAddons, 2512 message_id: props.messageId, 2513 handleAction: props.handleAction, 2514 layout: content.position 2515 }), ["theme", "single-select"].includes(tile.type) && tile.data && /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_SingleSelect__WEBPACK_IMPORTED_MODULE_3__.SingleSelect, { 2516 content: { 2517 tiles: tile 2518 }, 2519 activeTheme: props.activeTheme, 2520 handleAction: props.handleAction, 2521 activeSingleSelectSelections: props.activeSingleSelectSelections, 2522 setActiveSingleSelectSelection: props.setActiveSingleSelectSelection, 2523 singleSelectId: `single-select-${index}` 2524 }), tile.type === "mobile_downloads" && tile.data && /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_MobileDownloads__WEBPACK_IMPORTED_MODULE_4__.MobileDownloads, { 2525 data: tile.data, 2526 handleAction: props.handleAction 2527 }), tile.type === "multiselect" && tile.data && /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_MultiSelect__WEBPACK_IMPORTED_MODULE_5__.MultiSelect, { 2528 content: { 2529 tiles: tile 2530 }, 2531 screenMultiSelects: getTileMultiSelects(props.screenMultiSelects, index), 2532 setScreenMultiSelects: props.setScreenMultiSelects, 2533 activeMultiSelect: getTileActiveMultiSelect(props.activeMultiSelect, index), 2534 setActiveMultiSelect: props.setActiveMultiSelect, 2535 multiSelectId: `tile-${index}` 2536 }), tile.type === "migration-wizard" && /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_EmbeddedMigrationWizard__WEBPACK_IMPORTED_MODULE_6__.EmbeddedMigrationWizard, { 2537 handleAction: props.handleAction, 2538 content: { 2539 tiles: tile 2540 } 2541 }), tile.type === "action_checklist" && tile.data && /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_ActionChecklist__WEBPACK_IMPORTED_MODULE_8__.ActionChecklist, { 2542 content: content, 2543 message_id: props.messageId 2544 }), tile.type === "embedded_browser" && tile.data?.url && /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_EmbeddedBrowser__WEBPACK_IMPORTED_MODULE_9__.EmbeddedBrowser, { 2545 url: tile.data.url, 2546 style: tile.data.style 2547 }), tile.type === "backup_restore" && /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_EmbeddedBackupRestore__WEBPACK_IMPORTED_MODULE_12__.EmbeddedBackupRestore, { 2548 handleAction: props.handleAction, 2549 content: { 2550 tiles: tile 2551 }, 2552 skipButton: props.content.skip_button 2553 }), tile.type === "fx_backup_file_path" && /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_EmbeddedFxBackupOptIn__WEBPACK_IMPORTED_MODULE_7__.EmbeddedFxBackupOptIn, { 2554 handleAction: props.handleAction, 2555 isEncryptedBackup: content.isEncryptedBackup, 2556 options: tile.options 2557 }), tile.type === "fx_backup_password" && /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_EmbeddedFxBackupOptIn__WEBPACK_IMPORTED_MODULE_7__.EmbeddedFxBackupOptIn, { 2558 handleAction: props.handleAction, 2559 isEncryptedBackup: content.isEncryptedBackup, 2560 options: tile.options 2561 }), tile.type === "confirmation-checklist" && tile.data && /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_ConfirmationChecklist__WEBPACK_IMPORTED_MODULE_10__.ConfirmationChecklist, { 2562 content: tile.data, 2563 handleAction: props.handleAction 2564 })) : null); 2565 }; 2566 const renderContentTiles = () => { 2567 if (Array.isArray(tiles)) { 2568 const containerStyle = content?.tiles_container?.style; 2569 return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { 2570 id: "content-tiles-container", 2571 style: _lib_aboutwelcome_utils_mjs__WEBPACK_IMPORTED_MODULE_11__.AboutWelcomeUtils.getValidStyle(containerStyle, CONTAINER_STYLES) 2572 }, tiles.map((tile, index) => renderContentTile(tile, index))); 2573 } 2574 // If tiles is not an array render the tile alone without a container 2575 return renderContentTile(tiles, 0); 2576 }; 2577 if (content.tiles_header) { 2578 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", { 2579 className: "content-tiles-header secondary", 2580 onClick: toggleTiles, 2581 "aria-expanded": tilesHeaderExpanded, 2582 "aria-controls": `content-tiles-container` 2583 }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_MSLocalized__WEBPACK_IMPORTED_MODULE_1__.Localized, { 2584 text: content.tiles_header.title 2585 }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("span", { 2586 className: "header-title" 2587 })), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { 2588 className: "arrow-icon" 2589 })), tilesHeaderExpanded && renderContentTiles()); 2590 } 2591 return renderContentTiles(tiles); 2592 }; 2593 2594 /***/ }), 2595 /* 15 */ 2596 /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { 2597 2598 __webpack_require__.r(__webpack_exports__); 2599 /* harmony export */ __webpack_require__.d(__webpack_exports__, { 2600 /* harmony export */ AddonsPicker: () => (/* binding */ AddonsPicker) 2601 /* harmony export */ }); 2602 /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(1); 2603 /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_0__); 2604 /* harmony import */ var _lib_aboutwelcome_utils_mjs__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(3); 2605 /* harmony import */ var _MSLocalized__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(5); 2606 /* harmony import */ var _InstallButton__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(16); 2607 /* This Source Code Form is subject to the terms of the Mozilla Public 2608 * License, v. 2.0. If a copy of the MPL was not distributed with this file, 2609 * You can obtain one at http://mozilla.org/MPL/2.0/. */ 2610 2611 2612 2613 2614 2615 const AddonsPicker = props => { 2616 const { 2617 content, 2618 installedAddons, 2619 layout 2620 } = props; 2621 if (!content) { 2622 return null; 2623 } 2624 function handleAction(event) { 2625 const { 2626 message_id 2627 } = props; 2628 let { 2629 action, 2630 source_id 2631 } = content.tiles.data[event.currentTarget.value]; 2632 let { 2633 type, 2634 data 2635 } = action; 2636 if (type === "INSTALL_ADDON_FROM_URL") { 2637 if (!data) { 2638 return; 2639 } 2640 } 2641 _lib_aboutwelcome_utils_mjs__WEBPACK_IMPORTED_MODULE_1__.AboutWelcomeUtils.handleUserAction({ 2642 type, 2643 data 2644 }); 2645 _lib_aboutwelcome_utils_mjs__WEBPACK_IMPORTED_MODULE_1__.AboutWelcomeUtils.sendActionTelemetry(message_id, source_id); 2646 } 2647 function handleAuthorClick(event, authorId) { 2648 event.stopPropagation(); 2649 _lib_aboutwelcome_utils_mjs__WEBPACK_IMPORTED_MODULE_1__.AboutWelcomeUtils.handleUserAction({ 2650 type: "OPEN_URL", 2651 data: { 2652 args: `https://addons.mozilla.org/firefox/user/${authorId}/`, 2653 where: "tab" 2654 } 2655 }); 2656 } 2657 return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { 2658 className: "addons-picker-container" 2659 }, content.tiles.data.map(({ 2660 id, 2661 name: addonName, 2662 type, 2663 description, 2664 icon, 2665 author, 2666 install_label, 2667 install_complete_label 2668 }, index) => addonName ? /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { 2669 key: id, 2670 className: "addon-container" 2671 }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { 2672 className: "rtamo-icon" 2673 }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("img", { 2674 className: `${type === "theme" ? "rtamo-theme-icon" : "brand-logo"}`, 2675 src: icon, 2676 role: "presentation", 2677 alt: "" 2678 })), layout === "split" ? /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { 2679 className: "addon-rows-container" 2680 }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { 2681 className: "addon-row" 2682 }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { 2683 className: "addon-author-details" 2684 }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_MSLocalized__WEBPACK_IMPORTED_MODULE_2__.Localized, { 2685 text: addonName 2686 }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { 2687 className: "addon-title" 2688 })), author && /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { 2689 className: "addon-author" 2690 }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_MSLocalized__WEBPACK_IMPORTED_MODULE_2__.Localized, { 2691 text: author.byLine 2692 }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("span", { 2693 className: "addon-by-line" 2694 })), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("button", { 2695 href: "#", 2696 onClick: e => { 2697 handleAuthorClick(e, author.id); 2698 }, 2699 className: "author-link" 2700 }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("span", null, author.name)))), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_InstallButton__WEBPACK_IMPORTED_MODULE_3__.InstallButton, { 2701 key: id, 2702 addonId: id, 2703 handleAction: handleAction, 2704 index: index, 2705 installedAddons: installedAddons, 2706 install_label: install_label, 2707 install_complete_label: install_complete_label 2708 })), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { 2709 className: "addon-row" 2710 }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_MSLocalized__WEBPACK_IMPORTED_MODULE_2__.Localized, { 2711 text: description 2712 }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { 2713 className: "addon-description" 2714 })))) : /*#__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", { 2715 className: "addon-details" 2716 }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_MSLocalized__WEBPACK_IMPORTED_MODULE_2__.Localized, { 2717 text: addonName 2718 }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { 2719 className: "addon-title" 2720 })), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_MSLocalized__WEBPACK_IMPORTED_MODULE_2__.Localized, { 2721 text: description 2722 }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { 2723 className: "addon-description" 2724 }))), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_InstallButton__WEBPACK_IMPORTED_MODULE_3__.InstallButton, { 2725 key: id, 2726 addonId: id, 2727 handleAction: handleAction, 2728 index: index, 2729 installedAddons: installedAddons, 2730 install_label: install_label, 2731 install_complete_label: install_complete_label 2732 }))) : null)); 2733 }; 2734 2735 /***/ }), 2736 /* 16 */ 2737 /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { 2738 2739 __webpack_require__.r(__webpack_exports__); 2740 /* harmony export */ __webpack_require__.d(__webpack_exports__, { 2741 /* harmony export */ InstallButton: () => (/* binding */ InstallButton), 2742 /* harmony export */ Loader: () => (/* binding */ Loader) 2743 /* harmony export */ }); 2744 /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(1); 2745 /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_0__); 2746 /* harmony import */ var _MSLocalized__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(5); 2747 /* This Source Code Form is subject to the terms of the Mozilla Public 2748 * License, v. 2.0. If a copy of the MPL was not distributed with this file, 2749 * You can obtain one at http://mozilla.org/MPL/2.0/. */ 2750 2751 2752 2753 const Loader = () => { 2754 return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("button", { 2755 className: "primary" 2756 }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { 2757 className: "loaderContainer" 2758 }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("span", { 2759 className: "loader" 2760 }))); 2761 }; 2762 const InstallButton = props => { 2763 // determine if the addon is already installed so the state is 2764 // consistent on refresh or navigation 2765 const [installing, setInstalling] = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)(false); 2766 const [installComplete, setInstallComplete] = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)(false); 2767 const defaultInstallLabel = { 2768 string_id: "amo-picker-install-button-label" 2769 }; 2770 function getDefaultInstallCompleteLabel(addonType = "") { 2771 let defaultInstallCompleteLabel; 2772 if (addonType && addonType === "theme") { 2773 defaultInstallCompleteLabel = { 2774 string_id: "return-to-amo-theme-install-complete-label" 2775 }; 2776 } else if (addonType && addonType === "extension") { 2777 defaultInstallCompleteLabel = { 2778 string_id: "return-to-amo-extension-install-complete-label" 2779 }; 2780 } else { 2781 defaultInstallCompleteLabel = { 2782 string_id: "amo-picker-install-complete-label" 2783 }; 2784 } 2785 return defaultInstallCompleteLabel; 2786 } 2787 (0,react__WEBPACK_IMPORTED_MODULE_0__.useEffect)(() => { 2788 setInstallComplete(props.installedAddons?.includes(props.addonId)); 2789 }, [props.addonId, props.installedAddons]); 2790 let buttonLabel = installComplete ? props.install_complete_label || getDefaultInstallCompleteLabel(props.addonType) : props.install_label || defaultInstallLabel; 2791 function onClick(event) { 2792 props.handleAction(event); 2793 // Replace the label with the spinner 2794 setInstalling(true); 2795 window.AWEnsureAddonInstalled(props.addonId).then(value => { 2796 if (value === "complete") { 2797 // Set the label to "Installed" 2798 setInstallComplete(true); 2799 } 2800 // Whether the addon installs or not, we want to remove the spinner 2801 setInstalling(false); 2802 }); 2803 } 2804 return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { 2805 className: "install-button-wrapper" 2806 }, installing ? /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(Loader, null) : /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_MSLocalized__WEBPACK_IMPORTED_MODULE_1__.Localized, { 2807 text: buttonLabel 2808 }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("button", { 2809 id: `install-button-${props.addonId}`, 2810 value: props.index, 2811 onClick: onClick, 2812 disabled: installComplete, 2813 className: "primary", 2814 "data-l10n-args": JSON.stringify({ 2815 "addon-name": props.addonName ?? "" 2816 }) 2817 }))); 2818 }; 2819 2820 /***/ }), 2821 /* 17 */ 2822 /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { 2823 2824 __webpack_require__.r(__webpack_exports__); 2825 /* harmony export */ __webpack_require__.d(__webpack_exports__, { 2826 /* harmony export */ SingleSelect: () => (/* binding */ SingleSelect) 2827 /* harmony export */ }); 2828 /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(1); 2829 /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_0__); 2830 /* harmony import */ var _MSLocalized__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(5); 2831 /* harmony import */ var _TileButton__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(18); 2832 /* harmony import */ var _TileList__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(19); 2833 /* harmony import */ var _lib_aboutwelcome_utils_mjs__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(3); 2834 /* This Source Code Form is subject to the terms of the Mozilla Public 2835 * License, v. 2.0. If a copy of the MPL was not distributed with this file, 2836 * You can obtain one at http://mozilla.org/MPL/2.0/. */ 2837 2838 2839 2840 2841 2842 2843 2844 // This component was formerly "Themes" and continues to support theme 2845 const SingleSelect = ({ 2846 activeSingleSelectSelections = {}, 2847 // This now holds all active selections keyed by `singleSelectId` 2848 activeTheme, 2849 content, 2850 handleAction, 2851 setActiveSingleSelectSelection, 2852 singleSelectId 2853 }) => { 2854 const category = content.tiles?.category?.type || content.tiles?.type; 2855 const isSingleSelect = category === "single-select"; 2856 const autoTriggerAllowed = itemAction => { 2857 // Currently only enabled for sidebar experiment prefs 2858 const allowedActions = ["SET_PREF"]; 2859 const allowedPrefs = ["sidebar.revamp", "sidebar.verticalTabs", "sidebar.visibility"]; 2860 const checkAction = action => { 2861 if (!allowedActions.includes(action.type)) { 2862 return false; 2863 } 2864 if (action.type === "SET_PREF" && !allowedPrefs.includes(action.data?.pref.name)) { 2865 return false; 2866 } 2867 return true; 2868 }; 2869 if (itemAction.type === "MULTI_ACTION") { 2870 // Only allow autoTrigger if all actions are allowed 2871 return !itemAction.data.actions.some(action => !checkAction(action)); 2872 } 2873 return checkAction(itemAction); 2874 }; 2875 2876 // When screen renders for first time or user navigates back, update state to 2877 // check default option. 2878 (0,react__WEBPACK_IMPORTED_MODULE_0__.useEffect)(() => { 2879 if (isSingleSelect && !activeSingleSelectSelections[singleSelectId]) { 2880 let newActiveSingleSelect = content.tiles?.selected || content.tiles?.data[0].id; 2881 setActiveSingleSelectSelection(newActiveSingleSelect, singleSelectId); 2882 let selectedTile = content.tiles?.data.find(opt => opt.id === newActiveSingleSelect); 2883 // If applicable, automatically trigger the action for the default 2884 // selected tile. 2885 if (isSingleSelect && content.tiles?.autoTrigger && autoTriggerAllowed(selectedTile?.action)) { 2886 handleAction({ 2887 currentTarget: { 2888 value: selectedTile.id 2889 } 2890 }); 2891 } 2892 } 2893 }, [activeSingleSelectSelections]); // eslint-disable-line react-hooks/exhaustive-deps 2894 2895 const CONFIGURABLE_STYLES = ["background", "borderRadius", "height", "marginBlock", "marginBlockStart", "marginBlockEnd", "marginInline", "paddingBlock", "paddingBlockStart", "paddingBlockEnd", "paddingInline", "paddingInlineStart", "paddingInlineEnd", "width"]; 2896 return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { 2897 className: `tiles-single-select-container` 2898 }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", null, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("fieldset", { 2899 className: `tiles-single-select-section ${category}` 2900 }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_MSLocalized__WEBPACK_IMPORTED_MODULE_1__.Localized, { 2901 text: content.subtitle 2902 }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("legend", { 2903 className: "sr-only" 2904 })), content.tiles.data.map(({ 2905 description, 2906 inert, 2907 icon, 2908 id, 2909 label = "", 2910 body = "", 2911 theme, 2912 tooltip, 2913 type = "", 2914 flair, 2915 style, 2916 tilebutton 2917 }) => { 2918 const value = id || theme; 2919 let inputName = `select-item-${id}`; 2920 if (!isSingleSelect) { 2921 inputName = category === "theme" ? "theme" : id; // unique names per item are currently used in the wallpaper picker 2922 } 2923 const selected = theme && theme === activeTheme || isSingleSelect && activeSingleSelectSelections[singleSelectId] === value; 2924 const valOrObj = val => typeof val === "object" ? val : {}; 2925 const handleClick = evt => { 2926 if (isSingleSelect) { 2927 setActiveSingleSelectSelection(value, singleSelectId); // Update selection for the specific component 2928 } 2929 handleAction(evt); 2930 }; 2931 const handleKeyDown = evt => { 2932 if (evt.key === "Enter" || evt.keyCode === 13) { 2933 // Set target value to the input inside of the selected label 2934 evt.currentTarget.value = value; 2935 handleClick(evt); 2936 } 2937 }; 2938 return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_MSLocalized__WEBPACK_IMPORTED_MODULE_1__.Localized, { 2939 key: value + (isSingleSelect ? "" : label), 2940 text: valOrObj(tooltip) 2941 }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("label", { 2942 className: `select-item ${type}`, 2943 onKeyDown: e => handleKeyDown(e), 2944 style: { 2945 ..._lib_aboutwelcome_utils_mjs__WEBPACK_IMPORTED_MODULE_4__.AboutWelcomeUtils.getValidStyle(style, CONFIGURABLE_STYLES), 2946 ...(icon?.width ? { 2947 minWidth: icon.width 2948 } : {}) 2949 } 2950 }, flair ? /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_MSLocalized__WEBPACK_IMPORTED_MODULE_1__.Localized, { 2951 text: valOrObj(flair.text) 2952 }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("span", { 2953 className: `flair ${flair.centered ? "centered" : ""} ${flair.spacer ? "spacer" : ""} ${type}` 2954 })) : "", /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_MSLocalized__WEBPACK_IMPORTED_MODULE_1__.Localized, { 2955 text: valOrObj(description) 2956 }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("input", { 2957 type: "radio", 2958 value: value, 2959 name: inputName, 2960 checked: selected, 2961 className: "sr-only input", 2962 disabled: inert, 2963 onClick: e => handleClick(e) 2964 })), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { 2965 className: `icon ${selected ? " selected" : ""} ${value}`, 2966 style: _lib_aboutwelcome_utils_mjs__WEBPACK_IMPORTED_MODULE_4__.AboutWelcomeUtils.getValidStyle(icon, CONFIGURABLE_STYLES) 2967 }), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_MSLocalized__WEBPACK_IMPORTED_MODULE_1__.Localized, { 2968 text: label 2969 }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { 2970 className: "text label-text" 2971 })), body.items ? /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_TileList__WEBPACK_IMPORTED_MODULE_3__.TileList, { 2972 content: body 2973 }) : /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_MSLocalized__WEBPACK_IMPORTED_MODULE_1__.Localized, { 2974 text: body 2975 }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { 2976 className: "text body-text" 2977 })), tilebutton ? /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_TileButton__WEBPACK_IMPORTED_MODULE_2__.TileButton, { 2978 content: tilebutton, 2979 handleAction: handleAction, 2980 inputName: inputName 2981 }) : "")); 2982 })))); 2983 }; 2984 2985 /***/ }), 2986 /* 18 */ 2987 /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { 2988 2989 __webpack_require__.r(__webpack_exports__); 2990 /* harmony export */ __webpack_require__.d(__webpack_exports__, { 2991 /* harmony export */ TileButton: () => (/* binding */ TileButton) 2992 /* harmony export */ }); 2993 /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(1); 2994 /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_0__); 2995 /* harmony import */ var _MSLocalized__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(5); 2996 /* This Source Code Form is subject to the terms of the Mozilla Public 2997 * License, v. 2.0. If a copy of the MPL was not distributed with this file, 2998 * You can obtain one at http://mozilla.org/MPL/2.0/. */ 2999 3000 3001 3002 const TileButton = props => { 3003 const { 3004 content, 3005 handleAction, 3006 inputName 3007 } = props; 3008 const ref = (0,react__WEBPACK_IMPORTED_MODULE_0__.useRef)(null); 3009 if (!content) { 3010 return null; 3011 } 3012 function onClick(event) { 3013 let mockEvent = { 3014 currentTarget: ref.current, 3015 source: event.target.id, 3016 name: "command", 3017 action: content.action 3018 }; 3019 handleAction(mockEvent); 3020 } 3021 return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_MSLocalized__WEBPACK_IMPORTED_MODULE_1__.Localized, { 3022 text: content.label 3023 }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("button", { 3024 id: `tile-button-${inputName}`, 3025 onClick: onClick, 3026 value: "tile_button", 3027 ref: ref, 3028 className: `${content.style} tile-button slim` 3029 })); 3030 }; 3031 3032 /***/ }), 3033 /* 19 */ 3034 /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { 3035 3036 __webpack_require__.r(__webpack_exports__); 3037 /* harmony export */ __webpack_require__.d(__webpack_exports__, { 3038 /* harmony export */ TileList: () => (/* binding */ TileList) 3039 /* harmony export */ }); 3040 /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(1); 3041 /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_0__); 3042 /* harmony import */ var _lib_aboutwelcome_utils_mjs__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(3); 3043 /* harmony import */ var _MSLocalized__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(5); 3044 /* This Source Code Form is subject to the terms of the Mozilla Public 3045 * License, v. 2.0. If a copy of the MPL was not distributed with this file, 3046 * You can obtain one at http://mozilla.org/MPL/2.0/. */ 3047 3048 3049 3050 3051 const TileList = props => { 3052 const { 3053 content 3054 } = props; 3055 if (!content) { 3056 return null; 3057 } 3058 const CONFIGURABLE_STYLES = ["background", "borderRadius", "height", "marginBlock", "marginBlockStart", "marginBlockEnd", "marginInline", "paddingBlock", "paddingBlockStart", "paddingBlockEnd", "paddingInline", "paddingInlineStart", "paddingInlineEnd", "width"]; 3059 return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { 3060 className: "tile-list-container" 3061 }, content.items.map(({ 3062 icon, 3063 text 3064 }, index) => /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { 3065 key: index, 3066 className: "tile-list-item" 3067 }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { 3068 className: "tile-list-icon-wrapper" 3069 }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { 3070 className: "tile-list-icon", 3071 style: _lib_aboutwelcome_utils_mjs__WEBPACK_IMPORTED_MODULE_1__.AboutWelcomeUtils.getValidStyle(icon, CONFIGURABLE_STYLES) 3072 })), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { 3073 className: "tile-list-text" 3074 }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_MSLocalized__WEBPACK_IMPORTED_MODULE_2__.Localized, { 3075 text: text 3076 }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { 3077 className: "text body-text" 3078 })))))); 3079 }; 3080 3081 /***/ }), 3082 /* 20 */ 3083 /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { 3084 3085 __webpack_require__.r(__webpack_exports__); 3086 /* harmony export */ __webpack_require__.d(__webpack_exports__, { 3087 /* harmony export */ MarketplaceButtons: () => (/* binding */ MarketplaceButtons), 3088 /* harmony export */ MobileDownloads: () => (/* binding */ MobileDownloads) 3089 /* harmony export */ }); 3090 /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(1); 3091 /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_0__); 3092 /* harmony import */ var _MSLocalized__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(5); 3093 /* harmony import */ var _lib_aboutwelcome_utils_mjs__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(3); 3094 /* This Source Code Form is subject to the terms of the Mozilla Public 3095 * License, v. 2.0. If a copy of the MPL was not distributed with this file, 3096 * You can obtain one at http://mozilla.org/MPL/2.0/. */ 3097 3098 3099 3100 3101 const MarketplaceButtons = props => { 3102 return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("ul", { 3103 className: "mobile-download-buttons" 3104 }, props.buttons.includes("ios") ? /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("li", { 3105 className: "ios" 3106 }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("button", { 3107 "data-l10n-id": "spotlight-ios-marketplace-button", 3108 value: "ios", 3109 onClick: props.handleAction 3110 })) : null, props.buttons.includes("android") ? /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("li", { 3111 className: "android" 3112 }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("button", { 3113 "data-l10n-id": "spotlight-android-marketplace-button", 3114 value: "android", 3115 onClick: props.handleAction 3116 })) : null); 3117 }; 3118 const MobileDownloads = props => { 3119 const { 3120 QR_code: QRCode 3121 } = props.data; 3122 const showEmailLink = props.data.email && window.AWSendToDeviceEmailsSupported(); 3123 return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { 3124 className: "mobile-downloads" 3125 }, QRCode ? /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("img", { 3126 "data-l10n-id": QRCode.alt_text.string_id ? QRCode.alt_text.string_id : null, 3127 className: "qr-code-image", 3128 alt: typeof QRCode.alt_text === "string" ? QRCode.alt_text : "", 3129 src: QRCode.image_url, 3130 loading: _lib_aboutwelcome_utils_mjs__WEBPACK_IMPORTED_MODULE_2__.AboutWelcomeUtils.getLoadingStrategyFor(QRCode.image_url) 3131 }) : 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, { 3132 text: props.data.email.link_text 3133 }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("button", { 3134 className: "email-link", 3135 value: "email_link", 3136 onClick: props.handleAction 3137 }))) : null, props.data.marketplace_buttons ? /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(MarketplaceButtons, { 3138 buttons: props.data.marketplace_buttons, 3139 handleAction: props.handleAction 3140 }) : null); 3141 }; 3142 3143 /***/ }), 3144 /* 21 */ 3145 /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { 3146 3147 __webpack_require__.r(__webpack_exports__); 3148 /* harmony export */ __webpack_require__.d(__webpack_exports__, { 3149 /* harmony export */ MultiSelect: () => (/* binding */ MultiSelect) 3150 /* harmony export */ }); 3151 /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(1); 3152 /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_0__); 3153 /* harmony import */ var _MSLocalized__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(5); 3154 /* harmony import */ var _lib_aboutwelcome_utils_mjs__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(3); 3155 /* This Source Code Form is subject to the terms of the Mozilla Public 3156 * License, v. 2.0. If a copy of the MPL was not distributed with this file, 3157 * You can obtain one at http://mozilla.org/MPL/2.0/. */ 3158 3159 3160 3161 3162 const MULTI_SELECT_STYLES = [..._MSLocalized__WEBPACK_IMPORTED_MODULE_1__.CONFIGURABLE_STYLES, "flexDirection", "flexWrap", "flexFlow", "flexGrow", "flexShrink", "justifyContent", "alignItems", "gap"]; 3163 const TILE_STYLES = ["marginBlock", "marginInline", "paddingBlock", "paddingInline"]; 3164 3165 // Do not include styles applied at the content tile level 3166 for (let i = MULTI_SELECT_STYLES.length - 1; i >= 0; i--) { 3167 if (TILE_STYLES.includes(MULTI_SELECT_STYLES[i])) { 3168 MULTI_SELECT_STYLES.splice(i, 1); 3169 } 3170 } 3171 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"]; 3172 const MultiSelect = ({ 3173 content, 3174 screenMultiSelects, 3175 setScreenMultiSelects, 3176 activeMultiSelect, 3177 setActiveMultiSelect, 3178 multiSelectId 3179 }) => { 3180 const { 3181 data, 3182 multiSelectItemDesign 3183 } = content.tiles; 3184 const isPicker = multiSelectItemDesign === "picker"; 3185 const refs = (0,react__WEBPACK_IMPORTED_MODULE_0__.useRef)({}); 3186 const handleChange = (0,react__WEBPACK_IMPORTED_MODULE_0__.useCallback)(() => { 3187 const newActiveMultiSelect = []; 3188 Object.keys(refs.current).forEach(key => { 3189 if (refs.current[key]?.checked) { 3190 newActiveMultiSelect.push(key); 3191 } 3192 }); 3193 setActiveMultiSelect(newActiveMultiSelect, multiSelectId); 3194 }, [setActiveMultiSelect, multiSelectId]); 3195 const items = (0,react__WEBPACK_IMPORTED_MODULE_0__.useMemo)(() => { 3196 function getOrderedIds() { 3197 if (screenMultiSelects) { 3198 return screenMultiSelects; 3199 } 3200 let orderedIds = data.map(item => ({ 3201 id: item.id, 3202 rank: item.randomize ? Math.random() : NaN 3203 })).sort((a, b) => b.rank - a.rank).map(({ 3204 id 3205 }) => id); 3206 setScreenMultiSelects(orderedIds, multiSelectId); 3207 return orderedIds; 3208 } 3209 return getOrderedIds().map(id => data.find(item => item.id === id)); 3210 }, [] // eslint-disable-line react-hooks/exhaustive-deps 3211 ); 3212 const containerStyle = (0,react__WEBPACK_IMPORTED_MODULE_0__.useMemo)(() => _lib_aboutwelcome_utils_mjs__WEBPACK_IMPORTED_MODULE_2__.AboutWelcomeUtils.getTileStyle(content.tiles, MULTI_SELECT_STYLES), [content.tiles]); 3213 const PickerIcon = ({ 3214 emoji, 3215 bgColor, 3216 isChecked 3217 }) => { 3218 return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("span", { 3219 className: `picker-icon ${isChecked ? "picker-checked" : ""}`, 3220 style: { 3221 ...(!isChecked && bgColor && { 3222 backgroundColor: bgColor 3223 }) 3224 } 3225 }, !isChecked && emoji ? emoji : ""); 3226 }; 3227 3228 // This handles interaction for when the user is clicking on or keyboard-interacting 3229 // with the container element when using the picker design. It is required 3230 // for appropriate accessibility. 3231 const handleCheckboxContainerInteraction = e => { 3232 if (!isPicker) { 3233 return; 3234 } 3235 if (e.type === "keydown") { 3236 // Prevent scroll on space presses 3237 if (e.key === " ") { 3238 e.preventDefault(); 3239 } 3240 3241 // Only handle space and enter keypresses 3242 if (e.key !== " " && e.key !== "Enter") { 3243 return; 3244 } 3245 } 3246 const container = e.currentTarget; 3247 // Manually flip the hidden checkbox since handleChange relies on it 3248 const checkbox = container.querySelector('input[type="checkbox"]'); 3249 checkbox.checked = !checkbox.checked; 3250 3251 // Manually call handleChange to update the multiselect state 3252 handleChange(); 3253 }; 3254 3255 // When screen renders for first time, update state 3256 // with checkbox ids that has defaultvalue true 3257 (0,react__WEBPACK_IMPORTED_MODULE_0__.useEffect)(() => { 3258 if (!activeMultiSelect) { 3259 let newActiveMultiSelect = []; 3260 items.forEach(({ 3261 id, 3262 defaultValue 3263 }) => { 3264 if (defaultValue && id) { 3265 newActiveMultiSelect.push(id); 3266 } 3267 }); 3268 setActiveMultiSelect(newActiveMultiSelect, multiSelectId); 3269 } 3270 }, []); // eslint-disable-line react-hooks/exhaustive-deps 3271 3272 return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { 3273 className: `multi-select-container ${multiSelectItemDesign || ""}`, 3274 style: containerStyle, 3275 role: items.some(({ 3276 type, 3277 group 3278 }) => type === "radio" && group) ? "radiogroup" : "group", 3279 "aria-labelledby": "multi-stage-multi-select-label" 3280 }, content.tiles.label ? /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_MSLocalized__WEBPACK_IMPORTED_MODULE_1__.Localized, { 3281 text: content.tiles.label 3282 }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("h2", { 3283 id: "multi-stage-multi-select-label" 3284 })) : null, items.map(({ 3285 id, 3286 label, 3287 description, 3288 icon, 3289 type = "checkbox", 3290 group, 3291 style, 3292 pickerEmoji, 3293 pickerEmojiBackgroundColor 3294 }) => /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { 3295 key: id + label, 3296 className: "checkbox-container multi-select-item", 3297 style: _lib_aboutwelcome_utils_mjs__WEBPACK_IMPORTED_MODULE_2__.AboutWelcomeUtils.getValidStyle(style, MULTI_SELECT_STYLES), 3298 tabIndex: isPicker ? "0" : null, 3299 onClick: isPicker ? handleCheckboxContainerInteraction : null, 3300 onKeyDown: isPicker ? handleCheckboxContainerInteraction : null, 3301 role: isPicker ? "checkbox" : null, 3302 "aria-checked": isPicker ? activeMultiSelect?.includes(id) : null 3303 }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("input", { 3304 type: type // checkbox or radio 3305 , 3306 id: id, 3307 value: id, 3308 name: group, 3309 checked: activeMultiSelect?.includes(id), 3310 style: _lib_aboutwelcome_utils_mjs__WEBPACK_IMPORTED_MODULE_2__.AboutWelcomeUtils.getValidStyle(icon?.style, MULTI_SELECT_ICON_STYLES), 3311 onChange: handleChange, 3312 ref: el => refs.current[id] = el, 3313 "aria-describedby": description ? `${id}-description` : null, 3314 "aria-labelledby": description ? `${id}-label` : null, 3315 tabIndex: isPicker ? "-1" : "0" 3316 }), isPicker && /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(PickerIcon, { 3317 emoji: pickerEmoji, 3318 bgColor: pickerEmojiBackgroundColor, 3319 isChecked: activeMultiSelect?.includes(id) 3320 }), label ? /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_MSLocalized__WEBPACK_IMPORTED_MODULE_1__.Localized, { 3321 text: label 3322 }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("label", { 3323 id: `${id}-label`, 3324 htmlFor: id 3325 })) : null, description ? /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_MSLocalized__WEBPACK_IMPORTED_MODULE_1__.Localized, { 3326 text: description 3327 }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("p", { 3328 id: `${id}-description` 3329 })) : null))); 3330 }; 3331 3332 /***/ }), 3333 /* 22 */ 3334 /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { 3335 3336 __webpack_require__.r(__webpack_exports__); 3337 /* harmony export */ __webpack_require__.d(__webpack_exports__, { 3338 /* harmony export */ EmbeddedMigrationWizard: () => (/* binding */ EmbeddedMigrationWizard) 3339 /* harmony export */ }); 3340 /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(1); 3341 /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_0__); 3342 /* This Source Code Form is subject to the terms of the Mozilla Public 3343 * License, v. 2.0. If a copy of the MPL was not distributed with this file, 3344 * You can obtain one at http://mozilla.org/MPL/2.0/. */ 3345 3346 3347 3348 /** 3349 * Embeds a migration wizard component within About:Welcome, 3350 * and passes configuration options from content to the migration-wizard element 3351 * 3352 * @param {function} handleAction - The action handler function that processes migration events 3353 * @param {object} content - The content object that contains tiles configuration 3354 * @param {object} content.tiles - The tiles configuration object 3355 * @param {object} content.tiles.migration_wizard_options - Configuration options for the migration wizard 3356 * All options, including migration_wizard_options itself, are optional and have fallback values: 3357 * - {boolean} force_show_import_all - Whether to force show import all option 3358 * - {string} option_expander_title_string - Title string for the option expander 3359 * - {boolean} hide_option_expander_subtitle - Whether or not to hide the option expander subtitle 3360 * - {string} data_import_complete_success_string - Success message string after import completion 3361 * - {string} selection_header_string - Header string for the selection section 3362 * - {string} selection_subheader_string - Subheader string for the selection section 3363 * - {boolean} hide_select_all - Whether to hide the select all option 3364 * - {string} checkbox_margin_inline - Inline margin for checkboxes 3365 * - {string} checkbox_margin_block - Block margin for checkboxes 3366 * - {string} import_button_string - Text string for the import button 3367 * - {string} import_button_class - CSS class for the import button 3368 * - {string} header_font_size - Font size for the header 3369 * - {string} header_font_weight - Font weight for the header 3370 * - {string} header_margin_block - Block margin for the header 3371 * - {string} subheader_font_size - Font size for the subheader 3372 * - {string} subheader_font_weight - Font weight for the subheader 3373 * - {string} subheader_margin_block - Block margin for the subheader 3374 */ 3375 const EmbeddedMigrationWizard = ({ 3376 handleAction, 3377 content 3378 }) => { 3379 const ref = (0,react__WEBPACK_IMPORTED_MODULE_0__.useRef)(); 3380 const options = content.tiles?.migration_wizard_options; 3381 (0,react__WEBPACK_IMPORTED_MODULE_0__.useEffect)(() => { 3382 const handleBeginMigration = () => { 3383 handleAction({ 3384 currentTarget: { 3385 value: "migrate_start" 3386 }, 3387 source: "primary_button" 3388 }); 3389 }; 3390 const handleClose = () => { 3391 handleAction({ 3392 currentTarget: { 3393 value: "migrate_close" 3394 } 3395 }); 3396 }; 3397 const { 3398 current 3399 } = ref; 3400 current?.addEventListener("MigrationWizard:BeginMigration", handleBeginMigration); 3401 current?.addEventListener("MigrationWizard:Close", handleClose); 3402 return () => { 3403 current?.removeEventListener("MigrationWizard:BeginMigration", handleBeginMigration); 3404 current?.removeEventListener("MigrationWizard:Close", handleClose); 3405 }; 3406 }, []); // eslint-disable-line react-hooks/exhaustive-deps 3407 return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("migration-wizard", { 3408 "in-aboutwelcome-bundle": "", 3409 "force-show-import-all": options?.force_show_import_all || "false", 3410 "auto-request-state": "", 3411 ref: ref, 3412 "option-expander-title-string": options?.option_expander_title_string || "", 3413 "hide-option-expander-subtitle": options?.hide_option_expander_subtitle || false, 3414 "data-import-complete-success-string": options?.data_import_complete_success_string || "", 3415 "selection-header-string": options?.selection_header_string || "", 3416 "selection-subheader-string": options?.selection_subheader_string || "", 3417 "hide-select-all": options?.hide_select_all || false, 3418 "checkbox-margin-inline": options?.checkbox_margin_inline || "", 3419 "checkbox-margin-block": options?.checkbox_margin_block || "", 3420 "import-button-string": options?.import_button_string || "", 3421 "import-button-class": options?.import_button_class || "", 3422 "header-font-size": options?.header_font_size || "", 3423 "header-font-weight": options?.header_font_weight || "", 3424 "header-margin-block": options?.header_margin_block || "", 3425 "subheader-font-size": options?.subheader_font_size || "", 3426 "subheader-font-weight": options?.subheader_font_weight || "", 3427 "subheader-margin-block": options?.subheader_margin_block || "" 3428 }); 3429 }; 3430 3431 /***/ }), 3432 /* 23 */ 3433 /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { 3434 3435 __webpack_require__.r(__webpack_exports__); 3436 /* harmony export */ __webpack_require__.d(__webpack_exports__, { 3437 /* harmony export */ EmbeddedFxBackupOptIn: () => (/* binding */ EmbeddedFxBackupOptIn) 3438 /* harmony export */ }); 3439 /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(1); 3440 /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_0__); 3441 /* This Source Code Form is subject to the terms of the Mozilla Public 3442 * License, v. 2.0. If a copy of the MPL was not distributed with this 3443 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ 3444 3445 3446 const EmbeddedFxBackupOptIn = ({ 3447 handleAction, 3448 isEncryptedBackup, 3449 options 3450 }) => { 3451 const backupRef = (0,react__WEBPACK_IMPORTED_MODULE_0__.useRef)(null); 3452 const { 3453 // hide_password_input means it is the file chooser screen 3454 hide_password_input, 3455 hide_secondary_button, 3456 file_path_label, 3457 turn_on_backup_header, 3458 create_password_label, 3459 turn_on_backup_confirm_btn_label, 3460 turn_on_backup_cancel_btn_label 3461 } = options || {}; 3462 (0,react__WEBPACK_IMPORTED_MODULE_0__.useEffect)(() => { 3463 const { 3464 current 3465 } = backupRef; 3466 const handleEnableScheduledBackups = () => { 3467 handleAction({ 3468 currentTarget: { 3469 value: "tile_button" 3470 }, 3471 action: { 3472 navigate: true 3473 }, 3474 source: "backup_enabled" 3475 }); 3476 }; 3477 const handleAdvanceScreens = () => { 3478 handleAction({ 3479 currentTarget: { 3480 value: "tile_button" 3481 }, 3482 action: { 3483 navigate: true 3484 }, 3485 source: "advance_screens" 3486 }); 3487 }; 3488 const handleStateUpdate = ({ 3489 detail: { 3490 state 3491 } 3492 }) => { 3493 if (!current || !state) { 3494 return; 3495 } 3496 let { 3497 fileName, 3498 path, 3499 iconURL 3500 } = state.defaultParent; 3501 current.setAttribute("defaultlabel", fileName); 3502 current.setAttribute("defaultpath", path); 3503 current.setAttribute("defaulticonurl", iconURL); 3504 current.supportBaseLink = state.supportBaseLink; 3505 }; 3506 current?.addEventListener("BackupUI:StateWasUpdated", handleStateUpdate); 3507 current?.addEventListener("BackupUI:EnableScheduledBackups", handleEnableScheduledBackups); 3508 current?.addEventListener("SpotlightOnboardingAdvanceScreens", handleAdvanceScreens); 3509 return () => { 3510 current?.removeEventListener("BackupUI:EnableScheduledBackups", handleEnableScheduledBackups); 3511 current?.removeEventListener("BackupUI:StateWasUpdated", handleStateUpdate); 3512 current?.removeEventListener("SpotlightOnboardingAdvanceScreens", handleAdvanceScreens); 3513 }; 3514 }, []); // eslint-disable-line react-hooks/exhaustive-deps 3515 3516 return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("turn-on-scheduled-backups", { 3517 ref: backupRef, 3518 "hide-headers": "", 3519 "hide-password-input": !isEncryptedBackup || hide_password_input ? "" : undefined, 3520 "hide-secondary-button": !isEncryptedBackup || hide_secondary_button ? "" : undefined, 3521 "hide-file-path-chooser": isEncryptedBackup && !hide_password_input ? "" : undefined, 3522 "embedded-fx-backup-opt-in": "", 3523 "backup-is-encrypted": isEncryptedBackup ? "" : undefined, 3524 "file-path-label-l10n-id": file_path_label, 3525 "turn-on-backup-header-l10n-id": turn_on_backup_header, 3526 "create-password-label-l10n-id": create_password_label, 3527 "turn-on-backup-confirm-btn-l10n-id": turn_on_backup_confirm_btn_label, 3528 "turn-on-backup-cancel-btn-l10n-id": turn_on_backup_cancel_btn_label 3529 }); 3530 }; 3531 3532 /***/ }), 3533 /* 24 */ 3534 /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { 3535 3536 __webpack_require__.r(__webpack_exports__); 3537 /* harmony export */ __webpack_require__.d(__webpack_exports__, { 3538 /* harmony export */ ActionChecklist: () => (/* binding */ ActionChecklist), 3539 /* harmony export */ ActionChecklistItem: () => (/* binding */ ActionChecklistItem), 3540 /* harmony export */ ActionChecklistProgressBar: () => (/* binding */ ActionChecklistProgressBar) 3541 /* harmony export */ }); 3542 /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(1); 3543 /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_0__); 3544 /* harmony import */ var _MSLocalized__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(5); 3545 /* harmony import */ var _lib_aboutwelcome_utils_mjs__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(3); 3546 /* This Source Code Form is subject to the terms of the Mozilla Public 3547 * License, v. 2.0. If a copy of the MPL was not distributed with this file, 3548 * You can obtain one at http://mozilla.org/MPL/2.0/. */ 3549 3550 3551 3552 3553 async function evaluateTargeting(targeting) { 3554 return await window.AWEvaluateAttributeTargeting(targeting); 3555 } 3556 const ActionChecklistItem = ({ 3557 item, 3558 index, 3559 handleAction, 3560 showExternalLinkIcon 3561 }) => { 3562 const [actionTargeting, setActionTargeting] = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)(true); 3563 (0,react__WEBPACK_IMPORTED_MODULE_0__.useEffect)(() => { 3564 const setInitialTargetingValue = async () => { 3565 setActionTargeting(await evaluateTargeting(item.targeting)); 3566 }; 3567 setInitialTargetingValue(); 3568 }, []); // eslint-disable-line react-hooks/exhaustive-deps 3569 3570 function onButtonClick(event) { 3571 // Immediately set targeting to true to disable the button. 3572 // It will re-evaluate its targeting on the next load. 3573 setActionTargeting(true); 3574 handleAction(event); 3575 } 3576 return ( 3577 /*#__PURE__*/ 3578 // if actionTargeting is false, we want the button to be enabled 3579 // because it signifies that the action is not yet complete. 3580 // If it is true, the action has been completed, so we can disable the button. 3581 react__WEBPACK_IMPORTED_MODULE_0___default().createElement("button", { 3582 id: item.id, 3583 value: index, 3584 key: item.id, 3585 disabled: actionTargeting, 3586 onClick: onButtonClick 3587 }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { 3588 className: "action-checklist-label-container" 3589 }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { 3590 className: "check-icon-container" 3591 }, actionTargeting ? /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { 3592 className: "check-filled" 3593 }) : /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { 3594 className: "check-empty" 3595 })), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_MSLocalized__WEBPACK_IMPORTED_MODULE_1__.Localized, { 3596 text: item.label 3597 }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("span", null))), !actionTargeting && showExternalLinkIcon && /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { 3598 className: "external-link-icon-container" 3599 }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { 3600 className: "external-link-icon" 3601 }))) 3602 ); 3603 }; 3604 const ActionChecklistProgressBar = ({ 3605 progress 3606 }) => { 3607 return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { 3608 className: "action-checklist-progress-bar" 3609 }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("progress", { 3610 className: "sr-only", 3611 value: progress || 0, 3612 max: "100" 3613 }), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { 3614 className: "indicator", 3615 role: "presentation", 3616 style: { 3617 "--action-checklist-progress-bar-progress": `${progress || 0}%` 3618 } 3619 })); 3620 }; 3621 const ActionChecklist = ({ 3622 content, 3623 message_id 3624 }) => { 3625 const tiles = content.tiles.data; 3626 const [progressValue, setProgressValue] = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)(0); 3627 const [numberOfCompletedActions, setNumberOfCompletedActions] = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)(0); 3628 function determineProgressValue() { 3629 let newValue = numberOfCompletedActions / tiles.length * 100; 3630 setProgressValue(newValue); 3631 } 3632 3633 // This instance of useEffect is to evaluate the targeting of each individual action 3634 // when the component is initially loaded so that we can accurately populate the progress bar. 3635 // We're doing the heavy lifting here once on load, and keeping the rest of the information 3636 // regarding how many actions are complete handy in state for quick access, 3637 // and a lesser performance hit. 3638 (0,react__WEBPACK_IMPORTED_MODULE_0__.useEffect)(() => { 3639 let evaluateAllActionsTargeting = async () => { 3640 let completedActions = await Promise.all(tiles.map(async item => await evaluateTargeting(item.targeting))); 3641 let numCompletedActions = completedActions.filter(item => item).length; 3642 setNumberOfCompletedActions(numCompletedActions); 3643 }; 3644 evaluateAllActionsTargeting(); 3645 }, []); // eslint-disable-line react-hooks/exhaustive-deps 3646 3647 // This instance of useEffect is to initially update the progress bar, 3648 // and to also update the progress bar each time an action is completed. 3649 (0,react__WEBPACK_IMPORTED_MODULE_0__.useEffect)(() => { 3650 determineProgressValue(); 3651 }, [numberOfCompletedActions]); // eslint-disable-line react-hooks/exhaustive-deps 3652 3653 function handleAction(event) { 3654 let { 3655 action, 3656 source_id 3657 } = content.tiles.data[event.currentTarget.value]; 3658 let { 3659 type, 3660 data 3661 } = action; 3662 setNumberOfCompletedActions(numberOfCompletedActions + 1); 3663 _lib_aboutwelcome_utils_mjs__WEBPACK_IMPORTED_MODULE_2__.AboutWelcomeUtils.handleUserAction({ 3664 type, 3665 data 3666 }); 3667 _lib_aboutwelcome_utils_mjs__WEBPACK_IMPORTED_MODULE_2__.AboutWelcomeUtils.sendActionTelemetry(message_id, source_id); 3668 } 3669 return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { 3670 className: "action-checklist" 3671 }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("hr", { 3672 className: "action-checklist-divider" 3673 }), content.action_checklist_subtitle && /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_MSLocalized__WEBPACK_IMPORTED_MODULE_1__.Localized, { 3674 text: content.action_checklist_subtitle 3675 }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("p", { 3676 className: "action-checklist-subtitle" 3677 })), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(ActionChecklistProgressBar, { 3678 progress: progressValue 3679 }), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { 3680 className: "action-checklist-items" 3681 }, tiles.map((item, index) => /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(ActionChecklistItem, { 3682 key: item.id, 3683 index: index, 3684 item: item, 3685 handleAction: handleAction, 3686 showExternalLinkIcon: item.showExternalLinkIcon 3687 })))); 3688 }; 3689 3690 /***/ }), 3691 /* 25 */ 3692 /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { 3693 3694 __webpack_require__.r(__webpack_exports__); 3695 /* harmony export */ __webpack_require__.d(__webpack_exports__, { 3696 /* harmony export */ EmbeddedBrowser: () => (/* binding */ EmbeddedBrowser), 3697 /* harmony export */ "default": () => (__WEBPACK_DEFAULT_EXPORT__) 3698 /* harmony export */ }); 3699 /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(1); 3700 /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_0__); 3701 /* harmony import */ var _lib_aboutwelcome_utils_mjs__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(3); 3702 /* This Source Code Form is subject to the terms of the Mozilla Public 3703 * License, v. 2.0. If a copy of the MPL was not distributed with this 3704 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ 3705 3706 3707 3708 const BROWSER_STYLES = ["height", "width", "border", "borderRadius", "flex", "margin", "padding"]; 3709 const EmbeddedBrowser = props => { 3710 // Conditionally render the component only if the environment supports XULElements (such as in Spotlight modals) 3711 return document.createXULElement && props.url ? /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(EmbeddedBrowserInner, props) : null; 3712 }; 3713 const EmbeddedBrowserInner = ({ 3714 url, 3715 style 3716 }) => { 3717 const ref = (0,react__WEBPACK_IMPORTED_MODULE_0__.useRef)(null); 3718 const browserRef = (0,react__WEBPACK_IMPORTED_MODULE_0__.useRef)(null); 3719 (0,react__WEBPACK_IMPORTED_MODULE_0__.useEffect)(() => { 3720 if (!ref.current || browserRef.current) { 3721 return; 3722 } 3723 const browserEl = document.createXULElement("browser"); 3724 const remoteType = window.AWPredictRemoteType({ 3725 browserEl, 3726 url 3727 }); 3728 const attributes = [["disableglobalhistory", "true"], ["type", "content"], ["remote", "true"], ["maychangeremoteness", "true"], ["nodefaultsrc", "true"], ["remoteType", remoteType]]; 3729 attributes.forEach(([attr, val]) => browserEl.setAttribute(attr, val)); 3730 browserRef.current = browserEl; 3731 ref.current.appendChild(browserEl); 3732 // Initialize the browser element only once when the component mounts. The 3733 // empty dependency array ensures this effect runs only on the first render. 3734 }, []); // eslint-disable-line react-hooks/exhaustive-deps 3735 3736 (0,react__WEBPACK_IMPORTED_MODULE_0__.useEffect)(() => { 3737 if (browserRef.current) { 3738 browserRef.current.fixupAndLoadURIString(url, { 3739 triggeringPrincipal: Services.scriptSecurityManager.createNullPrincipal({}) 3740 }); 3741 } 3742 }, [url]); 3743 (0,react__WEBPACK_IMPORTED_MODULE_0__.useEffect)(() => { 3744 if (browserRef.current && style) { 3745 const validStyles = _lib_aboutwelcome_utils_mjs__WEBPACK_IMPORTED_MODULE_1__.AboutWelcomeUtils.getValidStyle(style, BROWSER_STYLES); 3746 Object.keys(validStyles).forEach(key => { 3747 browserRef.current.style.setProperty(key, style[key]); 3748 }); 3749 } 3750 }, [style]); 3751 return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { 3752 className: "embedded-browser-container", 3753 ref: ref 3754 }); 3755 }; 3756 /* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (EmbeddedBrowser); 3757 3758 /***/ }), 3759 /* 26 */ 3760 /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { 3761 3762 __webpack_require__.r(__webpack_exports__); 3763 /* harmony export */ __webpack_require__.d(__webpack_exports__, { 3764 /* harmony export */ ConfirmationChecklist: () => (/* binding */ ConfirmationChecklist) 3765 /* harmony export */ }); 3766 /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(1); 3767 /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_0__); 3768 /* harmony import */ var _lib_aboutwelcome_utils_mjs__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(3); 3769 /* harmony import */ var _MSLocalized__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(5); 3770 /* harmony import */ var _LinkParagraph__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(13); 3771 /* 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/. */ 3772 3773 3774 3775 3776 const ConfirmationChecklist = props => { 3777 const { 3778 content, 3779 handleAction 3780 } = props; 3781 if (!content) { 3782 return null; 3783 } 3784 const CONFIGURABLE_STYLES = ["background", "borderRadius", "display", "height", "marginBlock", "marginBlockStart", "marginBlockEnd", "marginInline", "paddingBlock", "paddingBlockStart", "paddingBlockEnd", "paddingInline", "paddingInlineStart", "paddingInlineEnd", "width"]; 3785 return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { 3786 className: `confirmation-checklist-section` 3787 }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { 3788 className: `confirmation-checklist-container`, 3789 style: _lib_aboutwelcome_utils_mjs__WEBPACK_IMPORTED_MODULE_1__.AboutWelcomeUtils.getValidStyle(content.style, CONFIGURABLE_STYLES) 3790 }, content.items.map(({ 3791 icon, 3792 text, 3793 subtext, 3794 link_keys 3795 }, index) => /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { 3796 key: index, 3797 className: "confirmation-checklist-item" 3798 }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { 3799 className: "confirmation-checklist-icon-wrapper" 3800 }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { 3801 className: "confirmation-checklist-icon", 3802 style: _lib_aboutwelcome_utils_mjs__WEBPACK_IMPORTED_MODULE_1__.AboutWelcomeUtils.getValidStyle(icon, CONFIGURABLE_STYLES) 3803 }), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { 3804 className: "confirmation-checklist-text" 3805 }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_MSLocalized__WEBPACK_IMPORTED_MODULE_2__.Localized, { 3806 text: text 3807 }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { 3808 className: "text body-text" 3809 })))), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { 3810 className: "confirmation-checklist-subtext" 3811 }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_LinkParagraph__WEBPACK_IMPORTED_MODULE_3__.LinkParagraph, { 3812 text_content: { 3813 text: subtext, 3814 link_keys 3815 }, 3816 handleAction: handleAction 3817 })))))); 3818 }; 3819 3820 /***/ }), 3821 /* 27 */ 3822 /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { 3823 3824 __webpack_require__.r(__webpack_exports__); 3825 /* harmony export */ __webpack_require__.d(__webpack_exports__, { 3826 /* harmony export */ EmbeddedBackupRestore: () => (/* binding */ EmbeddedBackupRestore) 3827 /* harmony export */ }); 3828 /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(1); 3829 /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_0__); 3830 /* harmony import */ var _lib_aboutwelcome_utils_mjs__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(3); 3831 /* harmony import */ var _MSLocalized__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(5); 3832 /* This Source Code Form is subject to the terms of the Mozilla Public 3833 * License, v. 2.0. If a copy of the MPL was not distributed with this file, 3834 * You can obtain one at http://mozilla.org/MPL/2.0/. */ 3835 3836 3837 3838 3839 const EmbeddedBackupRestore = ({ 3840 handleAction, 3841 skipButton 3842 }) => { 3843 const [recoveryInProgress, setRecoveryInProgress] = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)(false); 3844 const ref = (0,react__WEBPACK_IMPORTED_MODULE_0__.useRef)(null); 3845 (0,react__WEBPACK_IMPORTED_MODULE_0__.useEffect)(() => { 3846 const loadRestore = async () => { 3847 await window.AWFindBackupsInWellKnownLocations?.({ 3848 validateFile: true, 3849 multipleFiles: true 3850 }); 3851 }; 3852 loadRestore(); 3853 // Clear the pref used to target the restore screen so that users will not 3854 // automatically see it again the next time they visit about:welcome. 3855 _lib_aboutwelcome_utils_mjs__WEBPACK_IMPORTED_MODULE_1__.AboutWelcomeUtils.handleUserAction({ 3856 type: "SET_PREF", 3857 data: { 3858 pref: { 3859 name: "showRestoreFromBackup", 3860 value: false 3861 } 3862 } 3863 }); 3864 }, []); 3865 const onRecoveryProgressChange = (0,react__WEBPACK_IMPORTED_MODULE_0__.useCallback)(e => { 3866 setRecoveryInProgress(e.detail.recoveryInProgress); 3867 }, []); 3868 (0,react__WEBPACK_IMPORTED_MODULE_0__.useEffect)(() => { 3869 const backupRef = ref.current; 3870 if (backupRef.backupServiceState) { 3871 setRecoveryInProgress(backupRef.backupServiceState.recoveryInProgress); 3872 } 3873 backupRef.addEventListener("BackupUI:RecoveryProgress", onRecoveryProgressChange); 3874 return () => { 3875 backupRef.removeEventListener("BackupUI:RecoveryProgress", onRecoveryProgressChange); 3876 }; 3877 }, [onRecoveryProgressChange]); 3878 return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { 3879 className: "embedded-backup-restore-container" 3880 }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("restore-from-backup", { 3881 aboutWelcomeEmbedded: "true", 3882 labelFontWeight: "600", 3883 ref: ref 3884 }), skipButton ? /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { 3885 className: "action-buttons" 3886 }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { 3887 className: "secondary-cta" 3888 }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_MSLocalized__WEBPACK_IMPORTED_MODULE_2__.Localized, { 3889 text: skipButton.label 3890 }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("button", { 3891 id: "secondary_button", 3892 className: skipButton?.has_arrow_icon ? "secondary arrow-icon" : "secondary", 3893 value: "skip_button", 3894 disabled: recoveryInProgress, 3895 "aria-busy": recoveryInProgress || undefined, 3896 onClick: handleAction 3897 })))) : null); 3898 }; 3899 3900 /***/ }), 3901 /* 28 */ 3902 /***/ ((__unused_webpack___webpack_module__, __webpack_exports__, __webpack_require__) => { 3903 3904 __webpack_require__.r(__webpack_exports__); 3905 /* harmony export */ __webpack_require__.d(__webpack_exports__, { 3906 /* harmony export */ BASE_PARAMS: () => (/* binding */ BASE_PARAMS), 3907 /* harmony export */ addUtmParams: () => (/* binding */ addUtmParams) 3908 /* harmony export */ }); 3909 /* This Source Code Form is subject to the terms of the Mozilla Public 3910 * License, v. 2.0. If a copy of the MPL was not distributed with this file, 3911 * You can obtain one at http://mozilla.org/MPL/2.0/. */ 3912 3913 /** 3914 * BASE_PARAMS keys/values can be modified from outside this file 3915 */ 3916 const BASE_PARAMS = { 3917 utm_source: "activity-stream", 3918 utm_campaign: "firstrun", 3919 utm_medium: "referral", 3920 }; 3921 3922 /** 3923 * Takes in a url as a string or URL object and returns a URL object with the 3924 * utm_* parameters added to it. If a URL object is passed in, the paraemeters 3925 * are added to it (the return value can be ignored in that case as it's the 3926 * same object). 3927 */ 3928 function addUtmParams(url, utmTerm) { 3929 let returnUrl = url; 3930 if (typeof returnUrl === "string") { 3931 returnUrl = new URL(url); 3932 } 3933 for (let [key, value] of Object.entries(BASE_PARAMS)) { 3934 if (!returnUrl.searchParams.has(key)) { 3935 returnUrl.searchParams.append(key, value); 3936 } 3937 } 3938 if (!returnUrl.searchParams.has("utm_term")) { 3939 returnUrl.searchParams.append("utm_term", utmTerm); 3940 } 3941 return returnUrl; 3942 } 3943 3944 3945 /***/ }) 3946 /******/ ]); 3947 /************************************************************************/ 3948 /******/ // The module cache 3949 /******/ var __webpack_module_cache__ = {}; 3950 /******/ 3951 /******/ // The require function 3952 /******/ function __webpack_require__(moduleId) { 3953 /******/ // Check if module is in cache 3954 /******/ var cachedModule = __webpack_module_cache__[moduleId]; 3955 /******/ if (cachedModule !== undefined) { 3956 /******/ return cachedModule.exports; 3957 /******/ } 3958 /******/ // Create a new module (and put it into the cache) 3959 /******/ var module = __webpack_module_cache__[moduleId] = { 3960 /******/ // no module.id needed 3961 /******/ // no module.loaded needed 3962 /******/ exports: {} 3963 /******/ }; 3964 /******/ 3965 /******/ // Execute the module function 3966 /******/ __webpack_modules__[moduleId](module, module.exports, __webpack_require__); 3967 /******/ 3968 /******/ // Return the exports of the module 3969 /******/ return module.exports; 3970 /******/ } 3971 /******/ 3972 /************************************************************************/ 3973 /******/ /* webpack/runtime/compat get default export */ 3974 /******/ (() => { 3975 /******/ // getDefaultExport function for compatibility with non-harmony modules 3976 /******/ __webpack_require__.n = (module) => { 3977 /******/ var getter = module && module.__esModule ? 3978 /******/ () => (module['default']) : 3979 /******/ () => (module); 3980 /******/ __webpack_require__.d(getter, { a: getter }); 3981 /******/ return getter; 3982 /******/ }; 3983 /******/ })(); 3984 /******/ 3985 /******/ /* webpack/runtime/define property getters */ 3986 /******/ (() => { 3987 /******/ // define getter functions for harmony exports 3988 /******/ __webpack_require__.d = (exports, definition) => { 3989 /******/ for(var key in definition) { 3990 /******/ if(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) { 3991 /******/ Object.defineProperty(exports, key, { enumerable: true, get: definition[key] }); 3992 /******/ } 3993 /******/ } 3994 /******/ }; 3995 /******/ })(); 3996 /******/ 3997 /******/ /* webpack/runtime/hasOwnProperty shorthand */ 3998 /******/ (() => { 3999 /******/ __webpack_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop)) 4000 /******/ })(); 4001 /******/ 4002 /******/ /* webpack/runtime/make namespace object */ 4003 /******/ (() => { 4004 /******/ // define __esModule on exports 4005 /******/ __webpack_require__.r = (exports) => { 4006 /******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) { 4007 /******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); 4008 /******/ } 4009 /******/ Object.defineProperty(exports, '__esModule', { value: true }); 4010 /******/ }; 4011 /******/ })(); 4012 /******/ 4013 /************************************************************************/ 4014 var __webpack_exports__ = {}; 4015 // This entry needs to be wrapped in an IIFE because it needs to be isolated against other modules in the chunk. 4016 (() => { 4017 __webpack_require__.r(__webpack_exports__); 4018 /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(1); 4019 /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_0__); 4020 /* harmony import */ var react_dom__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(2); 4021 /* harmony import */ var react_dom__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(react_dom__WEBPACK_IMPORTED_MODULE_1__); 4022 /* harmony import */ var _lib_aboutwelcome_utils_mjs__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(3); 4023 /* harmony import */ var _components_MultiStageAboutWelcome__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(4); 4024 function _extends() { return _extends = Object.assign ? Object.assign.bind() : function (n) { for (var e = 1; e < arguments.length; e++) { var t = arguments[e]; for (var r in t) ({}).hasOwnProperty.call(t, r) && (n[r] = t[r]); } return n; }, _extends.apply(null, arguments); } 4025 /* This Source Code Form is subject to the terms of the Mozilla Public 4026 * License, v. 2.0. If a copy of the MPL was not distributed with this file, 4027 * You can obtain one at http://mozilla.org/MPL/2.0/. */ 4028 4029 4030 4031 4032 4033 class AboutWelcome extends (react__WEBPACK_IMPORTED_MODULE_0___default().PureComponent) { 4034 constructor(props) { 4035 super(props); 4036 this.state = { 4037 metricsFlowUri: null 4038 }; 4039 this.fetchFxAFlowUri = this.fetchFxAFlowUri.bind(this); 4040 } 4041 async fetchFxAFlowUri() { 4042 this.setState({ 4043 metricsFlowUri: await window.AWGetFxAMetricsFlowURI?.() 4044 }); 4045 } 4046 componentDidMount() { 4047 if (!this.props.skipFxA) { 4048 this.fetchFxAFlowUri(); 4049 } 4050 if (document.location.href === "about:welcome") { 4051 // Record impression with performance data after allowing the page to load 4052 const recordImpression = domState => { 4053 const { 4054 domComplete, 4055 domInteractive 4056 } = performance.getEntriesByType("navigation").pop(); 4057 _lib_aboutwelcome_utils_mjs__WEBPACK_IMPORTED_MODULE_2__.AboutWelcomeUtils.sendImpressionTelemetry(this.props.messageId, { 4058 domComplete, 4059 domInteractive, 4060 mountStart: performance.getEntriesByName("mount").pop().startTime, 4061 domState, 4062 source: this.props.UTMTerm 4063 }); 4064 }; 4065 if (document.readyState === "complete") { 4066 // Page might have already triggered a load event because it waited for async data, 4067 // e.g., attribution, so the dom load timing could be of a empty content 4068 // with domState in telemetry captured as 'complete' 4069 recordImpression(document.readyState); 4070 } else { 4071 window.addEventListener("load", () => recordImpression("load"), { 4072 once: true 4073 }); 4074 } 4075 4076 // Captures user has seen about:welcome by setting 4077 // firstrun.didSeeAboutWelcome pref to true and capturing welcome UI unique messageId 4078 window.AWSendToParent("SET_WELCOME_MESSAGE_SEEN", this.props.messageId); 4079 } 4080 } 4081 render() { 4082 const { 4083 props 4084 } = this; 4085 return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_components_MultiStageAboutWelcome__WEBPACK_IMPORTED_MODULE_3__.MultiStageAboutWelcome, { 4086 addonId: props.addonId, 4087 addonType: props.type, 4088 addonName: props.name || "", 4089 addonURL: props.url, 4090 addonIconURL: props.iconURL, 4091 themeScreenshots: props.screenshots, 4092 message_id: props.messageId, 4093 defaultScreens: props.screens, 4094 updateHistory: !props.disableHistoryUpdates, 4095 metricsFlowUri: this.state.metricsFlowUri, 4096 utm_term: props.UTMTerm, 4097 transitions: props.transitions, 4098 backdrop: props.backdrop, 4099 startScreen: props.startScreen || 0, 4100 appAndSystemLocaleInfo: props.appAndSystemLocaleInfo, 4101 ariaRole: props.aria_role, 4102 gateInitialPaint: true 4103 }); 4104 } 4105 } 4106 4107 // Computes messageId and UTMTerm info used in telemetry 4108 function ComputeTelemetryInfo(welcomeContent, experimentId, branchId) { 4109 let messageId = welcomeContent.template === "return_to_amo" ? `RTAMO_DEFAULT_WELCOME_${welcomeContent.type.toUpperCase()}` : "DEFAULT_ID"; 4110 let UTMTerm = "aboutwelcome-default"; 4111 if (welcomeContent.id) { 4112 messageId = welcomeContent.id.toUpperCase(); 4113 } 4114 if (experimentId && branchId) { 4115 UTMTerm = `aboutwelcome-${experimentId}-${branchId}`.toLowerCase(); 4116 } 4117 return { 4118 messageId, 4119 UTMTerm 4120 }; 4121 } 4122 async function retrieveRenderContent() { 4123 // Feature config includes RTAMO attribution data if exists 4124 // else below data in order specified 4125 // user prefs 4126 // experiment data 4127 // defaults 4128 let featureConfig = await window.AWGetFeatureConfig(); 4129 let { 4130 messageId, 4131 UTMTerm 4132 } = ComputeTelemetryInfo(featureConfig, featureConfig.slug, featureConfig.branch && featureConfig.branch.slug); 4133 return { 4134 featureConfig, 4135 messageId, 4136 UTMTerm 4137 }; 4138 } 4139 async function mount() { 4140 let { 4141 featureConfig: aboutWelcomeProps, 4142 messageId, 4143 UTMTerm 4144 } = await retrieveRenderContent(); 4145 react_dom__WEBPACK_IMPORTED_MODULE_1___default().render(/*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(AboutWelcome, _extends({ 4146 messageId: messageId, 4147 UTMTerm: UTMTerm 4148 }, aboutWelcomeProps)), document.getElementById("multi-stage-message-root")); 4149 } 4150 performance.mark("mount"); 4151 mount(); 4152 })(); 4153 4154 /******/ })() 4155 ;