Skip to content

Commit

Permalink
feat: doctor tab in the player inspector (#20116)
Browse files Browse the repository at this point in the history
* feat: doctor tab

* add simple snapshot type counting

* refactor

* fly-by don't reset start time as we load snapshots
  • Loading branch information
pauldambra authored Feb 6, 2024
1 parent 835fc66 commit a136a09
Show file tree
Hide file tree
Showing 7 changed files with 243 additions and 36 deletions.
1 change: 1 addition & 0 deletions frontend/src/lib/constants.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -205,6 +205,7 @@ export const FEATURE_FLAGS = {
AI_SESSION_SUMMARY: 'ai-session-summary', // owner: #team-replay
PRODUCT_INTRO_PAGES: 'product-intro-pages', // owner: @raquelmsmith
DATANODE_CONCURRENCY_LIMIT: 'datanode-concurrency-limit', // owner: @robbie-c
SESSION_REPLAY_DOCTOR: 'session-replay-doctor', // owner: #team-replay
} as const
export type FeatureFlagKey = (typeof FEATURE_FLAGS)[keyof typeof FEATURE_FLAGS]

Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import { IconX } from '@posthog/icons'
import { LemonButton, LemonCheckbox, LemonInput, LemonSelect, Tooltip } from '@posthog/lemon-ui'
import { useActions, useValues } from 'kea'
import { FEATURE_FLAGS } from 'lib/constants'
import {
IconBugShield,
IconGauge,
IconInfo,
IconPause,
Expand All @@ -11,13 +13,18 @@ import {
IconUnverifiedEvent,
} from 'lib/lemon-ui/icons'
import { Spinner } from 'lib/lemon-ui/Spinner/Spinner'
import { featureFlagLogic } from 'lib/logic/featureFlagLogic'
import { capitalizeFirstLetter } from 'lib/utils'
import { IconWindow } from 'scenes/session-recordings/player/icons'

import { SessionRecordingPlayerTab } from '~/types'

import { playerSettingsLogic } from '../playerSettingsLogic'
import { sessionRecordingPlayerLogic, SessionRecordingPlayerMode } from '../sessionRecordingPlayerLogic'
import {
sessionRecordingPlayerLogic,
SessionRecordingPlayerLogicProps,
SessionRecordingPlayerMode,
} from '../sessionRecordingPlayerLogic'
import { InspectorSearchInfo } from './components/InspectorSearchInfo'
import { playerInspectorLogic } from './playerInspectorLogic'

Expand All @@ -26,59 +33,83 @@ const TabToIcon = {
[SessionRecordingPlayerTab.EVENTS]: IconUnverifiedEvent,
[SessionRecordingPlayerTab.CONSOLE]: IconTerminal,
[SessionRecordingPlayerTab.NETWORK]: IconGauge,
[SessionRecordingPlayerTab.DOCTOR]: IconBugShield,
}

function TabButtons({
tabs,
logicProps,
}: {
tabs: SessionRecordingPlayerTab[]
logicProps: SessionRecordingPlayerLogicProps
}): JSX.Element {
const inspectorLogic = playerInspectorLogic(logicProps)
const { tab, tabsState } = useValues(inspectorLogic)
const { setTab } = useActions(inspectorLogic)

return (
<>
{tabs.map((tabId) => {
const TabIcon = TabToIcon[tabId]
return (
<LemonButton
key={tabId}
size="small"
// We want to indicate the tab is loading, but not disable it so we just override the icon here
icon={
TabIcon ? tabsState[tabId] === 'loading' ? <Spinner textColored /> : <TabIcon /> : undefined
}
active={tab === tabId}
onClick={() => setTab(tabId)}
>
{capitalizeFirstLetter(tabId)}
</LemonButton>
)
})}
</>
)
}

export function PlayerInspectorControls({ onClose }: { onClose: () => void }): JSX.Element {
const { logicProps } = useValues(sessionRecordingPlayerLogic)
const inspectorLogic = playerInspectorLogic(logicProps)
const { windowIdFilter, tab, syncScrollingPaused, tabsState, windowIds, showMatchingEventsFilter } =
useValues(inspectorLogic)
const { setWindowIdFilter, setTab, setSyncScrollPaused } = useActions(inspectorLogic)
const { tab, windowIdFilter, syncScrollingPaused, windowIds, showMatchingEventsFilter } = useValues(inspectorLogic)
const { setWindowIdFilter, setSyncScrollPaused, setTab } = useActions(inspectorLogic)
const { showOnlyMatching, timestampMode, miniFilters, syncScroll, searchQuery } = useValues(playerSettingsLogic)
const { setShowOnlyMatching, setTimestampMode, setMiniFilter, setSyncScroll, setSearchQuery } =
useActions(playerSettingsLogic)

const mode = logicProps.mode ?? SessionRecordingPlayerMode.Standard

const { featureFlags } = useValues(featureFlagLogic)

const tabs = [
SessionRecordingPlayerTab.ALL,
SessionRecordingPlayerTab.EVENTS,
SessionRecordingPlayerTab.CONSOLE,
SessionRecordingPlayerTab.NETWORK,
]
if (window.IMPERSONATED_SESSION || featureFlags[FEATURE_FLAGS.SESSION_REPLAY_DOCTOR]) {
tabs.push(SessionRecordingPlayerTab.DOCTOR)
} else {
// ensure we've not left the doctor tab in the tabs state
if (tab === SessionRecordingPlayerTab.DOCTOR) {
setTab(SessionRecordingPlayerTab.ALL)
}
}

if (mode === SessionRecordingPlayerMode.Sharing) {
// Events can't be loaded in sharing mode
tabs.splice(1, 1)
// Doctor tab is not available in sharing mode
tabs.pop()
}

return (
<div className="bg-side p-2 space-y-2 border-b">
<div className="flex justify-between gap-2 flex-nowrap">
<div className="flex flex-1 items-center gap-1">
{tabs.map((tabId) => {
const TabIcon = TabToIcon[tabId]
return (
<LemonButton
key={tabId}
size="small"
// We want to indicate the tab is loading, but not disable it so we just override the icon here
icon={
TabIcon ? (
tabsState[tabId] === 'loading' ? (
<Spinner textColored />
) : (
<TabIcon />
)
) : undefined
}
active={tab === tabId}
onClick={() => setTab(tabId)}
>
{capitalizeFirstLetter(tabId)}
</LemonButton>
)
})}
<TabButtons tabs={tabs} logicProps={logicProps} />
</div>
<LemonButton size="small" icon={<IconX />} onClick={onClose} />
</div>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { LemonButton } from '@posthog/lemon-ui'
import { CodeSnippet, Language } from 'lib/components/CodeSnippet'

import { InspectorListItemDoctor } from '../playerInspectorLogic'

export interface ItemDoctorProps {
item: InspectorListItemDoctor
expanded: boolean
setExpanded: (expanded: boolean) => void
}

export function ItemDoctor({ item, expanded, setExpanded }: ItemDoctorProps): JSX.Element {
return (
<>
<LemonButton noPadding onClick={() => setExpanded(!expanded)} fullWidth data-attr="item-doctor-item">
<div className="p-2 text-xs cursor-pointer truncate font-mono flex-1">{item.tag}</div>
</LemonButton>

{expanded && (
<div className="p-2 text-xs border-t">
{item.data && (
<CodeSnippet language={Language.JSON} wrap thing={`custom event - ${item.tag}`}>
{JSON.stringify(item.data, null, 2)}
</CodeSnippet>
)}
</div>
)}
</>
)
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { TZLabel } from '@posthog/apps-common'
import { IconGear } from '@posthog/icons'
import { LemonButton, LemonDivider } from '@posthog/lemon-ui'
import clsx from 'clsx'
import { useActions, useValues } from 'kea'
Expand All @@ -16,6 +17,7 @@ import { playerSettingsLogic } from '../../playerSettingsLogic'
import { sessionRecordingPlayerLogic } from '../../sessionRecordingPlayerLogic'
import { InspectorListItem, playerInspectorLogic } from '../playerInspectorLogic'
import { ItemConsoleLog } from './ItemConsoleLog'
import { ItemDoctor } from './ItemDoctor'
import { ItemEvent } from './ItemEvent'
import { ItemPerformanceEvent } from './ItemPerformanceEvent'

Expand All @@ -40,6 +42,14 @@ const typeToIconAndDescription = {
Icon: IconOffline,
tooltip: 'browser went offline or returned online',
},
['$session_config']: {
Icon: IconGear,
tooltip: 'Session recording config',
},
['doctor']: {
Icon: undefined,
tooltip: 'Doctor event',
},
}
const PLAYER_INSPECTOR_LIST_ITEM_MARGIN = 4

Expand Down Expand Up @@ -165,6 +175,8 @@ export function PlayerInspectorListItem({
<div className="flex items-start p-2 text-xs">
{item.offline ? 'Browser went offline' : 'Browser returned online'}
</div>
) : item.type === SessionRecordingPlayerTab.DOCTOR ? (
<ItemDoctor item={item} {...itemProps} />
) : null}

{isExpanded ? (
Expand Down
Loading

0 comments on commit a136a09

Please sign in to comment.