Skip to content

Commit

Permalink
fix(ScriptEditor): bad update performance
Browse files Browse the repository at this point in the history
  • Loading branch information
jstarpl committed Jan 11, 2024
1 parent ceede14 commit 312e31e
Show file tree
Hide file tree
Showing 7 changed files with 152 additions and 118 deletions.
Binary file modified .yarn/install-state.gz
Binary file not shown.
Original file line number Diff line number Diff line change
Expand Up @@ -99,33 +99,35 @@ export class PartTransformer {
if (!outputLayer?.isPGM) continue

if (sourceLayer) {
const sourceLayerShortName = sourceLayer.abbreviation ?? sourceLayer.name

switch (sourceLayer.type) {
case SourceLayerType.CAMERA:
usePiece.set(PartDisplayType.Camera, { label: piece.name })
usePiece.set(PartDisplayType.Camera, { label: sourceLayerShortName })

break
case SourceLayerType.VT:
usePiece.set(PartDisplayType.VT, { label: piece.name })
usePiece.set(PartDisplayType.VT, { label: sourceLayerShortName })
break
case SourceLayerType.REMOTE:
usePiece.set(PartDisplayType.Remote, { label: piece.name })
usePiece.set(PartDisplayType.Remote, { label: sourceLayerShortName })
break
case SourceLayerType.SCRIPT:
const pieceContent = piece.content as ScriptContent
derived.scriptContents = pieceContent.fullScript ?? ''
break
case SourceLayerType.SPLITS:
usePiece.set(PartDisplayType.Split, { label: piece.name })
usePiece.set(PartDisplayType.Split, { label: sourceLayerShortName })
break
case SourceLayerType.LIVE_SPEAK:
usePiece.set(PartDisplayType.LiveSpeak, { label: piece.name })
usePiece.set(PartDisplayType.LiveSpeak, { label: sourceLayerShortName })
break
case SourceLayerType.AUDIO:
case SourceLayerType.LOWER_THIRD:
case SourceLayerType.TRANSITION:
case SourceLayerType.LOCAL:
case SourceLayerType.GRAPHICS:
usePiece.set(PartDisplayType.LiveSpeak, { label: piece.name })
usePiece.set(PartDisplayType.LiveSpeak, { label: sourceLayerShortName })
// ignore these
break
case SourceLayerType.UNKNOWN:
Expand Down
22 changes: 17 additions & 5 deletions packages/apps/client/src/ScriptEditor/Editor.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import React, { useCallback, useEffect, useRef } from 'react'
import { PartId } from '@sofie-prompter-editor/shared-model'
import { undo, redo, history } from 'prosemirror-history'
import { keymap } from 'prosemirror-keymap'
import { EditorState, SelectionBookmark } from 'prosemirror-state'
Expand All @@ -14,7 +15,6 @@ import { formatingKeymap } from './keymaps'
import { deselectAll } from './commands/deselectAll'
import { fromMarkdown } from '../lib/prosemirrorDoc'
import { AppStore } from '../stores/AppStore'
import { UILineId } from '../model/UILine'
import { IReactionDisposer, autorun, reaction } from 'mobx'

export function Editor({
Expand Down Expand Up @@ -43,7 +43,7 @@ export function Editor({
}
}, [])

const updateLineScript = useCallback((lineId: UILineId, script: string | null) => {
const updateLineScript = useCallback((lineId: PartId, script: string | null) => {
if (!editorView.current) return

const editorState = editorView.current.state
Expand Down Expand Up @@ -130,6 +130,7 @@ export function Editor({
}
},
(data) => {
console.log(performance.mark('begin'))
lineReactionDisposers.forEach((destr) => destr())

const openRundown = AppStore.rundownStore.openRundown
Expand All @@ -143,9 +144,15 @@ export function Editor({
schema.node(schema.nodes.segmentTitle, undefined, schema.text(segment.name)),
...segment.linesInOrder.map((lines) => {
lineReactionDisposers.push(
autorun(() => {
updateLineScript(lines.id, lines.reactiveObj.script)
})
reaction(
() => lines.reactiveObj.script,
(script) => {
updateLineScript(lines.id, script)
},
{
fireImmediately: false,
}
)
)

return schema.node(
Expand All @@ -163,11 +170,16 @@ export function Editor({
),
])

console.log(performance.mark('createDoc'))
const doc = schema.node(schema.nodes.doc, undefined, [rundown])

console.log(performance.mark('updateState'))
editorView.current.updateState(makeNewEditorState(doc))

console.log(performance.mark('finished'))
},
{
delay: 250,
fireImmediately: true,
}
)
Expand Down
31 changes: 19 additions & 12 deletions packages/apps/client/src/model/UILine.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import { Part, PartDisplayType, PartId, ProtectedString, protectString } from '@sofie-prompter-editor/shared-model'
import { RundownStore } from '../stores/RundownStore'
import { randomId } from '../lib/lib'
import { action, makeAutoObservable } from 'mobx'
import { UISegment } from './UISegment'

export type UILineId = PartId

export class UILine {
slug: string = ''

Expand All @@ -25,24 +26,27 @@ export class UILine {

ready: boolean = false

constructor(private store: RundownStore, private owner: UISegment, public id: PartId) {
constructor(private store: RundownStore, private owner: UISegment, public id: UILineId) {
makeAutoObservable(this, {
updateFromJson: action,
remove: action,
})

this.store.connection.part.on('updated', (json: Part) => {
if (this.id !== json._id) return
this.store.connection.part.on('updated', this.onPartUpdated)
this.store.connection.part.on('removed', this.onPartRemoved)
}

this.updateFromJson(json)
})
private onPartUpdated = action('onPartUpdated', (json: Part) => {
if (this.id !== json._id) return

this.store.connection.part.on('removed', (json) => {
if (this.id !== json._id) return
this.updateFromJson(json)
})

this.remove()
})
}
private onPartRemoved = action('onPartRemoved', (json: Pick<Part, '_id'>) => {
if (this.id !== json._id) return

this.remove()
})

updateFromJson(json: Part) {
this.identifier = json.identifier ?? null
Expand All @@ -64,7 +68,10 @@ export class UILine {
this.dispose()
}

dispose() {}
dispose() {
this.store.connection.part.off('updated', this.onPartUpdated)
this.store.connection.part.off('removed', this.onPartRemoved)
}
}

function partDisplayTypeToLineTypeStyle(type: PartDisplayType): LineType {
Expand Down
89 changes: 53 additions & 36 deletions packages/apps/client/src/model/UIRundown.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ import {
import { UISegment } from './UISegment'
import { RundownStore } from '../stores/RundownStore'

export type UIRundownId = RundownPlaylistId

export class UIRundown {
name: string = ''

Expand All @@ -19,7 +21,7 @@ export class UIRundown {

private rundowns = observable.map<RundownId, Rundown>()

constructor(private store: RundownStore, public id: RundownPlaylistId) {
constructor(private store: RundownStore, public id: UIRundownId) {
makeAutoObservable(this, {
updateFromJson: action,
segmentsInOrder: computed,
Expand All @@ -36,7 +38,7 @@ export class UIRundown {
},
})
for (const rundown of rundowns) {
this._onRundownCreated(rundown)
this.onRundownCreated(rundown)
}

const segments = await this.store.connection.segment.find({
Expand All @@ -45,46 +47,56 @@ export class UIRundown {
},
})
for (const segment of segments) {
this._onSegmentCreated(segment)
this.onSegmentCreated(segment)
}

// get all segments

// register callbacks for events

// we track playlist changed and removed
this.store.connection.playlist.on('updated', (json: RundownPlaylist) => {
if (json._id !== this.id) return
this.store.connection.playlist.on(
'updated',
action((json: RundownPlaylist) => {
if (json._id !== this.id) return

this.updateFromJson(json)
})
this.updateFromJson(json)
})
)

this.store.connection.playlist.on('removed', (id: RundownPlaylistId) => {
if (id !== this.id) return
this.store.connection.playlist.on(
'removed',
action((id: RundownPlaylistId) => {
if (id !== this.id) return

this.close()
})
this.close()
})
)

// we track rundown created, changed and removed, because we own Rundowns
this.store.connection.rundown.on('created', (json: Rundown) => {
this.rundowns.set(json._id, json)
})

this.store.connection.rundown.on('updated', (json: Rundown) => {
this.rundowns.set(json._id, json)
})

this.store.connection.rundown.on('removed', (json) => {
this.rundowns.delete(json._id)
})
this.store.connection.rundown.on(
'created',
action((json: Rundown) => {
this.rundowns.set(json._id, json)
})
)

this.store.connection.rundown.on(
'updated',
action((json: Rundown) => {
this.rundowns.set(json._id, json)
})
)

this.store.connection.rundown.on(
'removed',
action((json) => {
this.rundowns.delete(json._id)
})
)

// we track segment created so that we can add new Segments when they are added
this.store.connection.segment.on('created', (json: Segment) => {
if (json.playlistId !== this.id) return
if (!this.rundowns.has(json.rundownId)) return

this._onSegmentCreated(json)
})
this.store.connection.segment.on('created', this.onSegmentCreated)
}

updateFromJson(json: RundownPlaylist) {
Expand All @@ -108,22 +120,27 @@ export class UIRundown {

dispose(): void {
// unregister event handlers from services
this.store.connection.segment.off('created', this.onSegmentCreated)

// TODO: Add more handlers
}
private _onRundownCreated = action('onRundownCreated', (json: Rundown) => {
private onRundownCreated = action('onRundownCreated', (json: Rundown) => {
this.rundowns.set(json._id, json)
})
private _onSegmentCreated = action('onSegmentCreated', (json: Segment) => {
console.log('individual segment', json._id, json)
private onSegmentCreated = action('onSegmentCreated', (json: Segment) => {
if (json.playlistId !== this.id) return
if (!this.rundowns.has(json.rundownId)) return

const existing = this.segments.get(json._id)

if (!existing) {
const newSegment = new UISegment(this.store, this, json._id)
this.segments.set(json._id, newSegment)
newSegment.updateFromJson(json)
} else {
// update existing segment

existing.updateFromJson(json)
this.segments.set(json._id, newSegment)
return
}

// update existing segment
existing.updateFromJson(json)
})
}
7 changes: 4 additions & 3 deletions packages/apps/client/src/model/UIRundownEntry.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
import { action, makeAutoObservable } from 'mobx'
import { RundownId, RundownPlaylist, RundownPlaylistId, protectString } from '@sofie-prompter-editor/shared-model'
import { RundownPlaylist } from '@sofie-prompter-editor/shared-model'
import { RundownStore } from '../stores/RundownStore'
import { UIRundownId } from './UIRundown'

// a lightweight domain object for tracking rundowns without their contents
export class UIRundownEntry {
name: string = ''

ready: boolean = false

constructor(private store: RundownStore, public id: RundownPlaylistId) {
constructor(private store: RundownStore, public id: UIRundownId) {
makeAutoObservable(this, {
updateFromJson: action,
})
Expand All @@ -21,7 +22,7 @@ export class UIRundownEntry {
this.updateFromJson(json)
})

this.store.connection.playlist.on('removed', (id: RundownPlaylistId) => {
this.store.connection.playlist.on('removed', (id: UIRundownId) => {
if (this.id !== id) return

this.remove()
Expand Down
Loading

0 comments on commit 312e31e

Please sign in to comment.