Skip to content

Commit ac44668

Browse files
authored
feat: add replay to omnisearch (PostHog#24978)
1 parent e46f90d commit ac44668

File tree

2 files changed

+104
-1
lines changed

2 files changed

+104
-1
lines changed

frontend/src/lib/components/CommandPalette/commandPaletteLogic.tsx

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ import posthog from 'posthog-js'
5656
import { newDashboardLogic } from 'scenes/dashboard/newDashboardLogic'
5757
import { insightTypeURL } from 'scenes/insights/utils'
5858
import { preflightLogic } from 'scenes/PreflightCheck/preflightLogic'
59+
import { WATCH_RECORDINGS_OF_KEY, watchRecordingsOfCommand } from 'scenes/session-recordings/replayPaletteCommands'
5960
import { teamLogic } from 'scenes/teamLogic'
6061
import { urls } from 'scenes/urls'
6162
import { userLogic } from 'scenes/userLogic'
@@ -119,7 +120,7 @@ export type RegExpCommandPairs = [RegExp | null, Command][]
119120

120121
const RESULTS_MAX = 5
121122

122-
const GLOBAL_COMMAND_SCOPE = 'global'
123+
export const GLOBAL_COMMAND_SCOPE = 'global'
123124

124125
function resolveCommand(source: Command | CommandFlow, argument?: string, prefixApplied?: string): CommandResult[] {
125126
// run resolver or use ready-made results
@@ -931,6 +932,7 @@ export const commandPaletteLogic = kea<commandPaletteLogicType>([
931932
actions.registerCommand(toggleHedgehogMode)
932933
actions.registerCommand(shortcuts)
933934
actions.registerCommand(sidepanel)
935+
actions.registerCommand(watchRecordingsOfCommand(push))
934936
},
935937
beforeUnmount: () => {
936938
actions.deregisterCommand('go-to')
@@ -945,6 +947,7 @@ export const commandPaletteLogic = kea<commandPaletteLogicType>([
945947
actions.deregisterCommand('toggle-hedgehog-mode')
946948
actions.deregisterCommand('shortcuts')
947949
actions.deregisterCommand('sidepanel')
950+
actions.deregisterCommand(WATCH_RECORDINGS_OF_KEY)
948951
},
949952
})),
950953
])
Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
import { IconRewindPlay } from '@posthog/icons'
2+
import { Command, GLOBAL_COMMAND_SCOPE } from 'lib/components/CommandPalette/commandPaletteLogic'
3+
import { isURL } from 'lib/utils'
4+
import { urls } from 'scenes/urls'
5+
6+
import {
7+
FilterLogicalOperator,
8+
PropertyFilterType,
9+
PropertyOperator,
10+
RecordingDurationFilter,
11+
RecordingUniversalFilters,
12+
ReplayTabs,
13+
} from '~/types'
14+
15+
function isUUIDLike(candidate: string): boolean {
16+
return candidate.length === 36 && !!candidate.toLowerCase().match(/^[0-9a-f-]+$/)?.length
17+
}
18+
19+
export const WATCH_RECORDINGS_OF_KEY = 'watch-recordings-of'
20+
export const watchRecordingsOfCommand = (
21+
push: (
22+
url: string,
23+
searchInput?: string | Record<string, any> | undefined,
24+
hashInput?: string | Record<string, any> | undefined
25+
) => void
26+
): Command => ({
27+
key: WATCH_RECORDINGS_OF_KEY,
28+
scope: GLOBAL_COMMAND_SCOPE,
29+
resolver: (argument: string | undefined) => {
30+
if (argument === undefined) {
31+
return null
32+
}
33+
34+
const replayFilter = (
35+
key: 'snapshot_source' | 'visited_page',
36+
value: string
37+
): Partial<RecordingUniversalFilters> => ({
38+
date_from: '-3d',
39+
filter_group: {
40+
type: FilterLogicalOperator.And,
41+
values: [
42+
{
43+
type: FilterLogicalOperator.And,
44+
values: [
45+
{
46+
key: key,
47+
value: [value],
48+
operator: PropertyOperator.Exact,
49+
type: PropertyFilterType.Recording,
50+
},
51+
],
52+
},
53+
],
54+
},
55+
duration: [
56+
{
57+
type: PropertyFilterType.Recording,
58+
key: 'duration',
59+
value: 1,
60+
operator: 'gt',
61+
} as RecordingDurationFilter,
62+
],
63+
})
64+
65+
const words = argument.split(' ')
66+
if (words.includes('web') || words.includes('mobile')) {
67+
return {
68+
icon: IconRewindPlay,
69+
display: `Watch ${argument} recordings`,
70+
executor: () => {
71+
push(urls.replay(ReplayTabs.Home, replayFilter('snapshot_source', argument)))
72+
},
73+
}
74+
}
75+
76+
const url = words.find((word) => isURL(word))
77+
if (url) {
78+
return {
79+
icon: IconRewindPlay,
80+
display: `Watch recordings of visits to ${url}`,
81+
executor: () => {
82+
push(urls.replay(ReplayTabs.Home, replayFilter('visited_page', url)))
83+
},
84+
}
85+
}
86+
87+
const uuid = words.find((word) => isUUIDLike(word))
88+
if (uuid) {
89+
return {
90+
icon: IconRewindPlay,
91+
display: `Watch recording of session: ${uuid}`,
92+
executor: () => {
93+
push(urls.replaySingle(uuid))
94+
},
95+
}
96+
}
97+
98+
return null
99+
},
100+
})

0 commit comments

Comments
 (0)