diff options
Diffstat (limited to 'src/client/util/RecordingApi.ts')
-rw-r--r-- | src/client/util/RecordingApi.ts | 269 |
1 files changed, 0 insertions, 269 deletions
diff --git a/src/client/util/RecordingApi.ts b/src/client/util/RecordingApi.ts deleted file mode 100644 index 7bffb0379..000000000 --- a/src/client/util/RecordingApi.ts +++ /dev/null @@ -1,269 +0,0 @@ -import { CollectionFreeFormView } from '../views/collections/collectionFreeForm'; -import { IReactionDisposer, observable, reaction } from 'mobx'; -import { NumCast } from '../../fields/Types'; -import { Doc } from '../../fields/Doc'; -import { VideoBox } from '../views/nodes/VideoBox'; -import { scaleDiverging } from 'd3-scale'; -import { Transform } from './Transform'; - -type Movement = { - time: number; - panX: number; - panY: number; - scale: number; -}; - -type Presentation = { - movements: Array<Movement> | null; - meta: Object; -}; - -export class RecordingApi { - private static NULL_PRESENTATION: Presentation = { - movements: null, - meta: {}, - }; - - // instance variables - private currentPresentation: Presentation; - private isRecording: boolean; - private absoluteStart: number; - - // create static instance and getter for global use - @observable static _instance: RecordingApi; - public static get Instance(): RecordingApi { - return RecordingApi._instance; - } - public constructor() { - // init the global instance - RecordingApi._instance = this; - - // init the instance variables - this.currentPresentation = RecordingApi.NULL_PRESENTATION; - this.isRecording = false; - this.absoluteStart = -1; - - // used for tracking movements in the view frame - this.disposeFunc = null; - this.recordingFFView = null; - - // for now, set playFFView - this.playFFView = null; - this.timers = null; - } - - // little helper :) - private get isInitPresenation(): boolean { - return this.currentPresentation.movements === null; - } - - public start = (meta?: Object): Error | undefined => { - // check if already init a presentation - if (!this.isInitPresenation) { - console.error('[recordingApi.ts] start() failed: current presentation data exists. please call clear() first.'); - return new Error('[recordingApi.ts] start()'); - } - - // update the presentation mode - Doc.UserDoc().presentationMode = 'recording'; - - // (1a) get start date for presenation - const startDate = new Date(); - // (1b) set start timestamp to absolute timestamp - this.absoluteStart = startDate.getTime(); - - // (2) assign meta content if it exists - this.currentPresentation.meta = meta || {}; - // (3) assign start date to currentPresenation - this.currentPresentation.movements = []; - // (4) set isRecording true to allow trackMovements - this.isRecording = true; - }; - - public clear = (): Error | Presentation => { - // TODO: maybe archive the data? - if (this.isRecording) { - console.error('[recordingApi.ts] clear() failed: currently recording presentation. call pause() first'); - return new Error('[recordingApi.ts] clear()'); - } - - // update the presentation mode - Doc.UserDoc().presentationMode = 'none'; - // set the previus recording view to the play view - this.playFFView = this.recordingFFView; - - const presCopy = { ...this.currentPresentation }; - - // clear presenation data - this.currentPresentation = RecordingApi.NULL_PRESENTATION; - // clear isRecording - this.isRecording = false; - // clear absoluteStart - this.absoluteStart = -1; - // clear the disposeFunc - this.removeRecordingFFView(); - - return presCopy; - }; - - public pause = (): Error | undefined => { - if (this.isInitPresenation) { - console.error('[recordingApi.ts] pause() failed: no presentation started. try calling init() first'); - return new Error('[recordingApi.ts] pause(): no presentation'); - } - // don't allow track movments - this.isRecording = false; - - // set adjust absoluteStart to add the time difference - const timestamp = new Date().getTime(); - this.absoluteStart = timestamp - this.absoluteStart; - }; - - public resume = () => { - this.isRecording = true; - // set absoluteStart to the difference in time - this.absoluteStart = new Date().getTime() - this.absoluteStart; - }; - - private trackMovements = (panX: number, panY: number, scale: number = 0): Error | undefined => { - // ensure we are recording - if (!this.isRecording) { - return new Error('[recordingApi.ts] trackMovements()'); - } - // check to see if the presetation is init - if (this.isInitPresenation) { - return new Error('[recordingApi.ts] trackMovements(): no presentation'); - } - - // get the time - const time = new Date().getTime() - this.absoluteStart; - // make new movement object - const movement: Movement = { time, panX, panY, scale }; - - // add that movement to the current presentation data's movement array - this.currentPresentation.movements && this.currentPresentation.movements.push(movement); - }; - - // instance variable for the FFView - private disposeFunc: IReactionDisposer | null; - private recordingFFView: CollectionFreeFormView | null; - - // set the FFView that will be used in a reaction to track the movements - public setRecordingFFView = (view: CollectionFreeFormView): void => { - // set the view to the current view - if (view === this.recordingFFView || view == null) return; - - // this.recordingFFView = view; - // set the reaction to track the movements - this.disposeFunc = reaction( - () => ({ x: NumCast(view.Document.panX, -1), y: NumCast(view.Document.panY, -1), scale: NumCast(view.Document.viewScale, -1) }), - res => res.x !== -1 && res.y !== -1 && this.isRecording && this.trackMovements(res.x, res.y, res.scale) - ); - - // for now, set the most recent recordingFFView to the playFFView - this.recordingFFView = view; - }; - - // call on dispose function to stop tracking movements - public removeRecordingFFView = (): void => { - this.disposeFunc?.(); - this.disposeFunc = null; - }; - - // TODO: extract this into different class with pause and resume recording - // TODO: store the FFview with the movements - private playFFView: CollectionFreeFormView | null; - private timers: NodeJS.Timeout[] | null; - - public setPlayFFView = (view: CollectionFreeFormView): void => { - this.playFFView = view; - }; - - // pausing movements will dispose all timers that are planned to replay the movements - // play movemvents will recreate them when the user resumes the presentation - public pauseMovements = (): undefined | Error => { - if (this.playFFView === null) { - return new Error('[recordingApi.ts] pauseMovements() failed: no view'); - } - - if (!this._isPlaying) { - //return new Error('[recordingApi.ts] pauseMovements() failed: not playing') - return; - } - this._isPlaying = false; - // TODO: set userdoc presentMode to browsing - this.timers?.map(timer => clearTimeout(timer)); - - // this.videoBox = null; - }; - - private videoBox: VideoBox | null = null; - - // by calling pause on the VideoBox, the pauseMovements will be called - public pauseVideoAndMovements = (): boolean => { - this.videoBox?.Pause(); - - this.pauseMovements(); - return this.videoBox == null; - }; - - public _isPlaying = false; - - public playMovements = (presentation: Presentation, timeViewed: number = 0, videoBox?: VideoBox): undefined | Error => { - if (presentation.movements === null || this.playFFView === null) { - return new Error('[recordingApi.ts] followMovements() failed: no presentation data or no view'); - } - if (this._isPlaying) return; - - this._isPlaying = true; - Doc.UserDoc().presentationMode = 'watching'; - - // TODO: consider this bug at the end of the clip on seek - this.videoBox = videoBox || null; - - // only get the movements that are remaining in the video time left - const filteredMovements = presentation.movements.filter(movement => movement.time > timeViewed * 1000); - - // helper to replay a movement - const document = this.playFFView; - let preScale = -1; - const zoomAndPan = (movement: Movement) => { - const { panX, panY, scale } = movement; - scale !== -1 && preScale !== scale && document.zoomSmoothlyAboutPt([panX, panY], scale, 0); - document.Document._panX = panX; - document.Document._panY = panY; - - preScale = scale; - }; - - // set the first frame to be at the start of the pres - zoomAndPan(filteredMovements[0]); - - // make timers that will execute each movement at the correct replay time - this.timers = filteredMovements.map(movement => { - const timeDiff = movement.time - timeViewed * 1000; - return setTimeout(() => { - // replay the movement - zoomAndPan(movement); - // if last movement, presentation is done -> set the instance var - if (movement === filteredMovements[filteredMovements.length - 1]) RecordingApi.Instance._isPlaying = false; - }, timeDiff); - }); - }; - - // Unfinished code for tracing multiple free form views - // export let pres: Map<CollectionFreeFormView, IReactionDisposer> = new Map() - - // export function AddRecordingFFView(ffView: CollectionFreeFormView): void { - // pres.set(ffView, - // reaction(() => ({ x: ffView.panX, y: ffView.panY }), - // (pt) => RecordingApi.trackMovements(ffView, pt.x, pt.y))) - // ) - // } - - // export function RemoveRecordingFFView(ffView: CollectionFreeFormView): void { - // const disposer = pres.get(ffView); - // disposer?.(); - // pres.delete(ffView) - // } -} |