tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

MessageWrapper.jsx (3878B)


      1 /* This Source Code Form is subject to the terms of the Mozilla Public
      2 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
      3 * You can obtain one at http://mozilla.org/MPL/2.0/. */
      4 
      5 import React, { useCallback, useEffect, useState } from "react";
      6 import { actionCreators as ac, actionTypes as at } from "common/Actions.mjs";
      7 import { useSelector } from "react-redux";
      8 import { useIntersectionObserver } from "../../lib/utils";
      9 
     10 // Note: MessageWrapper emits events via submitGleanPingForPing() in the OMC messaging-system.
     11 // If a feature is triggered outside of this flow (e.g., the Mobile Download QR Promo),
     12 // it should emit New Tab-specific Glean events independently.
     13 
     14 function MessageWrapper({ children, dispatch, hiddenOverride, onDismiss }) {
     15  const message = useSelector(state => state.Messages);
     16  const [isIntersecting, setIsIntersecting] = useState(false);
     17  const [tabIsVisible, setTabIsVisible] = useState(
     18    () =>
     19      typeof document !== "undefined" && document.visibilityState === "visible"
     20  );
     21  const [hasRun, setHasRun] = useState();
     22 
     23  const handleIntersection = useCallback(() => {
     24    setIsIntersecting(true);
     25    // only send impression if messageId is defined and tab is visible
     26    if (tabIsVisible && message.messageData.id && !hasRun) {
     27      setHasRun(true);
     28      dispatch(
     29        ac.AlsoToMain({
     30          type: at.MESSAGE_IMPRESSION,
     31          data: message.messageData,
     32        })
     33      );
     34    }
     35  }, [dispatch, message, tabIsVisible, hasRun]);
     36 
     37  useEffect(() => {
     38    // we dont want to dispatch this action unless the current tab is open and visible
     39    if (message.isVisible && tabIsVisible) {
     40      dispatch(
     41        ac.AlsoToMain({
     42          type: at.MESSAGE_NOTIFY_VISIBILITY,
     43          data: true,
     44        })
     45      );
     46    }
     47  }, [message, dispatch, tabIsVisible]);
     48 
     49  useEffect(() => {
     50    const handleVisibilityChange = () => {
     51      setTabIsVisible(document.visibilityState === "visible");
     52    };
     53 
     54    document.addEventListener("visibilitychange", handleVisibilityChange);
     55    return () => {
     56      document.removeEventListener("visibilitychange", handleVisibilityChange);
     57    };
     58  }, []);
     59 
     60  const ref = useIntersectionObserver(handleIntersection);
     61 
     62  const handleClose = useCallback(() => {
     63    const action = {
     64      type: at.MESSAGE_TOGGLE_VISIBILITY,
     65      data: false, //isVisible
     66    };
     67    if (message.portID) {
     68      dispatch(ac.OnlyToOneContent(action, message.portID));
     69    } else {
     70      dispatch(ac.AlsoToMain(action));
     71    }
     72    dispatch(
     73      ac.AlsoToMain({
     74        type: at.MESSAGE_NOTIFY_VISIBILITY,
     75        data: false,
     76      })
     77    );
     78    onDismiss?.();
     79  }, [dispatch, message, onDismiss]);
     80 
     81  function handleDismiss() {
     82    const { id } = message.messageData;
     83    if (id) {
     84      dispatch(
     85        ac.OnlyToMain({
     86          type: at.MESSAGE_DISMISS,
     87          data: { message: message.messageData },
     88        })
     89      );
     90    }
     91    handleClose();
     92  }
     93 
     94  function handleBlock() {
     95    const { id } = message.messageData;
     96    if (id) {
     97      dispatch(
     98        ac.OnlyToMain({
     99          type: at.MESSAGE_BLOCK,
    100          data: id,
    101        })
    102      );
    103    }
    104  }
    105 
    106  function handleClick(elementId) {
    107    const { id } = message.messageData;
    108    if (id) {
    109      dispatch(
    110        ac.OnlyToMain({
    111          type: at.MESSAGE_CLICK,
    112          data: { message: message.messageData, source: elementId || "" },
    113        })
    114      );
    115    }
    116  }
    117 
    118  if (!message || (!hiddenOverride && !message.isVisible)) {
    119    return null;
    120  }
    121 
    122  // only display the message if `isVisible` is true
    123  return (
    124    <div
    125      ref={el => {
    126        ref.current = [el];
    127      }}
    128      className="message-wrapper"
    129    >
    130      {React.cloneElement(children, {
    131        isIntersecting,
    132        handleDismiss,
    133        handleClick,
    134        handleBlock,
    135        handleClose,
    136      })}
    137    </div>
    138  );
    139 }
    140 
    141 export { MessageWrapper };