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

Improved session player navigation #2611

Merged
merged 12 commits into from
Dec 3, 2020
2 changes: 1 addition & 1 deletion frontend/src/lib/components/Drawer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { Drawer as AntDrawer } from 'antd'
import { DrawerProps } from 'antd/lib/drawer'

/**
* Ant Drawer extended to add class 'drawer-open' to <body> when drawer is out. Used to alter e.g. Papercups widget.
* Ant Drawer extended to add class 'drawer-open' to <body> when drawer is out. Used to alter Papercups widget position.
*/
export function Drawer(props: PropsWithChildren<DrawerProps>): JSX.Element {
const { visible } = props
Expand Down
6 changes: 0 additions & 6 deletions frontend/src/scenes/sceneLogic.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ export enum Scene {
Cohorts = 'cohorts',
Events = 'events',
Sessions = 'sessions',
SessionsPlay = 'sessionsPlay',
Person = 'person',
Persons = 'persons',
Action = 'action',
Expand Down Expand Up @@ -52,7 +51,6 @@ export const scenes: Record<Scene, () => any> = {
[Scene.Cohorts]: () => import(/* webpackChunkName: 'cohorts' */ './persons/Cohorts'),
[Scene.Events]: () => import(/* webpackChunkName: 'events' */ './events/Events'),
[Scene.Sessions]: () => import(/* webpackChunkName: 'sessions' */ './sessions/Sessions'),
[Scene.SessionsPlay]: () => import(/* webpackChunkName: 'sessionsPlay' */ './sessions/SessionsPlay'),
[Scene.Person]: () => import(/* webpackChunkName: 'person' */ './persons/Person'),
[Scene.Persons]: () => import(/* webpackChunkName: 'persons' */ './persons/Persons'),
[Scene.Action]: () => import(/* webpackChunkName: 'action' */ './actions/Action'),
Expand Down Expand Up @@ -104,9 +102,6 @@ export const sceneConfigurations: Partial<Record<Scene, SceneConfig>> = {
[Scene.ProjectCreateFirst]: {
plain: true,
},
[Scene.SessionsPlay]: {
plain: true,
},
}

export const redirects: Record<string, string | ((params: Params) => any)> = {
Expand All @@ -124,7 +119,6 @@ export const routes: Record<string, Scene> = {
'/events': Scene.Events,
'/events/*': Scene.Events,
'/sessions': Scene.Sessions,
'/sessions/play': Scene.SessionsPlay,
'/person_by_id/:id': Scene.Person,
'/person/*': Scene.Person,
'/persons': Scene.Persons,
Expand Down
8 changes: 0 additions & 8 deletions frontend/src/scenes/sessions/SessionsPlayerButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,21 +3,14 @@ import { PlayCircleOutlined } from '@ant-design/icons'
import { SessionType } from '~/types'
import { fromParams, toParams } from 'lib/utils'
import { Link } from 'lib/components/Link'
import { useValues } from 'kea'
import { featureFlagLogic } from 'lib/logic/featureFlagLogic'
import './Sessions.scss'

interface SessionsPlayerButtonProps {
session: SessionType
}

export default function SessionsPlayerButton({ session }: SessionsPlayerButtonProps): JSX.Element | null {
const { featureFlags } = useValues(featureFlagLogic)

const sessionPlayerUrl = (sessionRecordingId: string): string => {
if (featureFlags['full-page-player']) {
return `/sessions/play?${toParams({ id: sessionRecordingId })}`
}
return `${location.pathname}?${toParams({ ...fromParams(), sessionRecordingId })}`
}

Expand All @@ -30,7 +23,6 @@ export default function SessionsPlayerButton({ session }: SessionsPlayerButtonPr
{session.session_recording_ids.map((sessionRecordingId: string) => (
<Link
to={sessionPlayerUrl(sessionRecordingId)}
target={featureFlags['full-page-player'] ? '_blank' : undefined}
className="sessions-player-button"
key={sessionRecordingId}
onClick={(event) => event.stopPropagation()}
Expand Down
39 changes: 0 additions & 39 deletions frontend/src/scenes/sessions/SessionsPlayerDrawer.tsx

This file was deleted.

34 changes: 25 additions & 9 deletions frontend/src/scenes/sessions/SessionsTable.tsx
Original file line number Diff line number Diff line change
@@ -1,19 +1,25 @@
import React from 'react'
import { useValues, useActions } from 'kea'
import { Table, Button, Spin, Space, Tooltip } from 'antd'
import { Table, Button, Spin, Space, Tooltip, Drawer } from 'antd'
import { Link } from 'lib/components/Link'
import { sessionsTableLogic } from 'scenes/sessions/sessionsTableLogic'
import { humanFriendlyDuration, humanFriendlyDetailedTime, stripHTTP } from '~/lib/utils'
import { SessionDetails } from './SessionDetails'
import { DatePicker } from 'antd'
import moment from 'moment'
import { SessionType } from '~/types'
import { CaretLeftOutlined, CaretRightOutlined, PoweroffOutlined, QuestionCircleOutlined } from '@ant-design/icons'
import {
CaretLeftOutlined,
CaretRightOutlined,
PoweroffOutlined,
QuestionCircleOutlined,
ArrowLeftOutlined,
} from '@ant-design/icons'
import SessionsPlayerButton from './SessionsPlayerButton'
import { PropertyFilters } from 'lib/components/PropertyFilters'
import rrwebBlockClass from 'lib/utils/rrwebBlockClass'
import SessionsPlayerDrawer from 'scenes/sessions/SessionsPlayerDrawer'
import { PageHeader } from 'lib/components/PageHeader'
import { SessionsPlay } from './SessionsPlay'
import { userLogic } from 'scenes/userLogic'
import { commandPaletteLogic } from 'lib/components/CommandPalette/commandPaletteLogic'

Expand All @@ -22,6 +28,20 @@ interface SessionsTableProps {
isPersonPage?: boolean
}

function SessionPlayerDrawer({ isPersonPage = false }: { isPersonPage: boolean }): JSX.Element {
const { closeSessionPlayer } = useActions(sessionsTableLogic)
return (
<Drawer destroyOnClose visible width="100%" onClose={closeSessionPlayer}>
<>
<a onClick={closeSessionPlayer}>
<ArrowLeftOutlined /> Back to {isPersonPage ? 'persons' : 'sessions'}
</a>
<SessionsPlay />
</>
</Drawer>
)
}

export function SessionsTable({ personIds, isPersonPage = false }: SessionsTableProps): JSX.Element {
const logic = sessionsTableLogic({ personIds })
const {
Expand Down Expand Up @@ -144,11 +164,7 @@ export function SessionsTable({ personIds, isPersonPage = false }: SessionsTable
{!isPersonPage && <PageHeader title="Sessions By Day" />}
<Space className="mb-05">
<Button onClick={previousDay} icon={<CaretLeftOutlined />} />
<DatePicker
value={selectedDate}
onChange={(date) => setFilters(properties, date, sessionRecordingId)}
allowClear={false}
/>
<DatePicker value={selectedDate} onChange={(date) => setFilters(properties, date)} allowClear={false} />
<Button onClick={nextDay} icon={<CaretRightOutlined />} />
</Space>
<PropertyFilters pageKey={'sessions-' + (personIds && JSON.stringify(personIds))} />
Expand All @@ -170,7 +186,7 @@ export function SessionsTable({ personIds, isPersonPage = false }: SessionsTable
expandRowByClick: true,
}}
/>
{!!sessionRecordingId && <SessionsPlayerDrawer />}
{!!sessionRecordingId && <SessionPlayerDrawer isPersonPage={isPersonPage} />}
<div style={{ marginTop: '5rem' }} />
<div
style={{
Expand Down
6 changes: 3 additions & 3 deletions frontend/src/scenes/sessions/sessionsPlayLogic.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,9 +52,9 @@ export const sessionsPlayLogic = kea<sessionsPlayLogicType<SessionPlayerData, Ev
},
}),
urlToAction: ({ actions, values }) => ({
'*': (_: any, params: { id: string }) => {
const sessionRecordingId = params.id
if (sessionRecordingId !== values.sessionRecordingId) {
'*': (_: any, params: { sessionRecordingId: string }) => {
const sessionRecordingId = params.sessionRecordingId
if (sessionRecordingId !== values.sessionRecordingId && sessionRecordingId) {
actions.loadRecording(sessionRecordingId)
}
},
Expand Down
56 changes: 10 additions & 46 deletions frontend/src/scenes/sessions/sessionsTableLogic.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,13 +62,6 @@ export const sessionsTableLogic = kea<
return response.result
},
},
sessionPlayerData: {
loadSessionPlayer: async (sessionRecordingId: SessionRecordingId): Promise<eventWithTime[]> => {
const params = toParams({ session_recording_id: sessionRecordingId })
const response = await api.get(`api/event/session_recording?${params}`)
return response.result.snapshots
},
},
}),
actions: () => ({
setNextOffset: (nextOffset: number | null) => ({ nextOffset }),
Expand All @@ -77,8 +70,8 @@ export const sessionsTableLogic = kea<
previousDay: true,
nextDay: true,
setFilters: (properties: Array<PropertyFilter>, selectedDate: Moment | null) => ({ properties, selectedDate }),
setSessionRecordingId: (sessionRecordingId: SessionRecordingId) => ({ sessionRecordingId }),
closeSessionPlayer: true,
setPlayerSpeed: (speed: number) => ({ speed }),
}),
reducers: {
sessions: {
Expand All @@ -103,23 +96,10 @@ export const sessionsTableLogic = kea<
sessionRecordingId: [
null as SessionRecordingId | null,
{
loadSessionPlayer: (_, sessionRecordingId) => sessionRecordingId,
closeSessionPlayer: () => null,
},
],
sessionPlayerData: [
null as null | eventWithTime[],
{
setSessionRecordingId: (_, { sessionRecordingId }) => sessionRecordingId,
closeSessionPlayer: () => null,
},
],
sessionsPlayerSpeed: [
1,
{ persist: true },
{
setPlayerSpeed: (_, { speed }) => speed,
},
],
},
selectors: {
selectedDateURLparam: [(s) => [s.selectedDate], (selectedDate) => selectedDate?.format('YYYY-MM-DD')],
Expand Down Expand Up @@ -178,40 +158,24 @@ export const sessionsTableLogic = kea<
}),
actionToUrl: ({ values }) => ({
setFilters: () => buildURL(values.selectedDateURLparam, values.sessionRecordingId),
loadSessionPlayer: () => buildURL(values.selectedDateURLparam, values.sessionRecordingId),
setSessionRecordingId: () => buildURL(values.selectedDateURLparam, values.sessionRecordingId),
closeSessionPlayer: () => buildURL(values.selectedDateURLparam, null),
}),
urlToAction: ({ actions, values }) => ({
'/sessions': (_: any, params: Params) => {
'*': (_: any, params: Params) => {
const newDate = params.date ? moment(params.date).startOf('day') : moment().startOf('day')

if (JSON.stringify(params.properties || []) !== JSON.stringify(values.properties)) {
actions.setFilters(params.properties || [], newDate)
} else if (values.sessions.length === 0) {
actions.loadSessions(true)
}
if (params.sessionRecordingId !== (values.sessionRecordingId || undefined)) {
if (params.sessionRecordingId) {
actions.loadSessionPlayer(params.sessionRecordingId)
} else {
actions.closeSessionPlayer()
}
}
},
'/person/*': (_: any, params: Params) => {
const newDate = params.date ? moment(params.date).startOf('day') : moment().startOf('day')
if (
!values.selectedDate ||
values.selectedDate.format('YYYY-MM-DD') !== newDate.format('YYYY-MM-DD') ||
params.sessionRecordingId !== values.sessionRecordingId
JSON.stringify(params.properties || []) !== JSON.stringify(values.properties) ||
(values.selectedDate && values.selectedDate.format('YYYY-MM-DD') !== newDate.format('YYYY-MM-DD'))
) {
actions.setFilters(params.properties || [], newDate)
} else if (values.sessions.length === 0) {
actions.loadSessions(true)
}

if (params.sessionRecordingId) {
actions.loadSessionPlayer(params.sessionRecordingId)
} else {
actions.closeSessionPlayer()
if (params.sessionRecordingId && params.sessionRecordingId !== values.sessionRecordingId) {
actions.setSessionRecordingId(params.sessionRecordingId)
}
},
}),
Expand Down