import { STORAGE_TYPES, StorageType, selectStorageType } from 'Player'; import { Switch } from 'antd'; import cn from 'classnames'; import { observer } from 'mobx-react-lite'; import React from 'react'; import { useHistory } from 'react-router-dom'; import { PlayerContext } from 'App/components/Session/playerContext'; import { useStore } from 'App/mstore'; import { FullScreenButton, PlayButton, PlayingState } from 'App/player-ui'; import { session as sessionRoute, withSiteId } from 'App/routes'; import DropdownAudioPlayer from 'Components/Session/Player/ReplayPlayer/AudioPlayer'; import useShortcuts from 'Components/Session/Player/ReplayPlayer/useShortcuts'; import { LaunchConsoleShortcut, LaunchEventsShortcut, LaunchNetworkShortcut, LaunchPerformanceShortcut, LaunchStateShortcut, LaunchXRaShortcut, } from 'Components/Session_/Player/Controls/components/KeyboardHelp'; import { CONSOLE, GRAPHQL, INSPECTOR, NETWORK, OVERVIEW, PERFORMANCE, PROFILER, STACKEVENTS, STORAGE, BACKENDLOGS, } from 'App/mstore/uiPlayerStore'; import { Icon } from 'UI'; import LogsButton from 'App/components/Session/Player/SharedComponents/BackendLogs/LogsButton'; import ControlButton from './ControlButton'; import { WebEventsList } from "./EventsList"; import Timeline from './Timeline'; import PlayerControls from './components/PlayerControls'; import styles from './controls.module.css'; export const SKIP_INTERVALS = { 2: 2e3, 5: 5e3, 10: 1e4, 15: 15e3, 20: 2e4, 30: 3e4, 60: 6e4, }; function getStorageName(type: any) { switch (type) { case STORAGE_TYPES.REDUX: return 'Redux'; case STORAGE_TYPES.MOBX: return 'Mobx'; case STORAGE_TYPES.VUEX: return 'Vuex'; case STORAGE_TYPES.NGRX: return 'NgRx'; case STORAGE_TYPES.ZUSTAND: return 'Zustand'; case STORAGE_TYPES.NONE: return 'State'; default: return 'State'; } } function Controls({ setActiveTab }: any) { const { player, store } = React.useContext(PlayerContext); const { uxtestingStore, uiPlayerStore, projectsStore, sessionStore, userStore, } = useStore(); const permissions = userStore.account.permissions || []; const disableDevtools = userStore.isEnterprise && !( permissions.includes('DEV_TOOLS') || permissions.includes('SERVICE_DEV_TOOLS') ); const fullscreen = uiPlayerStore.fullscreen; const bottomBlock = uiPlayerStore.bottomBlock; const toggleBottomBlock = uiPlayerStore.toggleBottomBlock; const fullscreenOn = uiPlayerStore.fullscreenOn; const fullscreenOff = uiPlayerStore.fullscreenOff; const changeSkipInterval = uiPlayerStore.changeSkipInterval; const skipInterval = uiPlayerStore.skipInterval; const showStorageRedux = !uiPlayerStore.hiddenHints.storage; const history = useHistory(); const siteId = projectsStore.siteId; const { playing, completed, skip, speed, messagesLoading, markedTargets, inspectorMode, } = store.get(); const session = sessionStore.current; const previousSessionId = sessionStore.previousId; const nextSessionId = sessionStore.nextId; const disabled = disableDevtools || messagesLoading || inspectorMode || markedTargets; const sessionTz = session?.timezone; const nextHandler = () => { history.push(withSiteId(sessionRoute(nextSessionId), siteId)); }; const prevHandler = () => { history.push(withSiteId(sessionRoute(previousSessionId), siteId)); }; useShortcuts({ skipInterval, fullScreenOn: fullscreenOn, fullScreenOff: fullscreenOff, toggleBottomBlock, openNextSession: nextHandler, openPrevSession: prevHandler, setActiveTab, disableDevtools, }); const forthTenSeconds = () => { // @ts-ignore player.jumpInterval(SKIP_INTERVALS[skipInterval]); }; const backTenSeconds = () => { // @ts-ignore player.jumpInterval(-SKIP_INTERVALS[skipInterval]); }; const toggleBottomTools = (blockName: number) => { player.toggleInspectorMode(false); toggleBottomBlock(blockName); }; const state = completed ? PlayingState.Completed : playing ? PlayingState.Playing : PlayingState.Paused; const events = session.stackEvents ?? []; return (
{!fullscreen && (
player.toggleSpeed(speedIndex)} toggleSkip={() => player.toggleSkip()} playButton={ } skipIntervals={SKIP_INTERVALS} setSkipInterval={changeSkipInterval} currentInterval={skipInterval} startedAt={session.startedAt} />
{uxtestingStore.hideDevtools && uxtestingStore.isUxt() ? null : ( )}
)}
); } interface IDevtoolsButtons { showStorageRedux: boolean; toggleBottomTools: (blockName: number) => void; bottomBlock: number; disabled: boolean; events: any[]; } const DevtoolsButtons = observer( ({ showStorageRedux, toggleBottomTools, bottomBlock, disabled, events, }: IDevtoolsButtons) => { const { aiSummaryStore, integrationsStore } = useStore(); const { store, player } = React.useContext(PlayerContext); // @ts-ignore const originStr = window.env.ORIGIN || window.location.origin; const isSaas = /app\.openreplay\.com/.test(originStr); const { inspectorMode, currentTab, tabStates } = store.get(); const disableButtons = disabled; const profilesList = tabStates[currentTab]?.profilesList || []; const graphqlList = tabStates[currentTab]?.graphqlList || []; const logRedCount = tabStates[currentTab]?.logMarkedCountNow || 0; const resourceRedCount = tabStates[currentTab]?.resourceMarkedCountNow || 0; const stackRedCount = tabStates[currentTab]?.stackMarkedCountNow || 0; const exceptionsList = tabStates[currentTab]?.exceptionsList || []; const storageType = store.get().tabStates[currentTab] ? selectStorageType(store.get().tabStates[currentTab]) : StorageType.NONE; const profilesCount = profilesList.length; const graphqlCount = graphqlList.length; const showGraphql = graphqlCount > 0; const showProfiler = profilesCount > 0; const showExceptions = exceptionsList.length > 0; const showStorage = storageType !== STORAGE_TYPES.NONE || showStorageRedux; const showSummary = () => { player.pause(); if (bottomBlock !== OVERVIEW) { toggleBottomTools(OVERVIEW); } aiSummaryStore.setToggleSummary(!aiSummaryStore.toggleSummary); }; const possibleAudio = events.filter((e) => e.name.includes('media/audio')); const integratedServices = integrationsStore.integrations.backendLogIntegrations; return ( <> {isSaas ? : null}
Get a quick overview on the issues in this session.
} label={'X-Ray'} onClick={() => toggleBottomTools(OVERVIEW)} active={bottomBlock === OVERVIEW && !inspectorMode} />
Launch Console
} disabled={disableButtons} onClick={() => toggleBottomTools(CONSOLE)} active={bottomBlock === CONSOLE && !inspectorMode} label="Console" hasErrors={logRedCount > 0 || showExceptions} />
Launch Network
} disabled={disableButtons} onClick={() => toggleBottomTools(NETWORK)} active={bottomBlock === NETWORK && !inspectorMode} label="Network" hasErrors={resourceRedCount > 0} />
Launch Performance
} disabled={disableButtons} onClick={() => toggleBottomTools(PERFORMANCE)} active={bottomBlock === PERFORMANCE && !inspectorMode} label="Performance" /> {showGraphql && ( toggleBottomTools(GRAPHQL)} active={bottomBlock === GRAPHQL && !inspectorMode} label="Graphql" /> )} {showStorage && (
Launch State
} disabled={disableButtons} onClick={() => toggleBottomTools(STORAGE)} active={bottomBlock === STORAGE && !inspectorMode} label={getStorageName(storageType) as string} /> )}
Launch Events
} disabled={disableButtons} onClick={() => toggleBottomTools(STACKEVENTS)} active={bottomBlock === STACKEVENTS && !inspectorMode} label="Events" hasErrors={stackRedCount > 0} /> {showProfiler && ( toggleBottomTools(PROFILER)} active={bottomBlock === PROFILER && !inspectorMode} label="Profiler" /> )} {integratedServices.length ? ( service.name)} onClick={() => toggleBottomTools(BACKENDLOGS)} /> ) : null} {possibleAudio.length ? ( ) : null} ); } ); export function SummaryButton({ onClick, withToggle, onToggle, toggleValue, }: { onClick?: () => void; withToggle?: boolean; onToggle?: () => void; toggleValue?: boolean; }) { const [isHovered, setHovered] = React.useState(false); return (
setHovered(true)} onMouseLeave={() => setHovered(false)} > {withToggle ? ( ) : null}
Summary AI
); } export const gradientButton = { border: 'double 1px transparent', borderRadius: '60px', background: 'linear-gradient(#f6f6f6, #f6f6f6), linear-gradient(to right, #394EFF 0%, #3EAAAF 100%)', backgroundOrigin: 'border-box', backgroundClip: 'content-box, border-box', cursor: 'pointer', height: 24, display: 'flex', alignItems: 'center', justifyContent: 'center', }; const onHoverFillStyle = { width: '100%', height: '100%', display: 'flex', borderRadius: '60px', gap: 2, alignItems: 'center', padding: '1px 8px', background: 'linear-gradient(156deg, #E3E6FF 0%, #E4F3F4 69.48%)', }; const fillStyle = { width: '100%', height: '100%', display: 'flex', borderRadius: '60px', gap: 2, alignItems: 'center', padding: '1px 8px', }; export default observer(Controls);