Skip to content

Commit

Permalink
feat: rejig player controls (#25414)
Browse files Browse the repository at this point in the history
Co-authored-by: github-actions <41898282+github-actions[bot]@users.noreply.github.com>
  • Loading branch information
pauldambra and github-actions[bot] authored Oct 7, 2024
1 parent 7a5b418 commit cbc5afd
Show file tree
Hide file tree
Showing 10 changed files with 175 additions and 180 deletions.
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.
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.
23 changes: 16 additions & 7 deletions frontend/src/scenes/session-recordings/player/PlayerMetaLinks.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,11 @@ import {
IconTrash,
} from '@posthog/icons'
import { LemonButton, LemonButtonProps, LemonDialog, LemonMenu, LemonMenuItems } from '@posthog/lemon-ui'
import clsx from 'clsx'
import { useActions, useValues } from 'kea'
import { FEATURE_FLAGS } from 'lib/constants'
import { useFeatureFlag } from 'lib/hooks/useFeatureFlag'
import { IconComment } from 'lib/lemon-ui/icons'
import { IconComment, IconFullScreen } from 'lib/lemon-ui/icons'
import { featureFlagLogic } from 'lib/logic/featureFlagLogic'
import { Fragment } from 'react'
import { useNotebookNode } from 'scenes/notebooks/Nodes/NotebookNodeContext'
Expand Down Expand Up @@ -78,8 +79,9 @@ function PinToPlaylistButton({
}

export function PlayerMetaLinks({ iconsOnly }: { iconsOnly: boolean }): JSX.Element {
const { sessionRecordingId, logicProps } = useValues(sessionRecordingPlayerLogic)
const { sessionRecordingId, logicProps, isFullScreen } = useValues(sessionRecordingPlayerLogic)
const { setPause, setIsFullScreen } = useActions(sessionRecordingPlayerLogic)

const nodeLogic = useNotebookNode()
const { closeSessionPlayer } = useActions(sessionPlayerModalLogic())

Expand Down Expand Up @@ -112,6 +114,11 @@ export function PlayerMetaLinks({ iconsOnly }: { iconsOnly: boolean }): JSX.Elem
<div className="flex">
{![SessionRecordingPlayerMode.Sharing].includes(mode) ? (
<>
{sessionRecordingId && (
<div className="flex items-center gap-0.5">
{mode === SessionRecordingPlayerMode.Standard && <MenuActions />}
</div>
)}
<NotebookSelectButton
{...commonProps}
icon={<IconComment />}
Expand Down Expand Up @@ -159,11 +166,13 @@ export function PlayerMetaLinks({ iconsOnly }: { iconsOnly: boolean }): JSX.Elem

<PinToPlaylistButton buttonContent={buttonContent} {...commonProps} />

{sessionRecordingId && (
<div className="flex items-center gap-0.5">
{mode === SessionRecordingPlayerMode.Standard && <MenuActions />}
</div>
)}
<LemonButton
size="small"
onClick={() => setIsFullScreen(!isFullScreen)}
tooltip={`${!isFullScreen ? 'Go' : 'Exit'} full screen (F)`}
>
<IconFullScreen className={clsx('text-2xl', isFullScreen ? 'text-link' : 'text-primary-alt')} />
</LemonButton>
</>
) : null}
</div>
Expand Down
175 changes: 81 additions & 94 deletions frontend/src/scenes/session-recordings/player/PlayerSettings.tsx
Original file line number Diff line number Diff line change
@@ -1,109 +1,96 @@
import { IconEllipsis, IconFastForward } from '@posthog/icons'
import { IconFastForward, IconGear } from '@posthog/icons'
import { LemonButton, LemonMenu, LemonSelect, LemonSwitch } from '@posthog/lemon-ui'
import clsx from 'clsx'
import { useActions, useValues } from 'kea'

import { playerSettingsLogic } from './playerSettingsLogic'
import { PLAYBACK_SPEEDS } from './sessionRecordingPlayerLogic'

export const PlayerSettings = (): JSX.Element => {
const { speed, autoplayDirection, skipInactivitySetting, showMouseTail, showSeekbarTicks } =
useValues(playerSettingsLogic)
const { setSpeed, setAutoplayDirection, setSkipInactivitySetting, setShowMouseTail, setShowSeekbarTicks } =
const { autoplayDirection, skipInactivitySetting, showMouseTail, showSeekbarTicks } = useValues(playerSettingsLogic)
const { setAutoplayDirection, setSkipInactivitySetting, setShowMouseTail, setShowSeekbarTicks } =
useActions(playerSettingsLogic)

return (
<div className="pl-2">
<LemonMenu
closeOnClickInside={false}
items={[
{
custom: true,
label: () => (
<div className="flex flex-row items-center justify-between space-x-2 p-1 pl-1 min-w-64">
<span className="text-black font-medium">Autoplay</span>
<LemonMenu
closeOnClickInside={false}
items={[
{
custom: true,
label: () => (
<div className="flex flex-row items-center justify-between space-x-2 p-1 pl-1 min-w-64">
<span className="text-black font-medium">Autoplay</span>

<LemonSelect
tooltip={
<div className="text-center">
Autoplay next recording
<br />({!autoplayDirection ? 'off' : autoplayDirection})
</div>
}
value={autoplayDirection}
aria-label="Autoplay next recording"
onChange={setAutoplayDirection}
dropdownPlacement="bottom-end"
dropdownMatchSelectWidth={false}
options={[
{ value: null, label: 'Off' },
{ value: 'newer', label: 'Newer recordings' },
{ value: 'older', label: 'Older recordings' },
]}
size="small"
/>
</div>
),
},
{
custom: true,
label: () => (
<LemonSwitch
className="px-2 py-1"
checked={showMouseTail}
onChange={setShowMouseTail}
label="Show mouse tail"
fullWidth
/>
),
},
{
custom: true,
label: () => (
<LemonSwitch
className="px-2 py-1"
checked={showSeekbarTicks}
onChange={setShowSeekbarTicks}
label="Seekbar ticks"
tooltip="Show $pageview and $screen events on the seekbar"
fullWidth
/>
),
},
{
custom: true,
label: () => (
<LemonSwitch
handleContent={
<IconFastForward
className={clsx(
'p-0.5',
skipInactivitySetting ? 'text-primary-3000' : 'text-border-bold'
)}
/>
<LemonSelect
tooltip={
<div className="text-center">
Autoplay next recording
<br />({!autoplayDirection ? 'off' : autoplayDirection})
</div>
}
fullWidth
data-attr="skip-inactivity"
className="px-2 py-1"
checked={skipInactivitySetting}
onChange={setSkipInactivitySetting}
label="Skip inactivity"
value={autoplayDirection}
aria-label="Autoplay next recording"
onChange={setAutoplayDirection}
dropdownPlacement="bottom-end"
dropdownMatchSelectWidth={false}
options={[
{ value: null, label: 'Off' },
{ value: 'newer', label: 'Newer recordings' },
{ value: 'older', label: 'Older recordings' },
]}
size="small"
/>
),
},
{
label: `Playback speed (${speed}x)`,
items: PLAYBACK_SPEEDS.map((speedToggle) => ({
label: `${speedToggle}x`,
onClick: () => setSpeed(speedToggle),
active: speedToggle === speed,
})),
placement: 'right-end',
},
]}
>
<LemonButton size="small" icon={<IconEllipsis />} />
</LemonMenu>
</div>
</div>
),
},
{
custom: true,
label: () => (
<LemonSwitch
className="px-2 py-1"
checked={showMouseTail}
onChange={setShowMouseTail}
label="Show mouse tail"
fullWidth
/>
),
},
{
custom: true,
label: () => (
<LemonSwitch
className="px-2 py-1"
checked={showSeekbarTicks}
onChange={setShowSeekbarTicks}
label="Seekbar ticks"
tooltip="Show $pageview and $screen events on the seekbar"
fullWidth
/>
),
},
{
custom: true,
label: () => (
<LemonSwitch
handleContent={
<IconFastForward
className={clsx(
'p-0.5',
skipInactivitySetting ? 'text-primary-3000' : 'text-border-bold'
)}
/>
}
fullWidth
data-attr="skip-inactivity"
className="px-2 py-1"
checked={skipInactivitySetting}
onChange={setSkipInactivitySetting}
label="Skip inactivity"
/>
),
},
]}
>
<LemonButton size="small" icon={<IconGear />} />
</LemonMenu>
)
}
36 changes: 17 additions & 19 deletions frontend/src/scenes/session-recordings/player/PlayerSidebar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,28 +9,22 @@ import { featureFlagLogic } from 'lib/logic/featureFlagLogic'
import { capitalizeFirstLetter } from 'lib/utils'
import { useRef } from 'react'

import { SessionRecordingSidebarTab } from '~/types'
import { SessionRecordingSidebarStacking, SessionRecordingSidebarTab } from '~/types'

import { TabToIcon } from './inspector/PlayerInspectorControls'
import { PlayerPersonMeta } from './PlayerPersonMeta'
import { playerSettingsLogic } from './playerSettingsLogic'
import { playerSidebarLogic } from './sidebar/playerSidebarLogic'
import { PlayerSidebarTab } from './sidebar/PlayerSidebarTab'

export function PlayerSidebar({
isVerticallyStacked,
toggleLayoutStacking,
}: {
isVerticallyStacked: boolean
toggleLayoutStacking?: () => void
}): JSX.Element {
export function PlayerSidebar(): JSX.Element {
const ref = useRef<HTMLDivElement>(null)

const { featureFlags } = useValues(featureFlagLogic)
const { activeTab } = useValues(playerSidebarLogic)
const { setTab } = useActions(playerSidebarLogic)
const { sidebarOpen } = useValues(playerSettingsLogic)
const { setSidebarOpen } = useActions(playerSettingsLogic)
const { sidebarOpen, preferredSidebarStacking, isVerticallyStacked } = useValues(playerSettingsLogic)
const { setSidebarOpen, setPreferredSidebarStacking } = useActions(playerSettingsLogic)

const logicKey = `player-sidebar-${isVerticallyStacked ? 'vertical' : 'horizontal'}`

Expand Down Expand Up @@ -87,15 +81,19 @@ export function PlayerSidebar({
barClassName="mb-0"
/>
<div className="flex flex-1 border-b shrink-0" />
<div className="flex gap-1 border-b px-1 items-center">
{toggleLayoutStacking && (
<LemonButton
size="small"
icon={isVerticallyStacked ? <IconSidePanel /> : <IconBottomPanel />}
onClick={toggleLayoutStacking}
tooltip={`Dock to ${isVerticallyStacked ? 'right' : 'bottom'}`}
/>
)}
<div className="flex gap-1 border-b end">
<LemonButton
size="small"
icon={isVerticallyStacked ? <IconSidePanel /> : <IconBottomPanel />}
onClick={() =>
setPreferredSidebarStacking(
preferredSidebarStacking === SessionRecordingSidebarStacking.Vertical
? SessionRecordingSidebarStacking.Horizontal
: SessionRecordingSidebarStacking.Vertical
)
}
tooltip={`Dock to ${isVerticallyStacked ? 'right' : 'bottom'}`}
/>
<LemonButton
size="small"
icon={<IconX />}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,6 @@ import { RecordingNotFound } from 'scenes/session-recordings/player/RecordingNot
import { MatchingEventsMatchType } from 'scenes/session-recordings/playlist/sessionRecordingsPlaylistLogic'
import { urls } from 'scenes/urls'

import { SessionRecordingSidebarStacking } from '~/types'

import { NetworkView } from '../apm/NetworkView'
import { PlayerController } from './controller/PlayerController'
import { PlayerFrame } from './PlayerFrame'
Expand Down Expand Up @@ -95,8 +93,7 @@ export function SessionRecordingPlayer(props: SessionRecordingPlayerProps): JSX.
sessionRecordingPlayerLogic(logicProps)
)
const speedHotkeys = useMemo(() => createPlaybackSpeedKey(setSpeed), [setSpeed])
const { preferredSidebarStacking, sidebarOpen, playbackMode } = useValues(playerSettingsLogic)
const { setPreferredSidebarStacking } = useActions(playerSettingsLogic)
const { isVerticallyStacked, sidebarOpen, playbackMode } = useValues(playerSettingsLogic)

useKeyboardHotkeys(
{
Expand Down Expand Up @@ -160,10 +157,6 @@ export function SessionRecordingPlayer(props: SessionRecordingPlayerProps): JSX.
}
)

const compactLayout = size === 'small'
const layoutStacking = compactLayout ? SessionRecordingSidebarStacking.Vertical : preferredSidebarStacking
const isVerticallyStacked = layoutStacking === SessionRecordingSidebarStacking.Vertical

const lessThanFiveMinutesOld = dayjs().diff(start, 'minute') <= 5
const cannotPlayback = snapshotsInvalid && lessThanFiveMinutesOld && !messageTooLargeWarnings

Expand Down Expand Up @@ -239,22 +232,7 @@ export function SessionRecordingPlayer(props: SessionRecordingPlayerProps): JSX.
)}
</div>

{!noInspector && (
<PlayerSidebar
isVerticallyStacked={isVerticallyStacked}
toggleLayoutStacking={
compactLayout
? undefined
: () =>
setPreferredSidebarStacking(
preferredSidebarStacking ===
SessionRecordingSidebarStacking.Vertical
? SessionRecordingSidebarStacking.Horizontal
: SessionRecordingSidebarStacking.Vertical
)
}
/>
)}
{!noInspector && <PlayerSidebar />}
</>
)}
</FloatingContainerContext.Provider>
Expand Down
Loading

0 comments on commit cbc5afd

Please sign in to comment.