import React, { useContext, useMemo, useRef, useState, useEffect } from 'react'; import { useHistory } from 'react-router-dom'; import { useSelector } from 'react-redux'; import PropTypes from 'prop-types'; import classnames from 'classnames'; import { getCurrentLocale } from '../../../ducks/metamask/metamask'; import { I18nContext } from '../../../contexts/i18n'; import { useEqualityCheck } from '../../../hooks/useEqualityCheck'; import Button from '../../ui/button'; import Popover from '../../ui/popover'; import Typography from '../../ui/typography'; import { updateViewedNotifications } from '../../../store/actions'; import { getTranslatedUINotifications } from '../../../../shared/notifications'; import { getSortedAnnouncementsToShow } from '../../../selectors'; import { BUILD_QUOTE_ROUTE, ADVANCED_ROUTE, EXPERIMENTAL_ROUTE, SECURITY_ROUTE, } from '../../../helpers/constants/routes'; import { TYPOGRAPHY } from '../../../helpers/constants/design-system'; import ZENDESK_URLS from '../../../helpers/constants/zendesk-url'; function getActionFunctionById(id, history) { const actionFunctions = { 2: () => { global.platform.openTab({ url: 'https://survey.alchemer.com/s3/6173069/MetaMask-Extension-NPS-January-2021', }); }, 3: () => { global.platform.openTab({ url: 'https://community.metamask.io/t/about-the-security-category/72', }); }, 4: () => { updateViewedNotifications({ 4: true }); history.push(BUILD_QUOTE_ROUTE); }, 5: () => { updateViewedNotifications({ 5: true }); global.platform.openTab({ url: ZENDESK_URLS.SECRET_RECOVERY_PHRASE, }); }, 8: () => { updateViewedNotifications({ 8: true }); history.push(ADVANCED_ROUTE); }, 10: () => { updateViewedNotifications({ 10: true }); history.push(`${SECURITY_ROUTE}#token-description`); }, 12: () => { updateViewedNotifications({ 12: true }); history.push(EXPERIMENTAL_ROUTE); }, 14: () => { updateViewedNotifications({ 14: true }); history.push(`${ADVANCED_ROUTE}#backup-userdata`); }, 16: () => { updateViewedNotifications({ 16: true }); history.push(EXPERIMENTAL_ROUTE); }, 17: () => { updateViewedNotifications({ 17: true }); history.push(SECURITY_ROUTE); }, }; return actionFunctions[id]; } const renderDescription = (description) => { if (!Array.isArray(description)) { return ( {description} ); } return ( <> {description.map((piece, index) => { const isLast = index === description.length - 1; return ( {piece} ); })} ); }; const renderFirstNotification = (notification, idRefMap, history, isLast) => { const { id, date, title, description, image, actionText } = notification; const actionFunction = getActionFunctionById(id, history); const imageComponent = image && ( ); const placeImageBelowDescription = image?.placeImageBelowDescription; return (
{!placeImageBelowDescription && imageComponent}
{title}
{renderDescription(description)}
{date}
{placeImageBelowDescription && imageComponent} {actionText && ( )}
); }; const renderSubsequentNotification = ( notification, idRefMap, history, isLast, ) => { const { id, date, title, description, actionText } = notification; const actionFunction = getActionFunctionById(id, history); return (
{title}
{renderDescription(description)}
{date}
{actionText && (
{`${actionText} >`}
)}
); }; export default function WhatsNewPopup({ onClose }) { const t = useContext(I18nContext); const history = useHistory(); const notifications = useSelector(getSortedAnnouncementsToShow); const locale = useSelector(getCurrentLocale); const [seenNotifications, setSeenNotifications] = useState({}); const popoverRef = useRef(); const memoizedNotifications = useEqualityCheck(notifications); const idRefMap = useMemo( () => memoizedNotifications.reduce( (_idRefMap, notification) => ({ ..._idRefMap, [notification.id]: React.createRef(), }), {}, ), [memoizedNotifications], ); useEffect(() => { const observer = new window.IntersectionObserver( (entries, _observer) => { entries.forEach((entry) => { if (entry.isIntersecting) { const [id, ref] = Object.entries(idRefMap).find(([_, _ref]) => _ref.current.isSameNode(entry.target), ); setSeenNotifications((_seenNotifications) => ({ ..._seenNotifications, [id]: true, })); _observer.unobserve(ref.current); } }); }, { root: popoverRef.current, threshold: 1.0, }, ); Object.values(idRefMap).forEach((ref) => { observer.observe(ref.current); }); return () => { observer.disconnect(); }; }, [idRefMap, setSeenNotifications]); return ( { updateViewedNotifications(seenNotifications); onClose(); }} popoverRef={popoverRef} >
{notifications.map(({ id }, index) => { const notification = getTranslatedUINotifications(t, locale)[id]; const isLast = index === notifications.length - 1; // Display the swaps notification with full image return index === 0 || id === 1 ? renderFirstNotification(notification, idRefMap, history, isLast) : renderSubsequentNotification( notification, idRefMap, history, isLast, ); })}
); } WhatsNewPopup.propTypes = { onClose: PropTypes.func.isRequired, };