import { TYPES } from 'Types/session/event'; import cn from 'classnames'; import copy from 'copy-to-clipboard'; import { Angry, MessageCircleQuestion, MousePointerClick, Navigation, Pointer, TextCursorInput, } from 'lucide-react'; import React, { useRef, useState } from 'react'; import { prorata, numberWithCommas } from 'App/utils'; import withOverlay from 'Components/hocs/withOverlay'; import { Icon, TextEllipsis, Tooltip } from 'UI'; import LoadInfo from './LoadInfo'; import cls from './event.module.css'; import { useTranslation } from 'react-i18next'; type Props = { event: any; selected?: boolean; isCurrent?: boolean; onClick?: () => void; showSelection?: boolean; toggleLoadInfo?: () => void; isRed?: boolean; presentInSearch?: boolean; whiteBg?: boolean; }; const isFrustrationEvent = (evt: any): boolean => { if ( evt.type === 'mouse_thrashing' || evt.type === TYPES.CLICKRAGE || evt.type === TYPES.TAPRAGE ) { return true; } if (evt.type === TYPES.CLICK || evt.type === TYPES.INPUT) { return evt.hesitation > 1000; } return false; }; const Event: React.FC = ({ event, selected = false, isCurrent = false, onClick, showSelection = false, toggleLoadInfo, isRed = false, presentInSearch = false, whiteBg, }) => { const { t } = useTranslation(); const wrapperRef = useRef(null); const [menuOpen, setMenuOpen] = useState(false); const isLocation = event.type === TYPES.LOCATION; const onContextMenu = (e: React.MouseEvent) => { e.preventDefault(); setMenuOpen(true); }; const onMouseLeave = () => setMenuOpen(false); const copyHandler = (e: React.MouseEvent) => { e.stopPropagation(); const path = event.getIn(['target', 'path']) || event.url || ''; copy(path); setMenuOpen(false); }; const renderBody = () => { let title = event.type; let body; let icon = null; let iconName = null; const isFrustration = isFrustrationEvent(event); const tooltip = { disabled: true, text: '' }; switch (event.type) { case TYPES.LOCATION: title = t('Visited'); body = event.url; icon = ; break; case TYPES.SWIPE: title = t('Swipe'); body = event.direction; iconName = `chevron-${event.direction}`; break; case TYPES.TOUCH: title = t('Tapped'); body = event.label; iconName = 'event/click'; break; case TYPES.CLICK: title = t('Clicked'); body = event.label; icon = isFrustration ? ( ) : ( ); isFrustration ? Object.assign(tooltip, { disabled: false, text: `${t('User hesitated')} ${Math.round( event.hesitation / 1000, )}${t('s to perform this event')}`, }) : null; break; case TYPES.INPUT: title = 'Input'; body = event.value; icon = isFrustration ? ( ) : ( ); isFrustration ? Object.assign(tooltip, { disabled: false, text: `${t('User hesitated')} ${Math.round( event.hesitation / 1000, )}${t('s to enter a value in this input field.')}`, }) : null; break; case TYPES.CLICKRAGE: case TYPES.TAPRAGE: title = event.count ? `${event.count} ${t('Clicks')}` : t('Click Rage'); body = event.label; icon = ; break; case TYPES.IOS_VIEW: title = t('View'); body = event.name; iconName = 'event/ios_view'; break; case 'mouse_thrashing': title = t('Mouse Thrashing'); icon = ; break; } return (
{event.type && iconName ? ( ) : ( icon )}
{title} {body && !isLocation && ( )}
{isLocation && event.speedIndex != null && (
${t('Speed Index')}
{numberWithCommas(event.speedIndex || 0)}
)}
{event.target && event.target.label && (
{event.target.label}
)}
{isLocation && (
)}
); }; const isFrustration = isFrustrationEvent(event); const mobileTypes = [TYPES.TOUCH, TYPES.SWIPE, TYPES.TAPRAGE]; return (
{menuOpen && ( )}
{renderBody()}
{isLocation && (event.fcpTime || event.visuallyComplete || event.timeToInteractive || event.webvitals) ? ( elements / 1.2, divisorFn: (elements, parts) => elements / (2 * parts + 1), })} /> ) : null}
); }; export default withOverlay()(Event);