Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: show recording button if set #26565

Merged
merged 4 commits into from
Dec 2, 2024
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
41 changes: 41 additions & 0 deletions frontend/src/lib/components/ViewRecordingButton.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import { LemonButton, LemonButtonProps } from '@posthog/lemon-ui'
import { useActions } from 'kea'
import { Dayjs, dayjs } from 'lib/dayjs'
import { IconPlayCircle } from 'lib/lemon-ui/icons'
import { sessionPlayerModalLogic } from 'scenes/session-recordings/player/modal/sessionPlayerModalLogic'
import { urls } from 'scenes/urls'

import { EventType } from '~/types'

export default function ViewRecordingButton({
sessionId,
timestamp,
...props
}: Pick<LemonButtonProps, 'size' | 'type' | 'data-attr' | 'fullWidth' | 'className'> & {
sessionId: string
timestamp?: string | Dayjs
}): JSX.Element {
const { openSessionPlayer } = useActions(sessionPlayerModalLogic)

return (
<LemonButton
to={urls.replaySingle(sessionId)}
onClick={() => {
const fiveSecondsBeforeEvent = dayjs(timestamp).valueOf() - 5000
openSessionPlayer({ id: sessionId }, Math.max(fiveSecondsBeforeEvent, 0))
}}
sideIcon={<IconPlayCircle />}
{...props}
>
View recording
</LemonButton>
)
}

