diff --git a/frontend/__snapshots__/replay-player-failure--recent-recordings-404--dark.png b/frontend/__snapshots__/replay-player-failure--recent-recordings-404--dark.png index 4338f4712a54a..2fe69211c78a0 100644 Binary files a/frontend/__snapshots__/replay-player-failure--recent-recordings-404--dark.png and b/frontend/__snapshots__/replay-player-failure--recent-recordings-404--dark.png differ diff --git a/frontend/__snapshots__/replay-player-failure--recent-recordings-404--light.png b/frontend/__snapshots__/replay-player-failure--recent-recordings-404--light.png index 2b3cade561e6f..96602a314f7aa 100644 Binary files a/frontend/__snapshots__/replay-player-failure--recent-recordings-404--light.png and b/frontend/__snapshots__/replay-player-failure--recent-recordings-404--light.png differ diff --git a/frontend/__snapshots__/scenes-app-notebooks--recordings-playlist--dark.png b/frontend/__snapshots__/scenes-app-notebooks--recordings-playlist--dark.png index 592abd071d8f9..54b5fe58b05f5 100644 Binary files a/frontend/__snapshots__/scenes-app-notebooks--recordings-playlist--dark.png and b/frontend/__snapshots__/scenes-app-notebooks--recordings-playlist--dark.png differ diff --git a/frontend/__snapshots__/scenes-app-notebooks--recordings-playlist--light.png b/frontend/__snapshots__/scenes-app-notebooks--recordings-playlist--light.png index 44912f5aa119f..6588de25fb805 100644 Binary files a/frontend/__snapshots__/scenes-app-notebooks--recordings-playlist--light.png and b/frontend/__snapshots__/scenes-app-notebooks--recordings-playlist--light.png differ diff --git a/frontend/src/scenes/session-recordings/player/PlayerMeta.tsx b/frontend/src/scenes/session-recordings/player/PlayerMeta.tsx index 70be8a2abb3c7..56c69f0df5d03 100644 --- a/frontend/src/scenes/session-recordings/player/PlayerMeta.tsx +++ b/frontend/src/scenes/session-recordings/player/PlayerMeta.tsx @@ -1,7 +1,6 @@ import './PlayerMeta.scss' -import { IconEllipsis, IconTrash } from '@posthog/icons' -import { IconDownload, IconMagic, IconSearch } from '@posthog/icons' +import { IconDownload, IconEllipsis, IconMagic, IconSearch, IconTrash } from '@posthog/icons' import { LemonButton, LemonDialog, LemonMenu, LemonMenuItems, Link } from '@posthog/lemon-ui' import clsx from 'clsx' import { useActions, useValues } from 'kea' @@ -68,7 +67,7 @@ function URLOrScreen({ lastUrl }: { lastUrl: string | undefined }): JSX.Element ) } -export function PlayerMeta(): JSX.Element { +export function PlayerMeta({ linkIconsOnly = false }: { linkIconsOnly?: boolean }): JSX.Element { const { sessionRecordingId, logicProps, isFullScreen } = useValues(sessionRecordingPlayerLogic) const { @@ -181,10 +180,10 @@ export function PlayerMeta(): JSX.Element { {sessionRecordingId && ( - <> - +
+ {mode === SessionRecordingPlayerMode.Standard && } - +
)}
JSX.Element + buttonProps?: Partial +}): JSX.Element { const { logicProps } = useValues(sessionRecordingPlayerLogic) const { maybePersistRecording } = useActions(sessionRecordingPlayerLogic) const nodeLogic = useNotebookNode() @@ -34,6 +41,7 @@ function PinToPlaylistButton(): JSX.Element { return logicProps.setPinned ? ( { if (nodeLogic && !logicProps.pinned) { // If we are in a node, then pinning should persist the recording @@ -42,19 +50,16 @@ function PinToPlaylistButton(): JSX.Element { logicProps.setPinned?.(!logicProps.pinned) }} - size="small" tooltip={tooltip} data-attr={logicProps.pinned ? 'unpin-from-this-list' : 'pin-to-this-list'} icon={logicProps.pinned ? : } /> ) : ( - - {description} - + {buttonContent(description)} ) } -export function PlayerMetaLinks(): JSX.Element { +export function PlayerMetaLinks({ iconsOnly }: { iconsOnly: boolean }): JSX.Element { const { sessionRecordingId, logicProps } = useValues(sessionRecordingPlayerLogic) const { setPause, setIsFullScreen } = useActions(sessionRecordingPlayerLogic) const nodeLogic = useNotebookNode() @@ -79,14 +84,18 @@ export function PlayerMetaLinks(): JSX.Element { size: 'small', } + const buttonContent = (label: string): JSX.Element => { + return !iconsOnly ? {label} : + } + const mode = logicProps.mode ?? SessionRecordingPlayerMode.Standard return ( -
+ <> {![SessionRecordingPlayerMode.Sharing].includes(mode) ? ( <> } resource={{ type: NotebookNodeType.Recording, @@ -111,17 +120,17 @@ export function PlayerMetaLinks(): JSX.Element { personsModalLogic.findMounted()?.actions.closeModal() }} > - Comment + {buttonContent('Comment')} - } onClick={onShare} {...commonProps}> - Share + } onClick={onShare} {...commonProps}> + {buttonContent('Share')} {nodeLogic?.props.nodeType === NotebookNodeType.RecordingPlaylist ? ( } - size="small" onClick={() => { nodeLogic.actions.insertAfter({ type: NotebookNodeType.Recording, @@ -131,9 +140,9 @@ export function PlayerMetaLinks(): JSX.Element { /> ) : null} - + ) : null} -
+ ) } diff --git a/frontend/src/scenes/session-recordings/player/SessionRecordingPlayer.scss b/frontend/src/scenes/session-recordings/player/SessionRecordingPlayer.scss index 2f3bcac6708f3..5284d90c65570 100644 --- a/frontend/src/scenes/session-recordings/player/SessionRecordingPlayer.scss +++ b/frontend/src/scenes/session-recordings/player/SessionRecordingPlayer.scss @@ -1,8 +1,6 @@ @import '../../../styles/mixins'; .SessionRecordingPlayer { - --inspector-min-width: 24rem; - position: relative; display: flex; flex-direction: row; @@ -29,7 +27,6 @@ .SessionRecordingPlayer__main { flex: 1; - padding-right: 2.5rem; } &--fullscreen { @@ -62,46 +59,24 @@ } .SessionRecordingPlayer__inspector { - position: absolute; - top: 0; - right: 0; - bottom: 0; - z-index: 10; + position: relative; flex-shrink: 0; - min-width: var(--inspector-min-width); - max-width: 95%; - - &--collapsed { - --inspector-min-width: 2.5rem; - } - - .PlayerInspectorPreview { - position: absolute; - inset: 0; - z-index: 1; - cursor: pointer; - transition: opacity 0.2s ease-in-out; - } + min-width: 20rem; + max-width: 50%; } - &--widescreen { - .SessionRecordingPlayer__main { - padding-right: 0; - } - + &--wide { .SessionRecordingPlayer__inspector { - position: relative; - max-width: 75%; + min-width: 26rem; } } - &--inspector-hidden { - .SessionRecordingPlayer__main { - padding-right: 0; - } + &--stacked-vertically { + flex-direction: column; .SessionRecordingPlayer__inspector { - display: none; + min-width: 100%; + max-width: 100%; } } } diff --git a/frontend/src/scenes/session-recordings/player/SessionRecordingPlayer.tsx b/frontend/src/scenes/session-recordings/player/SessionRecordingPlayer.tsx index bde9dc7accee6..429fcc832216d 100644 --- a/frontend/src/scenes/session-recordings/player/SessionRecordingPlayer.tsx +++ b/frontend/src/scenes/session-recordings/player/SessionRecordingPlayer.tsx @@ -7,7 +7,7 @@ import { HotkeysInterface, useKeyboardHotkeys } from 'lib/hooks/useKeyboardHotke import { usePageVisibility } from 'lib/hooks/usePageVisibility' import { useResizeBreakpoints } from 'lib/hooks/useResizeObserver' import { LemonDivider } from 'lib/lemon-ui/LemonDivider' -import { useEffect, useMemo, useRef, useState } from 'react' +import { useMemo, useRef, useState } from 'react' import { useNotebookDrag } from 'scenes/notebooks/AddToNotebook/DraggableToNotebook' import { PlayerController } from 'scenes/session-recordings/player/controller/PlayerController' import { PlayerInspector } from 'scenes/session-recordings/player/inspector/PlayerInspector' @@ -35,6 +35,11 @@ export interface SessionRecordingPlayerProps extends SessionRecordingPlayerLogic matchingEventsMatchType?: MatchingEventsMatchType } +enum InspectorStacking { + Vertical = 'vertical', + Horizontal = 'horizontal', +} + export const createPlaybackSpeedKey = (action: (val: number) => void): HotkeysInterface => { return PLAYBACK_SPEEDS.map((x, i) => ({ key: `${i}`, value: x })).reduce( (acc, x) => ({ ...acc, [x.key]: { action: () => action(x.value) } }), @@ -59,6 +64,7 @@ export function SessionRecordingPlayer(props: SessionRecordingPlayerProps): JSX. } = props const playerRef = useRef(null) + const playerMainRef = useRef(null) const logicProps: SessionRecordingPlayerLogicProps = { sessionRecordingId, @@ -130,26 +136,34 @@ export function SessionRecordingPlayer(props: SessionRecordingPlayerProps): JSX. const { size } = useResizeBreakpoints( { - 0: 'tiny', - 400: 'small', - 1000: 'medium', + 0: 'small', + 1050: 'medium', + 1500: 'wide', }, { ref: playerRef, } ) + const { size: playerMainSize } = useResizeBreakpoints( + { + 0: 'small', + 650: 'medium', + }, + { + ref: playerMainRef, + } + ) - const isWidescreen = !isFullScreen && size === 'medium' + const isWidescreen = !isFullScreen && size === 'wide' const [inspectorExpanded, setInspectorExpanded] = useState(isWidescreen) + const [preferredInspectorStacking, setPreferredInspectorStacking] = useState(InspectorStacking.Horizontal) - const { draggable, elementProps } = useNotebookDrag({ href: urls.replaySingle(sessionRecordingId) }) + const compactLayout = size === 'small' + const layoutStacking = compactLayout ? InspectorStacking.Vertical : preferredInspectorStacking + const isVerticallyStacked = layoutStacking === InspectorStacking.Vertical - useEffect(() => { - if (isWidescreen) { - setInspectorExpanded(true) - } - }, [isWidescreen]) + const { draggable, elementProps } = useNotebookDrag({ href: urls.replaySingle(sessionRecordingId) }) if (isNotFound) { return ( @@ -163,14 +177,16 @@ export function SessionRecordingPlayer(props: SessionRecordingPlayerProps): JSX.
@@ -178,27 +194,35 @@ export function SessionRecordingPlayer(props: SessionRecordingPlayerProps): JSX. closeExplorer()} /> ) : ( <> -
{ - if (!isWidescreen) { - setInspectorExpanded(false) - } - }} - > - {(!noMeta || isFullScreen) && size !== 'tiny' ? : null} +
+ {!noMeta || isFullScreen ? ( + + ) : null}
- + setInspectorExpanded(!inspectorExpanded)} + />
- {!noInspector && ( + {!noInspector && inspectorExpanded && ( + setPreferredInspectorStacking( + preferredInspectorStacking === InspectorStacking.Vertical + ? InspectorStacking.Horizontal + : InspectorStacking.Vertical + ) + } /> )} diff --git a/frontend/src/scenes/session-recordings/player/controller/PlayerController.tsx b/frontend/src/scenes/session-recordings/player/controller/PlayerController.tsx index 98b391ec7d780..a186863703b66 100644 --- a/frontend/src/scenes/session-recordings/player/controller/PlayerController.tsx +++ b/frontend/src/scenes/session-recordings/player/controller/PlayerController.tsx @@ -1,4 +1,4 @@ -import { IconFastForward, IconPause, IconPlay } from '@posthog/icons' +import { IconFastForward, IconPause, IconPlay, IconSearch } from '@posthog/icons' import { LemonMenu, LemonSwitch } from '@posthog/lemon-ui' import clsx from 'clsx' import { useActions, useValues } from 'kea' @@ -17,7 +17,13 @@ import { playerSettingsLogic } from '../playerSettingsLogic' import { SeekSkip, Timestamp } from './PlayerControllerTime' import { Seekbar } from './Seekbar' -export function PlayerController(): JSX.Element { +export function PlayerController({ + inspectorExpanded, + toggleInspectorExpanded, +}: { + inspectorExpanded: boolean + toggleInspectorExpanded: () => void +}): JSX.Element { const { playingState, isFullScreen, endReached } = useValues(sessionRecordingPlayerLogic) const { togglePlayPause, setIsFullScreen } = useActions(sessionRecordingPlayerLogic) @@ -64,8 +70,6 @@ export function PlayerController(): JSX.Element { {speed}x -
-
+
+ + setIsFullScreen(!isFullScreen)}> + + + +
-
- - { - setIsFullScreen(!isFullScreen) - }} - > - - - -
+ {!inspectorExpanded && ( + } + > + Inspector + + )}
) diff --git a/frontend/src/scenes/session-recordings/player/inspector/PlayerInspector.tsx b/frontend/src/scenes/session-recordings/player/inspector/PlayerInspector.tsx index 7b7f1454d453d..b644a2fb987b0 100644 --- a/frontend/src/scenes/session-recordings/player/inspector/PlayerInspector.tsx +++ b/frontend/src/scenes/session-recordings/player/inspector/PlayerInspector.tsx @@ -6,49 +6,50 @@ import { useRef } from 'react' import { PlayerInspectorControls } from './PlayerInspectorControls' import { PlayerInspectorList } from './PlayerInspectorList' -import { PlayerInspectorPreview } from './PlayerInspectorPreview' export function PlayerInspector({ - inspectorExpanded, - setInspectorExpanded, + isVerticallyStacked, + onClose, + toggleLayoutStacking, }: { - inspectorExpanded: boolean - setInspectorExpanded: (focus: boolean) => void + isVerticallyStacked: boolean + onClose: (focus: boolean) => void + toggleLayoutStacking?: () => void }): JSX.Element { const ref = useRef(null) + const logicKey = `player-inspector-${isVerticallyStacked ? 'vertical' : 'horizontal'}` + const resizerLogicProps: ResizerLogicProps = { + logicKey, containerRef: ref, - logicKey: 'player-inspector', persistent: true, closeThreshold: 100, - placement: 'left', - onToggleClosed: (shouldBeClosed) => setInspectorExpanded(!shouldBeClosed), + placement: isVerticallyStacked ? 'top' : 'left', + onToggleClosed: (shouldBeClosed) => onClose(!shouldBeClosed), } const { desiredSize } = useValues(resizerLogic(resizerLogicProps)) return (
- - {inspectorExpanded ? ( - <> - setInspectorExpanded(false)} /> - - - ) : ( - setInspectorExpanded(true)} /> - )} + + onClose(false)} + isVerticallyStacked={isVerticallyStacked} + toggleLayoutStacking={toggleLayoutStacking} + /> +
) } diff --git a/frontend/src/scenes/session-recordings/player/inspector/PlayerInspectorControls.tsx b/frontend/src/scenes/session-recordings/player/inspector/PlayerInspectorControls.tsx index 344f520648255..3b261aa18fb7a 100644 --- a/frontend/src/scenes/session-recordings/player/inspector/PlayerInspectorControls.tsx +++ b/frontend/src/scenes/session-recordings/player/inspector/PlayerInspectorControls.tsx @@ -1,4 +1,4 @@ -import { IconBug, IconDashboard, IconInfo, IconTerminal, IconX } from '@posthog/icons' +import { IconBottomPanel, IconBug, IconDashboard, IconInfo, IconSidePanel, IconTerminal, IconX } from '@posthog/icons' import { LemonButton, LemonCheckbox, LemonInput, LemonSelect, LemonTabs, Tooltip } from '@posthog/lemon-ui' import { useActions, useValues } from 'kea' import { FEATURE_FLAGS } from 'lib/constants' @@ -45,7 +45,6 @@ function TabButtons({ onChange={(tabId) => setTab(tabId)} tabs={tabs.map((tabId) => { const TabIcon = TabToIcon[tabId] - return { key: tabId, label: ( @@ -66,7 +65,15 @@ function TabButtons({ ) } -export function PlayerInspectorControls({ onClose }: { onClose: () => void }): JSX.Element { +export function PlayerInspectorControls({ + onClose, + isVerticallyStacked, + toggleLayoutStacking, +}: { + onClose: () => void + isVerticallyStacked: boolean + toggleLayoutStacking?: () => void +}): JSX.Element { const { logicProps } = useValues(sessionRecordingPlayerLogic) const inspectorLogic = playerInspectorLogic(logicProps) const { tab, windowIdFilter, windowIds, showMatchingEventsFilter } = useValues(inspectorLogic) @@ -105,7 +112,14 @@ export function PlayerInspectorControls({ onClose }: { onClose: () => void }): J
-
+
+ {toggleLayoutStacking && ( + : } + onClick={toggleLayoutStacking} + /> + )} } onClick={onClose} />
diff --git a/frontend/src/scenes/session-recordings/player/inspector/PlayerInspectorPreview.tsx b/frontend/src/scenes/session-recordings/player/inspector/PlayerInspectorPreview.tsx deleted file mode 100644 index 0af3441533745..0000000000000 --- a/frontend/src/scenes/session-recordings/player/inspector/PlayerInspectorPreview.tsx +++ /dev/null @@ -1,49 +0,0 @@ -import { IconDashboard, IconSearch, IconTerminal } from '@posthog/icons' -import clsx from 'clsx' -import { useValues } from 'kea' -import { IconUnverifiedEvent } from 'lib/lemon-ui/icons' - -import { SessionRecordingPlayerTab } from '~/types' - -import { sessionRecordingPlayerLogic } from '../sessionRecordingPlayerLogic' -import { playerInspectorLogic } from './playerInspectorLogic' - -const TabToIcon = { - [SessionRecordingPlayerTab.ALL]: IconSearch, - [SessionRecordingPlayerTab.EVENTS]: IconUnverifiedEvent, - [SessionRecordingPlayerTab.CONSOLE]: IconTerminal, - [SessionRecordingPlayerTab.NETWORK]: IconDashboard, -} - -export function PlayerInspectorPreview(props: { onClick: () => void }): JSX.Element { - const { logicProps } = useValues(sessionRecordingPlayerLogic) - const inspectorLogic = playerInspectorLogic(logicProps) - - const { tab } = useValues(inspectorLogic) - - const tabs = [ - SessionRecordingPlayerTab.ALL, - SessionRecordingPlayerTab.EVENTS, - SessionRecordingPlayerTab.CONSOLE, - SessionRecordingPlayerTab.NETWORK, - ] - - return ( -
- {tabs.map((tabId) => { - const TabIcon = TabToIcon[tabId] - return ( -
- -
- ) - })} -
- ) -} diff --git a/frontend/src/scenes/session-recordings/player/playlist-popover/PlaylistPopover.tsx b/frontend/src/scenes/session-recordings/player/playlist-popover/PlaylistPopover.tsx index fe58d622bbf0d..bc504ee0dde4c 100644 --- a/frontend/src/scenes/session-recordings/player/playlist-popover/PlaylistPopover.tsx +++ b/frontend/src/scenes/session-recordings/player/playlist-popover/PlaylistPopover.tsx @@ -1,4 +1,4 @@ -import { IconPlus } from '@posthog/icons' +import { IconPin, IconPlus } from '@posthog/icons' import { LemonCheckbox, LemonDivider } from '@posthog/lemon-ui' import { useActions, useValues } from 'kea' import { Form } from 'kea-forms' @@ -119,7 +119,7 @@ export function PlaylistPopoverButton(props: LemonButtonProps): JSX.Element { } > } + icon={} active={showPlaylistPopover} onClick={() => setShowPlaylistPopover(!showPlaylistPopover)} sideIcon={null} diff --git a/frontend/src/scenes/session-recordings/playlist/SessionRecordingsPlaylist.scss b/frontend/src/scenes/session-recordings/playlist/SessionRecordingsPlaylist.scss index ea0f5d0d4fc07..ed2f90dc11186 100644 --- a/frontend/src/scenes/session-recordings/playlist/SessionRecordingsPlaylist.scss +++ b/frontend/src/scenes/session-recordings/playlist/SessionRecordingsPlaylist.scss @@ -12,15 +12,19 @@ border-radius: var(--radius); .SessionRecordingsPlaylist__list { + position: relative; display: flex; flex-direction: column; flex-shrink: 0; - width: 25%; - min-width: 300px; - max-width: 350px; height: 100%; overflow: hidden; + &:not(.SessionRecordingsPlaylist__list--collapsed) { + width: 25%; + min-width: 305px; + max-width: 350px; + } + .text-link { color: var(--default); } diff --git a/frontend/src/scenes/session-recordings/playlist/SessionRecordingsPlaylist.tsx b/frontend/src/scenes/session-recordings/playlist/SessionRecordingsPlaylist.tsx index 8b8e1c151d630..025a50b38c45d 100644 --- a/frontend/src/scenes/session-recordings/playlist/SessionRecordingsPlaylist.tsx +++ b/frontend/src/scenes/session-recordings/playlist/SessionRecordingsPlaylist.tsx @@ -1,15 +1,16 @@ import './SessionRecordingsPlaylist.scss' -import { IconFilter, IconGear } from '@posthog/icons' +import { IconCollapse, IconFilter, IconGear } from '@posthog/icons' import { LemonButton, Link } from '@posthog/lemon-ui' import clsx from 'clsx' import { range } from 'd3' import { BindLogic, useActions, useValues } from 'kea' import { EmptyMessage } from 'lib/components/EmptyMessage/EmptyMessage' import { PropertyKeyInfo } from 'lib/components/PropertyKeyInfo' +import { Resizer } from 'lib/components/Resizer/Resizer' import { FEATURE_FLAGS } from 'lib/constants' import { useResizeBreakpoints } from 'lib/hooks/useResizeObserver' -import { IconWithCount } from 'lib/lemon-ui/icons' +import { IconChevronRight, IconWithCount } from 'lib/lemon-ui/icons' import { LemonBanner } from 'lib/lemon-ui/LemonBanner' import { LemonTableLoader } from 'lib/lemon-ui/LemonTable/LemonTableLoader' import { Spinner } from 'lib/lemon-ui/Spinner' @@ -115,6 +116,7 @@ function RecordingsLists(): JSX.Element { logicProps, showOtherRecordings, recordingsCount, + isRecordingsListCollapsed, } = useValues(sessionRecordingsPlaylistLogic) const { setSelectedRecordingId, @@ -125,6 +127,7 @@ function RecordingsLists(): JSX.Element { setShowSettings, resetFilters, toggleShowOtherRecordings, + toggleRecordingsListCollapsed, } = useActions(sessionRecordingsPlaylistLogic) const onRecordingClick = (recording: SessionRecordingType): void => { @@ -161,11 +164,20 @@ function RecordingsLists(): JSX.Element { const notebookNode = useNotebookNode() - return ( -
+ return isRecordingsListCollapsed ? ( +
+ } onClick={() => toggleRecordingsListCollapsed()} /> +
+ ) : ( +
- + } + onClick={() => toggleRecordingsListCollapsed()} + /> + {!notebookNode ? ( Recordings @@ -324,9 +336,16 @@ export function SessionRecordingsPlaylist(props: SessionRecordingPlaylistLogicPr ...props, autoPlay: props.autoPlay ?? true, } + const playlistRecordingsListRef = useRef(null) const logic = sessionRecordingsPlaylistLogic(logicProps) - const { activeSessionRecording, activeSessionRecordingId, matchingEventsMatchType, pinnedRecordings } = - useValues(logic) + const { + activeSessionRecording, + activeSessionRecordingId, + matchingEventsMatchType, + pinnedRecordings, + isRecordingsListCollapsed, + } = useValues(logic) + const { toggleRecordingsListCollapsed } = useActions(logic) const { ref: playlistRef, size } = useResizeBreakpoints({ 0: 'small', @@ -346,8 +365,22 @@ export function SessionRecordingsPlaylist(props: SessionRecordingPlaylistLogicPr 'SessionRecordingsPlaylist--embedded': notebookNode, })} > -
+
+ toggleRecordingsListCollapsed(shouldBeClosed)} + onDoubleClick={() => toggleRecordingsListCollapsed()} + />
{activeSessionRecordingId ? ( diff --git a/frontend/src/scenes/session-recordings/playlist/sessionRecordingsPlaylistLogic.ts b/frontend/src/scenes/session-recordings/playlist/sessionRecordingsPlaylistLogic.ts index 5a12f9018eba7..b54aa53aa311b 100644 --- a/frontend/src/scenes/session-recordings/playlist/sessionRecordingsPlaylistLogic.ts +++ b/frontend/src/scenes/session-recordings/playlist/sessionRecordingsPlaylistLogic.ts @@ -161,6 +161,7 @@ export const sessionRecordingsPlaylistLogic = kea ({ show }), + toggleRecordingsListCollapsed: (override?: boolean) => ({ override }), }), propsChanged(({ actions, props }, oldProps) => { if (!objectsEqual(props.advancedFilters, oldProps.advancedFilters)) { @@ -426,6 +427,13 @@ export const sessionRecordingsPlaylistLogic = kea false, }, ], + isRecordingsListCollapsed: [ + false, + { persist: true }, + { + toggleRecordingsListCollapsed: (state, { override }) => override ?? !state, + }, + ], })), listeners(({ props, actions, values }) => ({ loadAllRecordings: () => {