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

fix: recording timestamps #16866

Merged
merged 6 commits into from
Aug 2, 2023
Merged
Show file tree
Hide file tree
Changes from all 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
22 changes: 10 additions & 12 deletions frontend/src/scenes/notebooks/Nodes/NotebookNodeReplayTimestamp.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -97,16 +97,14 @@ export function buildTimestampCommentContent(
currentPlayerTime: number | null,
sessionRecordingId: string
): JSONContent {
return [
{
type: 'paragraph',
content: [
{
type: NotebookNodeType.ReplayTimestamp,
attrs: { playbackTime: currentPlayerTime, sessionRecordingId: sessionRecordingId },
},
{ type: 'text', text: ' ' },
],
},
]
return {
type: 'paragraph',
content: [
{
type: NotebookNodeType.ReplayTimestamp,
attrs: { playbackTime: currentPlayerTime, sessionRecordingId: sessionRecordingId },
},
{ type: 'text', text: ' ' },
],
}
}
11 changes: 6 additions & 5 deletions frontend/src/scenes/notebooks/Notebook/Editor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { useCallback, useRef } from 'react'
import StarterKit from '@tiptap/starter-kit'
import ExtensionPlaceholder from '@tiptap/extension-placeholder'
import ExtensionDocument from '@tiptap/extension-document'
import { EditorRange, Node } from './utils'
import { EditorRange, EditorFocusPosition, Node } from './utils'