export const mightHaveRecording = (properties: EventType['properties']): boolean => {
return properties.$session_id
? properties.$recording_status
? properties.$recording_status === 'active'
: true
: false
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
: false
: isSemVerAfter("1.194.0") ? false : true

so.... we need to consider library version here, no?

something like that - with a made up method I don't think exists 🙈

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we not get away without it? I was working off the assumption that properties.$recording_status will be undefined for older versions of the SDK. That will fail the second check in the function and hence resolve to true (eg. for all events in a time before we captured $recording_status we assume there might be a recording

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ugh... yeah, ok... too many question marks and colons 🫠

    return properties.$session_id
        ? properties.$recording_status
            ? properties.$recording_status === 'active'
            : true
        : false        
        ```

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

too many for my brain

}
28 changes: 7 additions & 21 deletions frontend/src/queries/nodes/DataTable/EventRowActions.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,11 @@
import { useActions } from 'kea'
import { dayjs } from 'lib/dayjs'
import { IconLink, IconPlayCircle } from 'lib/lemon-ui/icons'
import ViewRecordingButton, { mightHaveRecording } from 'lib/components/ViewRecordingButton'
import { IconLink } from 'lib/lemon-ui/icons'
import { LemonButton } from 'lib/lemon-ui/LemonButton'
import { More } from 'lib/lemon-ui/LemonButton/More'
import { copyToClipboard } from 'lib/utils/copyToClipboard'
import { getCurrentTeamId } from 'lib/utils/getAppContext'
import { createActionFromEvent } from 'scenes/activity/explore/createActionFromEvent'
import { insightUrlForEvent } from 'scenes/insights/utils'
import { sessionPlayerModalLogic } from 'scenes/session-recordings/player/modal/sessionPlayerModalLogic'
import { teamLogic } from 'scenes/teamLogic'
import { urls } from 'scenes/urls'

Expand All @@ -18,7 +16,6 @@ interface EventActionProps {
}

export function EventRowActions({ event }: EventActionProps): JSX.Element {
const { openSessionPlayer } = useActions(sessionPlayerModalLogic)
const insightUrl = insightUrlForEvent(event)

return (
Expand Down Expand Up @@ -56,24 +53,13 @@ export function EventRowActions({ event }: EventActionProps): JSX.Element {
Copy link to event
</LemonButton>
)}
{!!event.properties?.$session_id && (
<LemonButton
to={urls.replaySingle(event.properties.$session_id)}
onClick={(e) => {
e.preventDefault()
if (event.properties.$session_id) {
openSessionPlayer(
{ id: event.properties.$session_id },
dayjs(event.timestamp).valueOf()
)
}
}}
{mightHaveRecording(event.properties) && (
<ViewRecordingButton
fullWidth
sideIcon={<IconPlayCircle />}
sessionId={event.properties.$session_id}
timestamp={event.timestamp}
data-attr="events-table-usage"
>
View recording
</LemonButton>
/>
)}
{insightUrl && (
<LemonButton to={insightUrl} fullWidth data-attr="events-table-usage">
Expand Down
44 changes: 14 additions & 30 deletions frontend/src/queries/nodes/HogQLX/render.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,7 @@
import { LemonButton, Link } from '@posthog/lemon-ui'
import { useActions } from 'kea'
import { Link } from '@posthog/lemon-ui'
import { JSONViewer } from 'lib/components/JSONViewer'
import { Sparkline } from 'lib/components/Sparkline'
import { IconPlayCircle } from 'lib/lemon-ui/icons'
import { sessionPlayerModalLogic } from 'scenes/session-recordings/player/modal/sessionPlayerModalLogic'
import { urls } from 'scenes/urls'
import ViewRecordingButton from 'lib/components/ViewRecordingButton'

import { ErrorBoundary } from '~/layout/ErrorBoundary'

Expand All @@ -24,30 +21,6 @@ export function parseHogQLX(value: any): any {
return value.map((v) => parseHogQLX(v))
}

function ViewRecordingModalButton({ sessionId }: { sessionId: string }): JSX.Element {
const { openSessionPlayer } = useActions(sessionPlayerModalLogic)
return (
<ErrorBoundary>
<LemonButton
type="primary"
size="xsmall"
sideIcon={<IconPlayCircle />}
data-attr="hog-ql-view-recording-button"
to={urls.replaySingle(sessionId)}
onClick={(e) => {
e.preventDefault()
if (sessionId) {
openSessionPlayer({ id: sessionId })
}
}}
className="inline-block"
>
View recording
</LemonButton>
</ErrorBoundary>
)
}

export function renderHogQLX(value: any): JSX.Element {
const object = parseHogQLX(value)

Expand All @@ -68,7 +41,18 @@ export function renderHogQLX(value: any): JSX.Element {
)
} else if (tag === 'RecordingButton') {
const { sessionId, ...props } = rest
return <ViewRecordingModalButton sessionId={sessionId} {...props} />
return (
<ErrorBoundary>
<ViewRecordingButton
sessionId={sessionId}
type="primary"
size="xsmall"
data-attr="hog-ql-view-recording-button"
className="inline-block"
{...props}
/>
</ErrorBoundary>
)
} else if (tag === 'a') {
const { href, source, target } = rest
return (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,7 @@ import { ReadingHog } from 'lib/components/hedgehogs'
import { ProductIntroduction } from 'lib/components/ProductIntroduction/ProductIntroduction'
import { Sparkline } from 'lib/components/Sparkline'
import { TZLabel } from 'lib/components/TZLabel'
import { IconPlayCircle } from 'lib/lemon-ui/icons'
import { LemonButton } from 'lib/lemon-ui/LemonButton'
import ViewRecordingButton from 'lib/components/ViewRecordingButton'
import { LemonTable } from 'lib/lemon-ui/LemonTable'
import { Link } from 'lib/lemon-ui/Link'
import { urls } from 'scenes/urls'
Expand Down Expand Up @@ -155,15 +154,13 @@ const WARNING_TYPE_RENDERER = {
<li>session_id: {details.session_id}</li>
</ul>
<div className="max-w-30 mt-2">
<LemonButton
<ViewRecordingButton
sessionId={details.session_id}
timestamp={details.timestamp}
type="primary"
size="xsmall"
to={urls.replaySingle(details.session_id)}
sideIcon={<IconPlayCircle />}
data-attr="skewed-timestamp-view-recording"
>
View recording
</LemonButton>
/>
</div>
</>
)
Expand All @@ -188,15 +185,13 @@ const WARNING_TYPE_RENDERER = {
<li>skew: {details.daysFromNow} days</li>
</ul>
<div className="max-w-30 mt-2">
<LemonButton
<ViewRecordingButton
sessionId={details.session_id}
timestamp={details.timestamp}
type="primary"
size="xsmall"
to={urls.replaySingle(details.session_id)}
sideIcon={<IconPlayCircle />}
data-attr="skewed-timestamp-view-recording"
>
View recording
</LemonButton>
/>
</div>
</>
)
Expand All @@ -216,15 +211,13 @@ const WARNING_TYPE_RENDERER = {
<li>session_id: {details.session_id}</li>
</ul>
<div className="max-w-30 mt-2">
<LemonButton
<ViewRecordingButton
sessionId={details.session_id}
timestamp={details.timestamp}
type="primary"
size="xsmall"
to={urls.replaySingle(details.session_id)}
sideIcon={<IconPlayCircle />}
data-attr="message-too-large-view-recording"
>
View recording
</LemonButton>
/>
</div>
</>
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { Scene } from 'scenes/sceneTypes'
import { urls } from 'scenes/urls'

import { ErrorTrackingIssue } from '~/queries/schema'
import { Breadcrumb } from '~/types'
import { Breadcrumb, EventType } from '~/types'

import type { errorTrackingIssueSceneLogicType } from './errorTrackingIssueSceneLogicType'
import { errorTrackingLogic } from './errorTrackingLogic'
Expand All @@ -16,7 +16,7 @@ import { errorTrackingIssueEventsQuery, errorTrackingIssueQuery } from './querie
export interface ErrorTrackingEvent {
uuid: string
timestamp: Dayjs
properties: Record<string, any>
properties: EventType['properties']
person: {
distinct_id: string
uuid?: string
Expand Down
31 changes: 8 additions & 23 deletions frontend/src/scenes/error-tracking/groups/OverviewTab.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,10 @@
import { PersonDisplay, TZLabel } from '@posthog/apps-common'
import { LemonButton } from '@posthog/lemon-ui'
import clsx from 'clsx'
import { useActions, useValues } from 'kea'
import { EmptyMessage } from 'lib/components/EmptyMessage/EmptyMessage'
import { ErrorDisplay } from 'lib/components/Errors/ErrorDisplay'
import { Playlist } from 'lib/components/Playlist/Playlist'
import { dayjs } from 'lib/dayjs'
import { sessionPlayerModalLogic } from 'scenes/session-recordings/player/modal/sessionPlayerModalLogic'
import ViewRecordingButton, { mightHaveRecording } from 'lib/components/ViewRecordingButton'
import { PropertyIcons } from 'scenes/session-recordings/playlist/SessionRecordingPreview'

import { ErrorTrackingEvent, errorTrackingIssueSceneLogic } from '../errorTrackingIssueSceneLogic'
Expand Down Expand Up @@ -38,7 +36,13 @@ export const OverviewTab = (): JSX.Element => {
event ? (
<div className="h-full overflow-auto">
<div className="bg-bg-light p-1 flex justify-end border-b min-h-[42px]">
<ViewSessionButton event={event} />
{mightHaveRecording(event.properties) && (
<ViewRecordingButton
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe nicer to do this mightHaveRecording check inside the component and set disabledReason on the button

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I didn't want to pass the properties into the component because not ever instance has access to them. Have changed it around so disabledReason is passed in as a prop in the two cases we know the event properties

size="small"
sessionId={event.properties.$session_id}
timestamp={event.timestamp}
/>
)}
</div>
<div className="pl-2">
<ErrorDisplay eventProperties={event.properties} />
Expand All @@ -62,25 +66,6 @@ export const OverviewTab = (): JSX.Element => {
)
}

const ViewSessionButton = ({ event }: { event: ErrorTrackingEvent }): JSX.Element | null => {
const { openSessionPlayer } = useActions(sessionPlayerModalLogic)

const sessionId = event.properties.$session_id

return (
<LemonButton
size="small"
onClick={() => {
const fiveSecondsBeforeEvent = dayjs(event.timestamp).valueOf() - 5000
openSessionPlayer({ id: sessionId }, Math.max(fiveSecondsBeforeEvent, 0))
}}
disabledReason={!sessionId ? 'There was no Session ID associated with this exception' : undefined}
>
View recording
</LemonButton>
)
}

const ListItemException = ({
item: { timestamp, properties, person },
isActive,
Expand Down
Loading