import cn from 'classnames'; import { Duration } from 'luxon'; import { observer } from 'mobx-react-lite'; import React, { useState, useCallback, useMemo } from 'react'; import { RouteComponentProps, useHistory, withRouter } from 'react-router-dom'; import { durationFormatted, formatTimeOrDate } from 'App/date'; import { useStore } from 'App/mstore'; import { assist as assistRoute, isRoute, liveSession, sessions as sessionsRoute, } from 'App/routes'; import { capitalize } from 'App/utils'; import { Avatar, CountryFlag, Icon, Label, TextEllipsis } from 'UI'; import Counter from './Counter'; import ErrorBars from './ErrorBars'; import PlayLink from './PlayLink'; import SessionMetaList from './SessionMetaList'; import stl from './sessionItem.module.css'; import { useTranslation } from 'react-i18next'; import { Tooltip } from 'antd'; const ASSIST_ROUTE = assistRoute(); const ASSIST_LIVE_SESSION = liveSession(); const SESSIONS_ROUTE = sessionsRoute(); interface Props { session: { sessionId: string; userBrowser: string; userOs: string; userId: string; userAnonymousId: string; userDisplayName: string; userCountry: string; userCity: string; userState: string; startedAt: number; duration: Duration; eventsCount: number; errorsCount: number; pagesCount: number; viewed: boolean; favorite: boolean; userDeviceType: string; userUuid: string; userNumericHash: number; live: boolean; metadata: Record; issueTypes: []; active: boolean; isCallActive?: boolean; agentIds?: string[]; timezone: string; platform: string; }; onUserClick?: (userId: string, userAnonymousId: string) => void; hasUserFilter?: boolean; disableUser?: boolean; metaList?: Array; lastPlayedSessionId?: string; live?: boolean; onClick?: any; compact?: boolean; isDisabled?: boolean; isAdd?: boolean; ignoreAssist?: boolean; bookmarked?: boolean; toggleFavorite?: (sessionId: string) => void; query?: string; slim?: boolean; } const PREFETCH_STATE = { none: 0, loading: 1, fetched: 2, }; function SessionItem(props: RouteComponentProps & Props) { const { location } = useHistory(); const { settingsStore, sessionStore, searchStore, searchStoreLive } = useStore(); const { timezone, shownTimezone } = settingsStore.sessionSettings; const { t } = useTranslation(); const [prefetchState, setPrefetched] = useState(PREFETCH_STATE.none); const { session, onUserClick = () => null, hasUserFilter = false, disableUser = false, lastPlayedSessionId, onClick = null, compact = false, ignoreAssist = false, query, isDisabled, live: propsLive, isAdd, slim, } = props; const { sessionId, userBrowser, userOs, userId, userAnonymousId, userDisplayName, userCountry, userCity, userState, startedAt, duration, eventsCount, viewed, userDeviceType, userNumericHash, live: sessionLive, metadata, issueTypes, active, platform, timezone: userTimezone, isCallActive, agentIds, } = session; const queryParams = useMemo( () => Object.fromEntries(new URLSearchParams(location.search)), [location.search], ); const isMobile = platform !== 'web'; const formattedDuration = useMemo( () => durationFormatted(duration), [duration], ); const hasUserId = userId || userAnonymousId; const isSessions = useMemo( () => isRoute(SESSIONS_ROUTE, location.pathname), [location.pathname], ); const isAssist = useMemo(() => { return ( (!ignoreAssist && (isRoute(ASSIST_ROUTE, location.pathname) || isRoute(ASSIST_LIVE_SESSION, location.pathname) || location.pathname.includes('multiview'))) || propsLive ); }, [ignoreAssist, location.pathname, propsLive]); const isLastPlayed = lastPlayedSessionId === sessionId; const live = sessionLive || propsLive; const isMultiviewDisabled = isDisabled && location.pathname.includes('multiview'); const _metaList = useMemo(() => { if (!metadata) return []; return Object.keys(metadata).map((key) => ({ label: key, value: metadata[key], })); }, [metadata]); const handleHover = useCallback(async () => { if (prefetchState !== PREFETCH_STATE.none || live || isAssist || isMobile) return; setPrefetched(PREFETCH_STATE.loading); try { await sessionStore.getFirstMob(sessionId); setPrefetched(PREFETCH_STATE.fetched); } catch (e) { setPrefetched(PREFETCH_STATE.none); console.error('Error while prefetching first mob', e); } }, [prefetchState, live, isAssist, isMobile, sessionStore, sessionId]); const populateData = useCallback(() => { if (live || isAssist || prefetchState === PREFETCH_STATE.none || isMobile) { return; } sessionStore.prefetchSession(session); }, [live, isAssist, prefetchState, isMobile, sessionStore, session]); const handleUserClick = useCallback(() => { if (!disableUser && !hasUserFilter && hasUserId) { onUserClick(userId, userAnonymousId); } }, [ disableUser, hasUserFilter, hasUserId, onUserClick, userId, userAnonymousId, ]); const handleAddClick = useCallback(() => { if (!isDisabled && onClick) { onClick(); } }, [isDisabled, onClick]); // Memoize time formatting const formattedTime = useMemo(() => { const timezoneToUse = shownTimezone === 'user' && userTimezone ? { label: userTimezone.split('+').join(' +'), value: userTimezone.split(':')[0], } : timezone; return formatTimeOrDate(startedAt, timezoneToUse); }, [startedAt, shownTimezone, userTimezone, timezone]); const timeTooltipContent = useMemo(() => { return (
Local Time: {formatTimeOrDate(startedAt, timezone, true)}{' '} {timezone.label} {userTimezone ? ( User's Time:{' '} {formatTimeOrDate( startedAt, { label: userTimezone.split('+').join(' +'), value: userTimezone.split(':')[0], }, true, )}{' '} {userTimezone} ) : null}
); }, [startedAt, timezone, userTimezone]); const onMetaClick = (meta: { name: string; value: string }) => { if (isAssist) { searchStoreLive.addFilterByKeyAndValue(meta.name, meta.value); } else { searchStore.addFilterByKeyAndValue(meta.name, meta.value); } }; return (
e.stopPropagation()} onMouseEnter={handleHover} >
{!compact && (
{!slim && _metaList.length > 0 && ( )}
)}
{!isAssist && ( <>
{eventsCount} {eventsCount === 0 || eventsCount > 1 ? 'Events' : 'Event'}
)}
{live ? : formattedDuration}
{userBrowser && ( )} {userOs && userBrowser && ( )} {userOs && ( )} {userOs && ( )}
{isSessions && (
)}
{live && isCallActive && agentIds && agentIds.length > 0 && (
)} {isSessions && isLastPlayed && ( )} {isAdd ? (
) : ( )}
); } export default withRouter(observer(SessionItem));