import { NotebookNodeFlag } from '../Nodes/NotebookNodeFlag'
import { NotebookNodeQuery } from 'scenes/notebooks/Nodes/NotebookNodeQuery'
Expand Down Expand Up @@ -140,13 +140,14 @@ export function Editor({
return false
},
},
autofocus: 'end',
onCreate: ({ editor }) => {
editorRef.current = editor
onCreate({
getJSON: () => editor.getJSON(),
setEditable: (editable: boolean) => editor.setEditable(editable, false),
setContent: (content: JSONContent) => editor.commands.setContent(content, false),
setEditable: (editable: boolean) => queueMicrotask(() => editor.setEditable(editable, false)),
setContent: (content: JSONContent) => queueMicrotask(() => editor.commands.setContent(content, false)),
focus: (position: EditorFocusPosition) => queueMicrotask(() => editor.commands.focus(position)),
destroy: () => editor.destroy(),
isEmpty: () => editor.isEmpty,
deleteRange: (range: EditorRange) => editor.chain().focus().deleteRange(range),
insertContent: (content: JSONContent) => editor.chain().insertContent(content).focus().run(),
Expand Down Expand Up @@ -237,7 +238,7 @@ function getChildren(node: Node, direct: boolean = true): Node[] {
function getPreviousNode(editor: TTEditor): Node | null {
const { $anchor } = editor.state.selection
const node = $anchor.node(1)
return !!node ? editor.state.doc.childBefore($anchor.pos - node.nodeSize).node : null
return !!node ? editor.state.doc.childBefore($anchor.pos - 1).node : null
}

export function hasMatchingNode(
Expand Down
1 change: 0 additions & 1 deletion frontend/src/scenes/notebooks/Notebook/Notebook.scss
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,6 @@
}

&.NotebookRecordingTimestamp--preview {
height: 1.375rem;
border: 1px solid var(--border-light);
color: var(--mid);
padding: 0rem 0.25rem;
Expand Down
11 changes: 10 additions & 1 deletion frontend/src/scenes/notebooks/Notebook/Notebook.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,15 +12,17 @@ import { SCRATCHPAD_NOTEBOOK } from './notebooksListLogic'
import { NotebookConflictWarning } from './NotebookConflictWarning'
import { NotebookLoadingState } from './NotebookLoadingState'
import { Editor } from './Editor'
import { EditorFocusPosition } from './utils'

export type NotebookProps = {
shortId: string
editable?: boolean
initialAutofocus?: EditorFocusPosition
}

const PLACEHOLDER_TITLES = ['Release notes', 'Product roadmap', 'Meeting notes', 'Bug analysis']

export function Notebook({ shortId, editable = false }: NotebookProps): JSX.Element {
export function Notebook({ shortId, editable = false, initialAutofocus = null }: NotebookProps): JSX.Element {
const logic = notebookLogic({ shortId })
const { notebook, content, notebookLoading, isEmpty, editor, conflictWarningVisible } = useValues(logic)
const { setEditor, onEditorUpdate, duplicateNotebook, loadNotebook } = useActions(logic)
Expand All @@ -37,9 +39,16 @@ export function Notebook({ shortId, editable = false }: NotebookProps): JSX.Elem
useEffect(() => {
if (editor) {
editor.setEditable(editable)
editor.focus(initialAutofocus)
}
}, [editor, editable])

useEffect(() => {
if (editor) {
editor.focus(initialAutofocus)
}
}, [editor])

// TODO - Render a special state if the notebook is empty

if (conflictWarningVisible) {
Expand Down
4 changes: 3 additions & 1 deletion frontend/src/scenes/notebooks/Notebook/NotebookSideBar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@ import { notebookLogic } from './notebookLogic'
import { urls } from 'scenes/urls'

export function NotebookSideBar({ children }: { children: React.ReactElement<any> }): JSX.Element {
const { notebookSideBarShown, fullScreen, selectedNotebook, desiredWidth } = useValues(notebookSidebarLogic)
const { notebookSideBarShown, fullScreen, selectedNotebook, desiredWidth, initialAutofocus } =
useValues(notebookSidebarLogic)
const { setNotebookSideBarShown, setFullScreen, selectNotebook, onResize, setElementRef } =
useActions(notebookSidebarLogic)
const { createNotebook } = useActions(notebooksListLogic)
Expand Down Expand Up @@ -121,6 +122,7 @@ export function NotebookSideBar({ children }: { children: React.ReactElement<any
key={selectedNotebook}
shortId={selectedNotebook}
editable={!notebook?.is_template}
initialAutofocus={initialAutofocus}
/>
</div>
</div>
Expand Down
10 changes: 9 additions & 1 deletion frontend/src/scenes/notebooks/Notebook/notebookSidebarLogic.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { urlToAction } from 'kea-router'
import { RefObject } from 'react'
import posthog from 'posthog-js'
import { subscriptions } from 'kea-subscriptions'
import { EditorFocusPosition } from './utils'

export const MIN_NOTEBOOK_SIDEBAR_WIDTH = 600

Expand All @@ -14,6 +15,7 @@ export const notebookSidebarLogic = kea<notebookSidebarLogicType>([
setNotebookSideBarShown: (shown: boolean) => ({ shown }),
setFullScreen: (full: boolean) => ({ full }),
selectNotebook: (id: string) => ({ id }),
setInitialAutofocus: (position: EditorFocusPosition) => ({ position }),
onResize: (event: { originX: number; desiredX: number; finished: boolean }) => event,
setDesiredWidth: (width: number) => ({ width }),
setElementRef: (element: RefObject<HTMLElement>) => ({ element }),
Expand Down Expand Up @@ -48,7 +50,13 @@ export const notebookSidebarLogic = kea<notebookSidebarLogicType>([
setDesiredWidth: (_, { width }) => width,
},
],

initialAutofocus: [
null as EditorFocusPosition,
{
selectNotebook: () => null,
setInitialAutofocus: (_, { position }) => position,
},
],
elementRef: [
null as RefObject<HTMLElement> | null,
{
Expand Down
12 changes: 9 additions & 3 deletions frontend/src/scenes/notebooks/Notebook/notebooksListLogic.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import { deleteWithUndo } from 'lib/utils'
import { teamLogic } from 'scenes/teamLogic'
import FuseClass from 'fuse.js'
import { notebookSidebarLogic } from './notebookSidebarLogic'
import { JSONContent, defaultNotebookContent } from './utils'
import { EditorFocusPosition, JSONContent, defaultNotebookContent } from './utils'

// Helping kea-typegen navigate the exported default class for Fuse
// eslint-disable-next-line @typescript-eslint/no-empty-interface
Expand All @@ -26,7 +26,11 @@ export const SCRATCHPAD_NOTEBOOK: NotebookListItemType = {
created_by: null,
}

export const openNotebook = (notebookId: string, target: NotebookTarget = NotebookTarget.Auto): void => {
export const openNotebook = (
notebookId: string,
target: NotebookTarget = NotebookTarget.Auto,
focus: EditorFocusPosition = null
): void => {
const sidebarLogic = notebookSidebarLogic.findMounted()

if (NotebookTarget.Sidebar === target) {
Expand All @@ -38,6 +42,8 @@ export const openNotebook = (notebookId: string, target: NotebookTarget = Notebo
} else {
router.actions.push(urls.notebookEdit(notebookId))
}

sidebarLogic?.actions.setInitialAutofocus(focus)
}

export const notebooksListLogic = kea<notebooksListLogicType>([
Expand Down Expand Up @@ -84,7 +90,7 @@ export const notebooksListLogic = kea<notebooksListLogicType>([
content: defaultNotebookContent(title, content),
})

openNotebook(notebook.short_id, location)
openNotebook(notebook.short_id, location, 'end')

posthog.capture(`notebook created`, {
short_id: notebook.short_id,
Expand Down
10 changes: 9 additions & 1 deletion frontend/src/scenes/notebooks/Notebook/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import {
JSONContent as TTJSONContent,
Editor as TTEditor,
ChainedCommands as EditorCommands,
FocusPosition as EditorFocusPosition,
Range as EditorRange,
getText,
} from '@tiptap/core'
Expand All @@ -13,13 +14,20 @@ import { NotebookNodeType } from '~/types'
export interface Node extends PMNode {}
export interface JSONContent extends TTJSONContent {}
/* eslint-enable @typescript-eslint/no-empty-interface */
// export type FocusPosition = number | boolean | 'start' | 'end' | 'all' | null

export { ChainedCommands as EditorCommands, Range as EditorRange } from '@tiptap/core'
export {
ChainedCommands as EditorCommands,
Range as EditorRange,
FocusPosition as EditorFocusPosition,
} from '@tiptap/core'

export interface NotebookEditor {
getJSON: () => JSONContent
setEditable: (editable: boolean) => void
setContent: (content: JSONContent) => void
focus: (position: EditorFocusPosition) => void
destroy: () => void
isEmpty: () => boolean
deleteRange: (range: EditorRange) => EditorCommands
insertContent: (content: JSONContent) => void
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
.FloatingSuggestion {
height: 1.375rem;
}
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import './FloatingSuggestions.scss'
import { Editor as TTEditor } from '@tiptap/core'
import { FloatingMenu } from '@tiptap/react'
import { useActions, useValues } from 'kea'
Expand All @@ -21,7 +22,11 @@ export function FloatingSuggestions({ editor }: { editor: TTEditor }): JSX.Eleme
return (
<FloatingMenu
editor={editor}
tippyOptions={{ duration: [100, 0], placement: 'left', offset: [0, 0] }}
tippyOptions={{
duration: [100, 0],
placement: 'right',
offset: [0, 0],
}}
className="NotebookFloatingButton"
shouldShow={({ editor }: { editor: TTEditor }) => {
if (!editor) {
Expand All @@ -39,7 +44,9 @@ export function FloatingSuggestions({ editor }: { editor: TTEditor }): JSX.Eleme
return false
}}
>
{Component && <Component previousNode={previousNode} />}
<div className="FloatingSuggestion flex items-center justify-content">
{Component && <Component previousNode={previousNode} />}
</div>
</FloatingMenu>
)
}
22 changes: 15 additions & 7 deletions frontend/src/scenes/notebooks/Suggestions/ReplayTimestamp.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,8 @@ import { InsertionSuggestion, InsertionSuggestionViewProps } from './InsertionSu
import { Node, NotebookEditor } from '../Notebook/utils'

const Component = ({ previousNode }: InsertionSuggestionViewProps): JSX.Element => {
const timestampNode = getTimestampChildNode(previousNode)
const { currentPlayerTime } = useValues(
sessionRecordingPlayerLogic(sessionRecordingPlayerProps(timestampNode.attrs.sessionRecordingId))
sessionRecordingPlayerLogic(sessionRecordingPlayerProps(getSessionRecordingId(previousNode)))
)

return (
Expand All @@ -21,25 +20,34 @@ const Component = ({ previousNode }: InsertionSuggestionViewProps): JSX.Element
}

export default InsertionSuggestion.create({
shouldShow: ({ previousNode }) =>
!!previousNode ? hasChildOfType(previousNode, NotebookNodeType.ReplayTimestamp) : false,
shouldShow: ({ previousNode }) => {
return !!previousNode
? previousNode.type.name === NotebookNodeType.Recording ||
hasChildOfType(previousNode, NotebookNodeType.ReplayTimestamp)
: false
},

onTab: ({ editor, previousNode }: { editor: NotebookEditor | null; previousNode: Node | null }) => {
if (!!previousNode && !!editor) {
const timestampNode = getTimestampChildNode(previousNode)
const sessionRecordingId = timestampNode.attrs.sessionRecordingId
const sessionRecordingId = getSessionRecordingId(previousNode)

const currentPlayerTime =
sessionRecordingPlayerLogic.findMounted(sessionRecordingPlayerProps(sessionRecordingId))?.values
.currentPlayerTime || 0

editor.insertContent(buildTimestampCommentContent(currentPlayerTime, sessionRecordingId))
editor.insertContent([buildTimestampCommentContent(currentPlayerTime, sessionRecordingId)])
}
},

Component,
})

function getSessionRecordingId(node: Node | null): string {
return node?.type.name === NotebookNodeType.Recording
? node.attrs.id
: getTimestampChildNode(node).attrs.sessionRecordingId
}

function getTimestampChildNode(node: Node | null): Node {
return firstChildOfType(node as Node, NotebookNodeType.ReplayTimestamp) as Node
}
Original file line number Diff line number Diff line change
Expand Up @@ -55,10 +55,9 @@ export function PlayerMetaLinks(): JSX.Element {
const onComment = (): void => {
const currentPlayerTime = getCurrentPlayerTime() * 1000
if (nodeLogic) {
nodeLogic.actions.insertAfterLastNodeOfType(
NotebookNodeType.ReplayTimestamp,
buildTimestampCommentContent(currentPlayerTime, sessionRecordingId)
)
nodeLogic.actions.insertAfterLastNodeOfType(NotebookNodeType.ReplayTimestamp, [
buildTimestampCommentContent(currentPlayerTime, sessionRecordingId),
])
} else {
const title = `Session Replay Notes ${dayjs().format('DD/MM')}`
createNotebook(title, NotebookTarget.Sidebar, [
Expand Down
16 changes: 8 additions & 8 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -72,14 +72,14 @@
"@rrweb/types": "^2.0.0-alpha.9",
"@sentry/react": "7.22.0",
"@testing-library/dom": ">=7.21.4",
"@tiptap/core": "2.0.3",
"@tiptap/extension-document": "2.0.3",
"@tiptap/extension-floating-menu": "2.0.3",
"@tiptap/extension-placeholder": "2.0.3",
"@tiptap/pm": "2.0.3",
"@tiptap/react": "2.0.3",
"@tiptap/starter-kit": "2.0.3",
"@tiptap/suggestion": "2.0.3",
"@tiptap/core": "^2.1.0-rc.12",
"@tiptap/extension-document": "^2.1.0-rc.12",
"@tiptap/extension-floating-menu": "^2.1.0-rc.12",
"@tiptap/extension-placeholder": "^2.1.0-rc.12",
"@tiptap/pm": "^2.1.0-rc.12",
"@tiptap/react": "^2.1.0-rc.12",
"@tiptap/starter-kit": "^2.1.0-rc.12",
"@tiptap/suggestion": "^2.1.0-rc.12",
"@types/md5": "^2.3.0",
"@types/react-input-autosize": "^2.2.1",
"@types/react-textfit": "^1.1.0",
Expand Down
Loading
Loading