/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this file, * You can obtain one at http://mozilla.org/MPL/2.0/. */ import React, { useCallback, useEffect, useState } from "react"; import { actionCreators as ac, actionTypes as at } from "common/Actions.mjs"; import { useSelector } from "react-redux"; import { useIntersectionObserver } from "../../lib/utils"; // Note: MessageWrapper emits events via submitGleanPingForPing() in the OMC messaging-system. // If a feature is triggered outside of this flow (e.g., the Mobile Download QR Promo), // it should emit New Tab-specific Glean events independently. function MessageWrapper({ children, dispatch, hiddenOverride, onDismiss }) { const message = useSelector(state => state.Messages); const [isIntersecting, setIsIntersecting] = useState(false); const [tabIsVisible, setTabIsVisible] = useState( () => typeof document !== "undefined" && document.visibilityState === "visible" ); const [hasRun, setHasRun] = useState(); const handleIntersection = useCallback(() => { setIsIntersecting(true); // only send impression if messageId is defined and tab is visible if (tabIsVisible && message.messageData.id && !hasRun) { setHasRun(true); dispatch( ac.AlsoToMain({ type: at.MESSAGE_IMPRESSION, data: message.messageData, }) ); } }, [dispatch, message, tabIsVisible, hasRun]); useEffect(() => { // we dont want to dispatch this action unless the current tab is open and visible if (message.isVisible && tabIsVisible) { dispatch( ac.AlsoToMain({ type: at.MESSAGE_NOTIFY_VISIBILITY, data: true, }) ); } }, [message, dispatch, tabIsVisible]); useEffect(() => { const handleVisibilityChange = () => { setTabIsVisible(document.visibilityState === "visible"); }; document.addEventListener("visibilitychange", handleVisibilityChange); return () => { document.removeEventListener("visibilitychange", handleVisibilityChange); }; }, []); const ref = useIntersectionObserver(handleIntersection); const handleClose = useCallback(() => { const action = { type: at.MESSAGE_TOGGLE_VISIBILITY, data: false, //isVisible }; if (message.portID) { dispatch(ac.OnlyToOneContent(action, message.portID)); } else { dispatch(ac.AlsoToMain(action)); } dispatch( ac.AlsoToMain({ type: at.MESSAGE_NOTIFY_VISIBILITY, data: false, }) ); onDismiss?.(); }, [dispatch, message, onDismiss]); function handleDismiss() { const { id } = message.messageData; if (id) { dispatch( ac.OnlyToMain({ type: at.MESSAGE_DISMISS, data: { message: message.messageData }, }) ); } handleClose(); } function handleBlock() { const { id } = message.messageData; if (id) { dispatch( ac.OnlyToMain({ type: at.MESSAGE_BLOCK, data: id, }) ); } } function handleClick(elementId) { const { id } = message.messageData; if (id) { dispatch( ac.OnlyToMain({ type: at.MESSAGE_CLICK, data: { message: message.messageData, source: elementId || "" }, }) ); } } if (!message || (!hiddenOverride && !message.isVisible)) { return null; } // only display the message if `isVisible` is true return (