import { CollectionFreeFormView } from "../../views/collections/collectionFreeForm"; import React, { useState } from "react"; import { observable, observe } from "mobx"; type Movement = { time: number, panX: number, panY: number, } type Presentation = { movements: Array meta: Object, startDate: Date | null, } export class RecordingApi { private static NULL_PRESENTATION: Presentation = { movements: [], meta: {}, startDate: null, } // 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 } constructor() { // init the global instance RecordingApi._instance = this; // init the instance variables this.currentPresentation = RecordingApi.NULL_PRESENTATION this.isRecording = false; this.absoluteStart = -1; } // little helper :) private get isInitPresenation(): boolean { return this.currentPresentation.startDate === null } public start = (view: CollectionFreeFormView, 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()') } // (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.startDate = startDate // (4) set isRecording true to allow trackMovements this.isRecording = true } public clear = (): Error | undefined => { // TODO: maybe archive the data? if (this.isRecording) { console.error('[recordingApi.ts] clear() failed: currently recording presentation. call pause() or finish() first') return new Error('[recordingApi.ts] clear()') } // clear presenation data this.currentPresentation = RecordingApi.NULL_PRESENTATION // clear isRecording this.isRecording = false // clear absoluteStart this.absoluteStart = -1 } public pause = (): Error | undefined => { if (this.currentPresentation.startDate === null) { console.error('[recordingApi.ts] pause() failed: no presentation started. try calling init() first') return new Error('[recordingApi.ts] pause(): no presenation') } // don't allow track movments this.isRecording = false // set relativeStart to the pausedTimestamp const timestamp = new Date().getTime() this.absoluteStart = timestamp } public resume = () => { const timestamp = new Date().getTime() const startTimestamp = this.currentPresentation.startDate?.getTime() if (!startTimestamp) { console.error('[recordingApi.ts] resume() failed: no presentation data. try calling start() first') return new Error('[recordingApi.ts] pause()') } // update absoluteStart to bridge the paused time const absoluteTimePaused = timestamp - this.absoluteStart this.absoluteStart = absoluteTimePaused } public finish = (): Error | Presentation => { if (this.isInitPresenation) { console.error('[recordingApi.ts] finish() failed: no presentation data. try calling start() first') return new Error('[recordingApi.ts] finish()') } // return a copy of the the presentation data return { ...this.currentPresentation } } public trackMovements = (panX: number, panY: number): Error | undefined => { // ensure we are recording if (!this.isRecording) { console.error('[recordingApi.ts] pause() failed: recording is paused()') return new Error('[recordingApi.ts] pause()') } // get the relative time const timestamp = new Date().getTime() const relativeTime = timestamp - this.absoluteStart // make new movement struct const movement: Movement = { time: relativeTime, panX, panY } // add that movement to the current presentation data's movement array this.currentPresentation.movements.push(movement) } // TOOD: need to pause all intervals if possible lol // TODO: extract this into different class with pause and resume recording public followMovements = (presentation: Presentation, docView: CollectionFreeFormView): void => { const document = docView.Document const { movements } = presentation movements.forEach(movement => { const { panX, panY, time } = movement // set the pan to what was stored setTimeout(() => { document._panX = panX; document._panY = panY; }, time) }) } // observer that can be updated to track the relevant FreeFormView // public setFreeFormView = (view: CollectionFreeFormView): void => { // observe(view, 'Document', (change) => { // if (change.name === '_panX') { // this.trackMovements(view.Document._panX, view.Document._panY) // } // } // } }