Skip to content

Commit

Permalink
Fix up for loading IDs
Browse files Browse the repository at this point in the history
  • Loading branch information
benjackwhite committed Oct 6, 2023
1 parent 36f1ba8 commit b190423
Show file tree
Hide file tree
Showing 14 changed files with 155 additions and 60 deletions.
20 changes: 16 additions & 4 deletions frontend/src/scenes/notebooks/Nodes/NotebookNodePlaylist.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { createPostHogWidgetNode } from 'scenes/notebooks/Nodes/NodeWrapper'
import { FilterType, NotebookNodeType, RecordingFilters } from '~/types'
import { SessionRecordingsPlaylistProps } from 'scenes/session-recordings/playlist/SessionRecordingsPlaylist'
import {
SessionRecordingPlaylistLogicProps,
addedAdvancedFilters,
getDefaultFilters,
sessionRecordingsPlaylistLogic,
Expand All @@ -19,10 +19,10 @@ import { sessionRecordingPlayerLogic } from 'scenes/session-recordings/player/se
import { summarizePlaylistFilters } from 'scenes/session-recordings/playlist/playlistUtils'

const Component = (props: NotebookNodeViewProps<NotebookNodePlaylistAttributes>): JSX.Element => {
const { filters, nodeId } = props.attributes
const { filters, pinned, nodeId } = props.attributes
const playerKey = `notebook-${nodeId}`

const recordingPlaylistLogicProps: SessionRecordingsPlaylistProps = useMemo(
const recordingPlaylistLogicProps: SessionRecordingPlaylistLogicProps = useMemo(
() => ({
logicKey: playerKey,
filters,
Expand All @@ -33,8 +33,16 @@ const Component = (props: NotebookNodeViewProps<NotebookNodePlaylistAttributes>)
filters: newFilters,
})
},
pinnedRecordings: pinned,
onPinnedChange(recording, isPinned) {
props.updateAttributes({
pinned: isPinned
? [...(pinned || []), String(recording.id)]
: pinned?.filter((id) => id !== recording.id),
})
},
}),
[playerKey, filters]
[playerKey, filters, pinned]
)

