From 380ee1acac1c0b7972d7d423cf804af146dc0edf Mon Sep 17 00:00:00 2001 From: bobzel Date: Sun, 10 Dec 2023 20:19:27 -0500 Subject: massive changes to use mobx 6 which means not accessing props directly in @computed functions. --- .../collections/CollectionStackedTimeline.tsx | 217 +++++++++++---------- 1 file changed, 118 insertions(+), 99 deletions(-) (limited to 'src/client/views/collections/CollectionStackedTimeline.tsx') diff --git a/src/client/views/collections/CollectionStackedTimeline.tsx b/src/client/views/collections/CollectionStackedTimeline.tsx index 92b5470ae..28d15be71 100644 --- a/src/client/views/collections/CollectionStackedTimeline.tsx +++ b/src/client/views/collections/CollectionStackedTimeline.tsx @@ -1,7 +1,7 @@ -import * as React from 'react'; -import { action, computed, IReactionDisposer, observable, reaction } from 'mobx'; +import { action, computed, IReactionDisposer, makeObservable, observable, override, reaction, runInAction } from 'mobx'; import { observer } from 'mobx-react'; import { computedFn } from 'mobx-utils'; +import * as React from 'react'; import { Doc, Opt } from '../../../fields/Doc'; import { Id } from '../../../fields/FieldSymbols'; import { List } from '../../../fields/List'; @@ -9,7 +9,7 @@ import { listSpec } from '../../../fields/Schema'; import { ComputedField, ScriptField } from '../../../fields/ScriptField'; import { Cast, NumCast } from '../../../fields/Types'; import { ImageField } from '../../../fields/URLField'; -import { emptyFunction, formatTime, returnEmptyDoclist, returnEmptyFilter, returnFalse, returnNone, returnTrue, returnZero, setupMoveUpEvents, smoothScrollHorizontal, StopEvent } from '../../../Utils'; +import { copyProps, emptyFunction, formatTime, returnEmptyDoclist, returnEmptyFilter, returnFalse, returnNone, returnTrue, returnZero, setupMoveUpEvents, smoothScrollHorizontal, StopEvent } from '../../../Utils'; import { Docs } from '../../documents/Documents'; import { DocumentType } from '../../documents/DocumentTypes'; import { DocumentManager } from '../../util/DocumentManager'; @@ -20,9 +20,9 @@ import { ScriptingGlobals } from '../../util/ScriptingGlobals'; import { SnappingManager } from '../../util/SnappingManager'; import { Transform } from '../../util/Transform'; import { undoBatch, UndoManager } from '../../util/UndoManager'; -import { AudioWaveform } from '../AudioWaveform'; -import { CollectionSubView } from '../collections/CollectionSubView'; +import { CollectionSubView, SubCollectionViewProps } from '../collections/CollectionSubView'; import { LightboxView } from '../LightboxView'; +import { AudioWaveform } from '../nodes/audio/AudioWaveform'; import { DocFocusFunc, DocFocusOptions, DocumentView, DocumentViewProps, OpenWhere } from '../nodes/DocumentView'; import { LabelBox } from '../nodes/LabelBox'; import { VideoBox } from '../nodes/VideoBox'; @@ -53,17 +53,28 @@ export enum TrimScope { } @observer -export class CollectionStackedTimeline extends CollectionSubView() { +export class CollectionStackedTimeline extends CollectionSubView() { @observable static SelectingRegion: CollectionStackedTimeline | undefined = undefined; @observable public static CurrentlyPlaying: DocumentView[]; + _prevProps: React.PropsWithChildren; + @override _props: React.PropsWithChildren; + constructor(props: React.PropsWithChildren) { + super(props); + this._props = this._prevProps = props; + makeObservable(this); + } + componentDidUpdate() { + copyProps(this); + } + static LabelScript: ScriptField; static LabelPlayScript: ScriptField; private _timeline: HTMLDivElement | null = null; // ref to actual timeline div private _timelineWrapper: HTMLDivElement | null = null; // ref to timeline wrapper div for zooming and scrolling private _markerStart: number = 0; - @observable _markerEnd: number | undefined; + @observable _markerEnd: number | undefined = undefined; @observable _trimming: number = TrimScope.None; @observable _trimStart: number = 0; // trim controls start pos @observable _trimEnd: number = 0; // trim controls end pos @@ -73,14 +84,14 @@ export class CollectionStackedTimeline extends CollectionSubView (CollectionStackedTimeline.SelectingRegion = undefined)); } } @@ -149,8 +159,8 @@ export class CollectionStackedTimeline extends CollectionSubView NumCast(anchor._timecodeToShow, NumCast(anchor[this.props.startTag])); - anchorEnd = (anchor: Doc, val: any = null) => NumCast(anchor._timecodeToHide, NumCast(anchor[this.props.endTag], val) ?? null); + anchorStart = (anchor: Doc) => NumCast(anchor._timecodeToShow, NumCast(anchor[this._props.startTag])); + anchorEnd = (anchor: Doc, val: any = null) => NumCast(anchor._timecodeToHide, NumCast(anchor[this._props.endTag], val) ?? null); // converts screen pixel offset to time toTimeline = (screen_delta: number, width: number) => { @@ -177,13 +187,13 @@ export class CollectionStackedTimeline extends CollectionSubView 15 && !this.IsTrimming) { - const anchor = CollectionStackedTimeline.createAnchor(this.Document, this.dataDoc, this.props.fieldKey, this._markerStart, this._markerEnd, undefined, true); + const anchor = CollectionStackedTimeline.createAnchor(this.Document, this.dataDoc, this._props.fieldKey, this._markerStart, this._markerEnd, undefined, true); setTimeout(() => DocumentManager.Instance.getDocumentView(anchor)?.select(false)); } (!isClick || !wasSelecting) && (this._markerEnd = undefined); @@ -265,17 +275,17 @@ export class CollectionStackedTimeline extends CollectionSubView { if (e.button !== 2) { - this.props.select(false); - !wasPlaying && doubleTap && this.props.Play(); + this._props.select(false); + !wasPlaying && doubleTap && this._props.Play(); } }, - this.props.isSelected() || this.props.isContentActive(), + this._props.isSelected() || this._props.isContentActive(), undefined, () => { if (shiftKey) { - CollectionStackedTimeline.createAnchor(this.Document, this.dataDoc, this.props.fieldKey, this.currentTime, undefined, undefined, true); + CollectionStackedTimeline.createAnchor(this.Document, this.dataDoc, this._props.fieldKey, this.currentTime, undefined, undefined, true); } else { - !wasPlaying && this.props.setTime(this.toTimeline(clientX - rect.x, rect.width)); + !wasPlaying && this._props.setTime(this.toTimeline(clientX - rect.x, rect.width)); } } ); @@ -290,7 +300,7 @@ export class CollectionStackedTimeline extends CollectionSubView 0 ? new ImageField(this.thumbnails[nearest]) : undefined; this._thumbnail = imgField?.url?.href ? imgField.url.href.replace('.png', '_m.png') : undefined; } @@ -305,7 +315,7 @@ export class CollectionStackedTimeline extends CollectionSubView { - if (rect && this.props.isContentActive()) { + if (rect && this._props.isContentActive()) { this._trimStart = Math.min(Math.max(this.trimStart + (e.movementX / rect.width) * this.clipDuration, this.clipStart), this.trimEnd - this.minTrimLength); } return false; @@ -325,7 +335,7 @@ export class CollectionStackedTimeline extends CollectionSubView { - if (rect && this.props.isContentActive()) { + if (rect && this._props.isContentActive()) { this._trimEnd = Math.max(Math.min(this.trimEnd + (e.movementX / rect.width) * this.clipDuration, this.clipEnd), this.trimStart + this.minTrimLength); } return false; @@ -348,8 +358,8 @@ export class CollectionStackedTimeline extends CollectionSubView { if (this._timelineWrapper) { - if (time > this.toTimeline(this._scroll + this.props.PanelWidth(), this.timelineContentWidth)) { - this._scroll = Math.min(this._scroll + this.props.PanelWidth(), this.timelineContentWidth - this.props.PanelWidth()); + if (time > this.toTimeline(this._scroll + this._props.PanelWidth(), this.timelineContentWidth)) { + this._scroll = Math.min(this._scroll + this._props.PanelWidth(), this.timelineContentWidth - this._props.PanelWidth()); smoothScrollHorizontal(200, this._timelineWrapper, this._scroll); } else if (time < this.toTimeline(this._scroll, this.timelineContentWidth)) { this._scroll = (time / this.timelineContentWidth) * this.clipDuration; @@ -363,15 +373,15 @@ export class CollectionStackedTimeline extends CollectionSubView { const anchorEnd = this.anchorEnd(drop); if (anchorEnd !== undefined) { - Doc.SetInPlace(drop, drop._timecodeToHide === undefined ? this.props.endTag : 'timecodeToHide', timelinePt + anchorEnd - this.anchorStart(drop), false); + Doc.SetInPlace(drop, drop._timecodeToHide === undefined ? this._props.endTag : 'timecodeToHide', timelinePt + anchorEnd - this.anchorStart(drop), false); } - Doc.SetInPlace(drop, drop._timecodeToShow === undefined ? this.props.startTag : 'timecodeToShow', timelinePt, false); + Doc.SetInPlace(drop, drop._timecodeToShow === undefined ? this._props.startTag : 'timecodeToShow', timelinePt, false); }); return true; @@ -386,7 +396,6 @@ export class CollectionStackedTimeline extends CollectionSubView, anchorEndTime: Opt, docAnchor: Opt, addAsAnnotation: boolean) { if (anchorStartTime === undefined) return doc; const startTag = '_timecodeToShow'; @@ -422,20 +431,20 @@ export class CollectionStackedTimeline extends CollectionSubView NumCast(this.layoutDoc._layout_currentTimecode)) { - if (!this.layoutDoc.autoPlayAnchors && this.props.playing()) { - this.props.Pause(); + if (!this.layoutDoc.autoPlayAnchors && this._props.playing()) { + this._props.Pause(); } else { - this.props.Play(); + this._props.Play(); } } else { - this.props.playFrom(seekTimeInSeconds, endTime); + this._props.playFrom(seekTimeInSeconds, endTime); this.scrollToTime(seekTimeInSeconds); } } @@ -450,17 +459,17 @@ export class CollectionStackedTimeline extends CollectionSubView NumCast(this.layoutDoc._layout_currentTimecode) - 1e-4) { - if (this.props.playing()) this.props.Pause(); - else if (this.layoutDoc.autoPlayAnchors) this.props.Play(); + if (this._props.playing()) this._props.Pause(); + else if (this.layoutDoc.autoPlayAnchors) this._props.Play(); else if (!this.layoutDoc.autoPlayAnchors) { const rect = this._timeline?.getBoundingClientRect(); - rect && this.props.setTime(this.toTimeline(clientX - rect.x, rect.width)); + rect && this._props.setTime(this.toTimeline(clientX - rect.x, rect.width)); } } else { if (this.layoutDoc.autoPlayAnchors) { - this.props.playFrom(seekTimeInSeconds, endTime); + this._props.playFrom(seekTimeInSeconds, endTime); } else { - this.props.setTime(seekTimeInSeconds); + this._props.setTime(seekTimeInSeconds); } } return { select: true }; @@ -490,18 +499,18 @@ export class CollectionStackedTimeline extends CollectionSubView (this.props.PanelHeight() * (100 - this.dictationHeightPercent)) / 100; + dictationHeight = () => (this._props.PanelHeight() * (100 - this.dictationHeightPercent)) / 100; @computed get timelineContentHeight() { - return (this.props.PanelHeight() * this.dictationHeightPercent) / 100; + return (this._props.PanelHeight() * this.dictationHeightPercent) / 100; } @computed get timelineContentWidth() { - return this.props.PanelWidth() * this.zoomFactor; + return this._props.PanelWidth() * this.zoomFactor; } // subtract size of container border - dictationScreenToLocalTransform = () => this.props.ScreenToLocalTransform().translate(0, -this.timelineContentHeight); + dictationScreenToLocalTransform = () => this._props.ScreenToLocalTransform().translate(0, -this.timelineContentHeight); - isContentActive = () => this.props.isSelected() || this.props.isContentActive(); + isContentActive = () => this._props.isSelected() || this._props.isContentActive(); currentTimecode = () => this.currentTime; @@ -520,7 +529,7 @@ export class CollectionStackedTimeline extends CollectionSubView
this.isContentActive() && e.stopPropagation()} onScroll={this.setScroll} onMouseMove={e => this.isContentActive() && this.onHover(e)} @@ -553,11 +562,11 @@ export class CollectionStackedTimeline extends CollectionSubView this.clipEnd) return null; const left = Math.max(((start - this.clipStart) / this.clipDuration) * this.timelineContentWidth, 0); - const top = (d.level / maxLevel) * this.props.PanelHeight(); + const top = (d.level / maxLevel) * this._props.PanelHeight(); const timespan = Math.max(0, Math.min(end - this.clipStart, this.clipEnd)) - Math.max(0, start - this.clipStart); const width = (timespan / this.clipDuration) * this.timelineContentWidth; - const height = this.props.PanelHeight() / maxLevel; - return this.props.Document.hideAnchors ? null : ( + const height = this._props.PanelHeight() / maxLevel; + return this._props.Document.hideAnchors ? null : (
)} @@ -691,37 +701,44 @@ class StackedTimelineAnchor extends React.Component _lastTimecode: number; _disposer: IReactionDisposer | undefined; - constructor(props: any) { + _prevProps: React.PropsWithChildren; + @observable _props: React.PropsWithChildren; + constructor(props: React.PropsWithChildren) { super(props); - this._lastTimecode = this.props.currentTimecode(); + this._props = this._prevProps = props; + makeObservable(this); + this._lastTimecode = this._props.currentTimecode(); + } + componentDidUpdate() { + copyProps(this); } // updates marker document title to reflect correct timecodes computeTitle = () => { - if (this.props.mark.type !== DocumentType.LABEL) return undefined; - const start = Math.max(NumCast(this.props.mark[this.props.startTag]), this.props.trimStart) - this.props.trimStart; - const end = Math.min(NumCast(this.props.mark[this.props.endTag]), this.props.trimEnd) - this.props.trimStart; + if (this._props.mark.type !== DocumentType.LABEL) return undefined; + const start = Math.max(NumCast(this._props.mark[this._props.startTag]), this._props.trimStart) - this._props.trimStart; + const end = Math.min(NumCast(this._props.mark[this._props.endTag]), this._props.trimEnd) - this._props.trimStart; return `#${formatTime(start)}-${formatTime(end)}`; }; componentDidMount() { this._disposer = reaction( - () => this.props.currentTimecode(), + () => this._props.currentTimecode(), time => { - const dictationDoc = Cast(this.props.layoutDoc.data_dictation, Doc, null); - const isDictation = dictationDoc && LinkManager.Links(this.props.mark).some(link => Cast(link.link_anchor_1, Doc, null)?.annotationOn === dictationDoc); + const dictationDoc = Cast(this._props.layoutDoc.data_dictation, Doc, null); + const isDictation = dictationDoc && LinkManager.Links(this._props.mark).some(link => Cast(link.link_anchor_1, Doc, null)?.annotationOn === dictationDoc); if ( !LightboxView.LightboxDoc && // bcz: when should links be followed? we don't want to move away from the video to follow a link but we can open it in a sidebar/etc. But we don't know that upfront. // for now, we won't follow any links when the lightbox is oepn to avoid "losing" the video. - /*(isDictation || !Doc.AreProtosEqual(LightboxView.LightboxDoc, this.props.layoutDoc))*/ - !this.props.layoutDoc.dontAutoFollowLinks && - LinkManager.Links(this.props.mark).length && - time > NumCast(this.props.mark[this.props.startTag]) && - time < NumCast(this.props.mark[this.props.endTag]) && - this._lastTimecode < NumCast(this.props.mark[this.props.startTag]) - 1e-5 + /*(isDictation || !Doc.AreProtosEqual(LightboxView.LightboxDoc, this._props.layoutDoc))*/ + !this._props.layoutDoc.dontAutoFollowLinks && + LinkManager.Links(this._props.mark).length && + time > NumCast(this._props.mark[this._props.startTag]) && + time < NumCast(this._props.mark[this._props.endTag]) && + this._lastTimecode < NumCast(this._props.mark[this._props.startTag]) - 1e-5 ) { - LinkFollower.FollowLink(undefined, this.props.mark, false); + LinkFollower.FollowLink(undefined, this._props.mark, false); } this._lastTimecode = time; } @@ -736,16 +753,16 @@ class StackedTimelineAnchor extends React.Component // starting the drag event for anchor resizing @action onAnchorDown = (e: React.PointerEvent, anchor: Doc, left: boolean): void => { - //this.props._timeline?.setPointerCapture(e.pointerId); + //this._props._timeline?.setPointerCapture(e.pointerId); const newTime = (e: PointerEvent) => { const rect = (e.target as any).getBoundingClientRect(); - return this.props.toTimeline(e.clientX - rect.x, rect.width); + return this._props.toTimeline(e.clientX - rect.x, rect.width); }; const changeAnchor = (anchor: Doc, left: boolean, time: number | undefined) => { - const timelineOnly = Cast(anchor[this.props.startTag], 'number', null) !== undefined; + const timelineOnly = Cast(anchor[this._props.startTag], 'number', null) !== undefined; if (timelineOnly) { - if (!left && time !== undefined && time <= NumCast(anchor[this.props.startTag])) time = undefined; - Doc.SetInPlace(anchor, left ? this.props.startTag : this.props.endTag, time, true); + if (!left && time !== undefined && time <= NumCast(anchor[this._props.startTag])) time = undefined; + Doc.SetInPlace(anchor, left ? this._props.startTag : this._props.endTag, time, true); if (!left) Doc.SetInPlace(anchor, 'layout_borderRounding', time !== undefined ? undefined : '100%', true); } else { anchor[left ? '_timecodeToShow' : '_timecodeToHide'] = time; @@ -759,11 +776,11 @@ class StackedTimelineAnchor extends React.Component e, e => { if (!undo) undo = UndoManager.StartBatch('drag anchor'); - this.props.setTime(newTime(e)); + this._props.setTime(newTime(e)); return changeAnchor(anchor, left, newTime(e)); }, action(e => { - this.props.setTime(newTime(e)); + this._props.setTime(newTime(e)); undo?.end(); this.noEvents = false; }), @@ -774,7 +791,9 @@ class StackedTimelineAnchor extends React.Component // context menu contextMenuItems = () => { const resetTitle = { - script: ScriptField.MakeFunction(`this.title = this["${this.props.endTag}"] ? "#" + formatToTime(this["${this.props.startTag}"]) + "-" + formatToTime(this["${this.props.endTag}"]) : "#" + formatToTime(this["${this.props.startTag}"])`)!, + script: ScriptField.MakeFunction( + `this.title = this["${this._props.endTag}"] ? "#" + formatToTime(this["${this._props.startTag}"]) + "-" + formatToTime(this["${this._props.endTag}"]) : "#" + formatToTime(this["${this._props.startTag}"])` + )!, icon: 'folder-plus', label: 'Reset Title', }; @@ -785,7 +804,7 @@ class StackedTimelineAnchor extends React.Component renderInner = computedFn(function (this: StackedTimelineAnchor, mark: Doc, script: undefined | (() => ScriptField), doublescript: undefined | (() => ScriptField), screenXf: () => Transform, width: () => number, height: () => number) { const anchor = observable({ view: undefined as Opt | null }); const focusFunc = (doc: Doc, options: DocFocusOptions): number | undefined => { - this.props.playLink(mark, options); + this._props.playLink(mark, options); return undefined; }; return { @@ -793,7 +812,7 @@ class StackedTimelineAnchor extends React.Component view: ( (anchor.view = r))} @@ -801,11 +820,11 @@ class StackedTimelineAnchor extends React.Component TemplateDataDocument={undefined} docViewPath={returnEmptyDoclist} pointerEvents={this.noEvents ? returnNone : undefined} - styleProvider={this.props.styleProvider} - renderDepth={this.props.renderDepth + 1} + styleProvider={this._props.styleProvider} + renderDepth={this._props.renderDepth + 1} LayoutTemplate={undefined} LayoutTemplateString={LabelBox.LayoutStringWithTitle('data', this.computeTitle())} - isDocumentActive={this.props.isDocumentActive} + isDocumentActive={this._props.isDocumentActive} PanelWidth={width} PanelHeight={height} layout_fitWidth={returnTrue} @@ -817,7 +836,7 @@ class StackedTimelineAnchor extends React.Component childFilters={returnEmptyFilter} childFiltersByRanges={returnEmptyFilter} onClick={script} - onDoubleClick={this.props.layoutDoc.autoPlayAnchors ? undefined : doublescript} + onDoubleClick={this._props.layoutDoc.autoPlayAnchors ? undefined : doublescript} ignoreAutoHeight={false} hideResizeHandles={true} bringToFront={emptyFunction} @@ -827,19 +846,19 @@ class StackedTimelineAnchor extends React.Component }; }); - anchorScreenToLocalXf = () => this.props.ScreenToLocalTransform().translate(-this.props.left, -this.props.top); - width = () => this.props.width; - height = () => this.props.height; + anchorScreenToLocalXf = () => this._props.ScreenToLocalTransform().translate(-this._props.left, -this._props.top); + width = () => this._props.width; + height = () => this._props.height; render() { - const inner = this.renderInner(this.props.mark, this.props.rangeClickScript, this.props.rangePlayScript, this.anchorScreenToLocalXf, this.width, this.height); + const inner = this.renderInner(this._props.mark, this._props.rangeClickScript, this._props.rangePlayScript, this.anchorScreenToLocalXf, this.width, this.height); return (
{inner.view} {!inner.anchor.view || !inner.anchor.view.SELECTED ? null : ( <> -
this.onAnchorDown(e, this.props.mark, true)} /> -
this.onAnchorDown(e, this.props.mark, false)} /> +
this.onAnchorDown(e, this._props.mark, true)} /> +
this.onAnchorDown(e, this._props.mark, false)} /> )}
-- cgit v1.2.3-70-g09d2