const { expanded } = useValues(notebookNodeLogic)
Expand Down Expand Up @@ -126,6 +134,7 @@ export const Settings = ({

type NotebookNodePlaylistAttributes = {
filters: RecordingFilters
pinned?: string[]
}

export const NotebookNodePlaylist = createPostHogWidgetNode<NotebookNodePlaylistAttributes>({
Expand All @@ -143,6 +152,9 @@ export const NotebookNodePlaylist = createPostHogWidgetNode<NotebookNodePlaylist
filters: {
default: undefined,
},
pinned: {
default: undefined,
},
},
pasteOptions: {
find: urls.replay() + '(.+)',
Expand Down
36 changes: 22 additions & 14 deletions frontend/src/scenes/session-recordings/player/PlayerMetaLinks.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import {
} from 'scenes/session-recordings/player/sessionRecordingPlayerLogic'
import { useActions, useValues } from 'kea'
import { LemonButton, LemonButtonProps } from 'lib/lemon-ui/LemonButton'
import { IconComment, IconDelete, IconJournalPlus, IconLink } from 'lib/lemon-ui/icons'
import { IconComment, IconDelete, IconJournalPlus, IconLink, IconPinFilled, IconPinOutline } from 'lib/lemon-ui/icons'
import { openPlayerShareDialog } from 'scenes/session-recordings/player/share/PlayerShare'
import { PlaylistPopoverButton } from './playlist-popover/PlaylistPopover'
import { LemonDialog } from 'lib/lemon-ui/LemonDialog'
Expand Down Expand Up @@ -83,19 +83,27 @@ export function PlayerMetaLinks(): JSX.Element {
<span>Share</span>
</LemonButton>

{nodeLogic ? (
nodeLogic.props.nodeType !== NotebookNodeType.Recording ? (
<LemonButton
icon={<IconJournalPlus />}
size="small"
onClick={() => {
nodeLogic.actions.insertAfter({
type: NotebookNodeType.Recording,
attrs: { id: sessionRecordingId },
})
}}
/>
) : null
{nodeLogic?.props.nodeType === NotebookNodeType.RecordingPlaylist ? (
<LemonButton
icon={<IconJournalPlus />}
size="small"
onClick={() => {
nodeLogic.actions.insertAfter({
type: NotebookNodeType.Recording,
attrs: { id: sessionRecordingId },
})
}}
/>
) : null}

{logicProps.setPinned ? (
<LemonButton
onClick={() => {
logicProps.setPinned?.(!logicProps.pinned)
}}
size="small"
icon={logicProps.pinned ? <IconPinFilled /> : <IconPinOutline />}
/>
) : (
<PlaylistPopoverButton {...commonProps}>
<span>Pin</span>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,8 @@ export function SessionRecordingPlayer(props: SessionRecordingPlayerProps): JSX.
autoPlay = true,
playlistLogic,
mode = SessionRecordingPlayerMode.Standard,
pinned,
setPinned,
} = props

const playerRef = useRef<HTMLDivElement>(null)
Expand All @@ -62,6 +64,8 @@ export function SessionRecordingPlayer(props: SessionRecordingPlayerProps): JSX.
playlistLogic,
mode,
playerRef,
pinned,
setPinned,
}
const {
incrementClickCount,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { MutableRefObject } from 'react'
import { actions, connect, kea, key, listeners, path, props, reducers, selectors } from 'kea'
import type { seekbarLogicType } from './seekbarLogicType'
import {
SessionRecordingLogicProps,
SessionRecordingPlayerLogicProps,
sessionRecordingPlayerLogic,
} from 'scenes/session-recordings/player/sessionRecordingPlayerLogic'
import { clamp } from 'lib/utils'
Expand All @@ -11,9 +11,9 @@ import { getXPos, InteractEvent, ReactInteractEvent, THUMB_OFFSET, THUMB_SIZE }

export const seekbarLogic = kea<seekbarLogicType>([
path((key) => ['scenes', 'session-recordings', 'player', 'seekbarLogic', key]),
props({} as SessionRecordingLogicProps),
key((props: SessionRecordingLogicProps) => `${props.playerKey}-${props.sessionRecordingId}`),
connect((props: SessionRecordingLogicProps) => ({
props({} as SessionRecordingPlayerLogicProps),
key((props: SessionRecordingPlayerLogicProps) => `${props.playerKey}-${props.sessionRecordingId}`),
connect((props: SessionRecordingPlayerLogicProps) => ({
values: [sessionRecordingPlayerLogic(props), ['sessionPlayerData', 'currentPlayerTime']],
actions: [sessionRecordingPlayerLogic(props), ['seekToTime', 'startScrub', 'endScrub', 'setCurrentTimestamp']],
})),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import {
} from '~/types'
import type { playerInspectorLogicType } from './playerInspectorLogicType'
import { playerSettingsLogic } from 'scenes/session-recordings/player/playerSettingsLogic'
import { SessionRecordingLogicProps, sessionRecordingPlayerLogic } from '../sessionRecordingPlayerLogic'
import { SessionRecordingPlayerLogicProps, sessionRecordingPlayerLogic } from '../sessionRecordingPlayerLogic'
import { sessionRecordingDataLogic } from '../sessionRecordingDataLogic'
import FuseClass from 'fuse.js'
import { Dayjs, dayjs } from 'lib/dayjs'
Expand Down Expand Up @@ -120,7 +120,7 @@ export type InspectorListItemPerformance = InspectorListItemBase & {

export type InspectorListItem = InspectorListItemEvent | InspectorListItemConsole | InspectorListItemPerformance

export interface PlayerInspectorLogicProps extends SessionRecordingLogicProps {
export interface PlayerInspectorLogicProps extends SessionRecordingPlayerLogicProps {
matchingEventsMatchType?: MatchingEventsMatchType
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { connect, kea, key, listeners, path, props, selectors } from 'kea'
import type { playerMetaLogicType } from './playerMetaLogicType'
import { sessionRecordingDataLogic } from 'scenes/session-recordings/player/sessionRecordingDataLogic'
import {
SessionRecordingLogicProps,
SessionRecordingPlayerLogicProps,
sessionRecordingPlayerLogic,
} from 'scenes/session-recordings/player/sessionRecordingPlayerLogic'
import { eventWithTime } from '@rrweb/types'
Expand All @@ -12,9 +12,9 @@ import { sessionRecordingsListPropertiesLogic } from '../playlist/sessionRecordi

export const playerMetaLogic = kea<playerMetaLogicType>([
path((key) => ['scenes', 'session-recordings', 'player', 'playerMetaLogic', key]),
props({} as SessionRecordingLogicProps),
key((props: SessionRecordingLogicProps) => `${props.playerKey}-${props.sessionRecordingId}`),
connect((props: SessionRecordingLogicProps) => ({
props({} as SessionRecordingPlayerLogicProps),
key((props: SessionRecordingPlayerLogicProps) => `${props.playerKey}-${props.sessionRecordingId}`),
connect((props: SessionRecordingPlayerLogicProps) => ({
values: [
sessionRecordingDataLogic(props),
[
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { loaders } from 'kea-loaders'
import api from 'lib/api'
import { toParams } from 'lib/utils'
import {
SessionRecordingLogicProps,
SessionRecordingPlayerLogicProps,
sessionRecordingPlayerLogic,
} from 'scenes/session-recordings/player/sessionRecordingPlayerLogic'

Expand All @@ -18,9 +18,9 @@ import { sessionRecordingsPlaylistSceneLogic } from 'scenes/session-recordings/p

export const playlistPopoverLogic = kea<playlistPopoverLogicType>([
path((key) => ['scenes', 'session-recordings', 'player', 'playlist-popover', 'playlistPopoverLogic', key]),
props({} as SessionRecordingLogicProps),
key((props: SessionRecordingLogicProps) => `${props.playerKey}-${props.sessionRecordingId}`),
connect((props: SessionRecordingLogicProps) => ({
props({} as SessionRecordingPlayerLogicProps),
key((props: SessionRecordingPlayerLogicProps) => `${props.playerKey}-${props.sessionRecordingId}`),
connect((props: SessionRecordingPlayerLogicProps) => ({
actions: [
sessionRecordingPlayerLogic(props),
['setPause'],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,12 @@ import { windowValues } from 'kea-window-values'
import type { sessionRecordingPlayerLogicType } from './sessionRecordingPlayerLogicType'
import { Replayer } from 'rrweb'
import { eventUsageLogic } from 'lib/utils/eventUsageLogic'
import { AvailableFeature, RecordingSegment, SessionPlayerData, SessionPlayerState, SessionRecordingId } from '~/types'
import { AvailableFeature, RecordingSegment, SessionPlayerData, SessionPlayerState } from '~/types'
import { getBreakpoint } from 'lib/utils/responsiveUtils'
import { sessionRecordingDataLogic } from 'scenes/session-recordings/player/sessionRecordingDataLogic'
import {
SessionRecordingDataLogicProps,
sessionRecordingDataLogic,
} from 'scenes/session-recordings/player/sessionRecordingDataLogic'
import { deleteRecording } from './utils/playerUtils'
import { playerSettingsLogic } from './playerSettingsLogic'
import { clamp, downloadFile, fromParamsGivenUrl } from 'lib/utils'
Expand Down Expand Up @@ -74,19 +77,16 @@ export enum SessionRecordingPlayerMode {
Preview = 'preview',
}

// This is the basic props used by most sub-logics
export interface SessionRecordingLogicProps {
sessionRecordingId: SessionRecordingId
export interface SessionRecordingPlayerLogicProps extends SessionRecordingDataLogicProps {
playerKey: string
}

export interface SessionRecordingPlayerLogicProps extends SessionRecordingLogicProps {
sessionRecordingData?: SessionPlayerData
matchingEventsMatchType?: MatchingEventsMatchType
playlistLogic?: BuiltLogic<sessionRecordingsPlaylistLogicType>
autoPlay?: boolean
mode?: SessionRecordingPlayerMode
playerRef?: RefObject<HTMLDivElement>
pinned?: boolean
setPinned?: (pinned: boolean) => void
}

const isMediaElementPlaying = (element: HTMLMediaElement): boolean =>
Expand Down Expand Up @@ -1013,7 +1013,7 @@ export const sessionRecordingPlayerLogic = kea<sessionRecordingPlayerLogicType>(
}),
])

export const getCurrentPlayerTime = (logicProps: SessionRecordingLogicProps): number => {
export const getCurrentPlayerTime = (logicProps: SessionRecordingPlayerLogicProps): number => {
// NOTE: We pull this value at call time as otherwise it would trigger re-renders if pulled from the hook
const playerTime = sessionRecordingPlayerLogic.findMounted(logicProps)?.values.currentPlayerTime || 0
return Math.floor(playerTime / 1000)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ export interface SessionRecordingPreviewProps {
onPropertyClick?: (property: string, value?: string) => void
isActive?: boolean
onClick?: () => void
pinned?: boolean
}

function RecordingDuration({
Expand Down Expand Up @@ -150,12 +151,12 @@ function FirstURL(props: { startUrl: string | undefined }): JSX.Element {
)
}

function PinnedIndicator(props: { pinnedCount: number | undefined }): JSX.Element | null {
return (props.pinnedCount ?? 0) > 0 ? (
<Tooltip placement="topRight" title={`This recording is pinned on ${props.pinnedCount} playlists`}>
function PinnedIndicator(): JSX.Element | null {
return (
<Tooltip placement="topRight" title={`This recording is pinned to this list`}>
<IconPinFilled className="text-sm text-orange shrink-0" />
</Tooltip>
) : null
)
}

function ViewedIndicator(props: { viewed: boolean }): JSX.Element | null {
Expand All @@ -179,6 +180,7 @@ export function SessionRecordingPreview({
isActive,
onClick,
onPropertyClick,
pinned,
}: SessionRecordingPreviewProps): JSX.Element {
const { durationTypeToShow } = useValues(playerSettingsLogic)

Expand Down Expand Up @@ -220,7 +222,7 @@ export function SessionRecordingPreview({

<div className="w-6 flex flex-col items-center mt-1">
<ViewedIndicator viewed={recording.viewed} />
<PinnedIndicator pinnedCount={recording.pinned_count} />
{pinned ? <PinnedIndicator /> : null}
</div>
</div>
</DraggableToNotebook>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ function RecordingsLists(): JSX.Element {
hasNext,
recordings,
sessionRecordingsResponseLoading,
activeSessionRecording,
activeSessionRecordingId,
showFilters,
showSettings,
totalFiltersCount,
Expand All @@ -71,6 +71,7 @@ function RecordingsLists(): JSX.Element {
showAdvancedFilters,
hasAdvancedFilters,
logicProps,
pinnedRecordingIds,
} = useValues(sessionRecordingsPlaylistLogic)
const {
setSelectedRecordingId,
Expand Down Expand Up @@ -208,7 +209,8 @@ function RecordingsLists(): JSX.Element {
recording={rec}
onClick={() => onRecordingClick(rec)}
onPropertyClick={onPropertyClick}
isActive={activeSessionRecording?.id === rec.id}
isActive={activeSessionRecordingId === rec.id}
pinned={pinnedRecordingIds.includes(rec.id)}
/>
</Fragment>
))}
Expand Down Expand Up @@ -275,7 +277,8 @@ export function SessionRecordingsPlaylist(props: SessionRecordingPlaylistLogicPr
autoPlay: props.autoPlay ?? true,
}
const logic = sessionRecordingsPlaylistLogic(logicProps)
const { activeSessionRecording, matchingEventsMatchType } = useValues(logic)
const { activeSessionRecording, activeSessionRecordingId, matchingEventsMatchType, pinnedRecordingIds } =
useValues(logic)

const { ref: playlistRef, size } = useResizeBreakpoints({
0: 'small',
Expand All @@ -299,13 +302,24 @@ export function SessionRecordingsPlaylist(props: SessionRecordingPlaylistLogicPr
<RecordingsLists />
</div>
<div className="SessionRecordingsPlaylist__player">
{activeSessionRecording?.id ? (
{activeSessionRecordingId ? (
<SessionRecordingPlayer
playerKey="playlist"
sessionRecordingId={activeSessionRecording?.id}
sessionRecordingId={activeSessionRecordingId}
matchingEventsMatchType={matchingEventsMatchType}
playlistLogic={logic}
noBorder
pinned={pinnedRecordingIds.includes(activeSessionRecordingId)}
setPinned={
props.onPinnedChange
? (pinned) => {
if (!activeSessionRecording?.id) {
return
}
props.onPinnedChange?.(activeSessionRecording, pinned)
}
: undefined
}
/>
) : (
<div className="mt-20">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ export function SessionRecordingsPlaylistScene(): JSX.Element {
const { playlist, playlistLoading, pinnedRecordings, hasChanges, derivedName } = useValues(
sessionRecordingsPlaylistSceneLogic
)
const { setFilters, updatePlaylist, duplicatePlaylist, deletePlaylist } = useActions(
const { setFilters, updatePlaylist, duplicatePlaylist, deletePlaylist, onPinnedChange } = useActions(
sessionRecordingsPlaylistSceneLogic
)

Expand Down Expand Up @@ -150,6 +150,7 @@ export function SessionRecordingsPlaylistScene(): JSX.Element {
<SessionRecordingsPlaylist
filters={playlist.filters}
onFiltersChange={setFilters}
onPinnedChange={onPinnedChange}
pinnedRecordings={pinnedRecordings?.results}
/>
</div>
Expand Down
Loading

0 comments on commit b190423

Please sign in to comment.