aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/client/apis/recording/RecordingApi.ts174
-rw-r--r--src/client/apis/recording/recordingApi.tsx208
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx2
3 files changed, 208 insertions, 176 deletions
diff --git a/src/client/apis/recording/RecordingApi.ts b/src/client/apis/recording/RecordingApi.ts
deleted file mode 100644
index e19635b9c..000000000
--- a/src/client/apis/recording/RecordingApi.ts
+++ /dev/null
@@ -1,174 +0,0 @@
-import { CollectionFreeFormView } from "../../views/collections/collectionFreeForm";
-import React, { useState } from "react";
-import { IReactionDisposer, observe, reaction, observable } from "mobx";
-import { SelectionManager } from "../../util/SelectionManager";
-export class RecordingApi {
-
- @observable static _instance: LinkManager;
-
- constructor() {
- }
-
- type Movement = {
- time: number,
- panX: number,
- panY: number,
- }
-
- export type Presentation = {
- movements: Array<Movement>
- meta: Object,
- startDate: Date | null,
- }
-
- const NULL_PRESENTATION = {
- movements: [],
- meta: {},
- startDate: null,
- }
-
- const [currentPresentation, setCurrentPresenation] = useState<Presentation>(NULL_PRESENTATION)
- const [isRecording, setIsRecording] = useState(false)
- const [absoluteStart, setAbsoluteStart] = useState<number>(-1)
-
- export const initAndStart = (meta?: Object): Error | undefined => {
- // check if already init a presentation
- if (currentPresentation.startDate !== null) {
- 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
- setAbsoluteStart(startDate.getTime())
-
- // TODO: (2) assign meta content
-
- // (3) assign init values to currentPresenation
- setCurrentPresenation({ ...currentPresentation, startDate })
-
- // (4) set isRecording true to allow trackMovements
- setIsRecording(true)
- }
-
- export const clear = (): Error | undefined => {
- // TODO: maybe archive the data?
- if (isRecording) {
- console.error('[recordingApi.ts] clear() failed: currently recording presentation. call pause() or finish() first')
- return new Error('[recordingApi.ts] clear()')
- }
- // clear presenation data
- setCurrentPresenation(NULL_PRESENTATION)
-
- // set isRecording false
- setIsRecording(false)
-
- // default absoluteStart
- setAbsoluteStart(-1)
- }
-
- export const pause = (): Error | undefined => {
- if (currentPresentation.startDate === null) {
- console.error('[recordingApi.ts] pause() failed: no presentation started. try calling init() first')
- return new Error('[recordingApi.ts] pause()')
- }
- // don't allow track movments
- setIsRecording(false)
-
- // set relativeStart to the pausedTimestamp
- const timestamp = new Date().getTime()
- setAbsoluteStart(timestamp)
- }
-
- export const resume = () => {
- if (currentPresentation.startDate === null) {
- console.error('[recordingApi.ts] resume() failed: no presentation started. try calling init() first')
- return new Error('[recordingApi.ts] resume()')
- }
-
- const timestamp = new Date().getTime()
- const startTimestamp = currentPresentation.startDate?.getTime()
- if (!startTimestamp) {
- console.error('[recordingApi.ts] resume() failed: no presentation data. try calling init() first')
- return new Error('[recordingApi.ts] pause()')
- }
-
- setAbsoluteStart(prevTime => {
- // const relativeUnpause = timestamp - absoluteStart
- // const timePaused = relativeUnpause - prevTime
- // return timePaused + absoluteStart
- const absoluteTimePaused = timestamp - prevTime
- return absoluteTimePaused
- })
- }
-
- export const finish = (): Error | Presentation => {
- if (currentPresentation.movements === null) {
- console.error('[recordingApi.ts] finish() failed: no presentation data. try calling init() first')
- return new Error('[recordingApi.ts] finish()')
- }
-
- // make copy and clear this class's data
- // const returnCopy = { ...currentPresentation }
- // clear()
-
- // // return the copy
- // return returnCopy
-
- return currentPresentation
- }
-
- export const trackMovements = (panX: number, panY: number): Error | undefined => {
- // ensure we are recording
- if (!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 - absoluteStart
-
- // make new movement struct
- const movement: Movement = { time: relativeTime, panX, panY }
-
- // add that movement struct to the current presentation data
- setCurrentPresenation(prevPres => {
- const movements = [...prevPres.movements, movement]
- return {...prevPres, movements}
- })
- }
-
- // TOOD: need to pause all intervals if possible lol
- // TODO: extract this into different class with pause and resume recording
- export const 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)
- })
- }
-
- // 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)
- // }
-
-} \ No newline at end of file
diff --git a/src/client/apis/recording/recordingApi.tsx b/src/client/apis/recording/recordingApi.tsx
new file mode 100644
index 000000000..cce19deec
--- /dev/null
+++ b/src/client/apis/recording/recordingApi.tsx
@@ -0,0 +1,208 @@
+import { CollectionFreeFormView } from "../../views/collections/collectionFreeForm";
+import React, { useState } from "react";
+import { IReactionDisposer, observable, reaction } from "mobx";
+import { NumCast } from "../../../fields/Types";
+
+type Movement = {
+ time: number,
+ panX: number,
+ panY: number,
+}
+
+type Presentation = {
+ movements: Array<Movement>
+ 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;
+
+ // used for tracking movements in the view frame
+ this.disposeFunc = null;
+
+ // for now, set playFFView
+ this.playFFView = null;
+ }
+
+ // 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
+
+ // clear the disposeFunc
+ this.disposeFunc = null
+ }
+
+ 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 }
+ }
+
+ private 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)
+ }
+
+ // instance variable for the FFView
+ private disposeFunc: IReactionDisposer | 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 === 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) }),
+ (res) => (res.x !== -1 && res.y !== -1) && this.trackMovements(res.x, res.y)
+ )
+
+ // for now, set the most recent recordingFFView to the playFFView
+ this.playFFView = 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
+ private playFFView: CollectionFreeFormView | null;
+
+ public followMovements = (presentation: Presentation): undefined | Error => {
+ if (presentation.startDate === null || this.playFFView === null) {
+ return new Error('[recordingApi.ts] followMovements() failed: no presentation data or no view')
+ }
+
+ const document = this.playFFView.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)
+ })
+ }
+
+ // 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)
+ // }
+} \ No newline at end of file
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index ec2fa8d63..35bd9cf79 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -122,8 +122,6 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
@observable _keyframeEditing = false;
@observable ChildDrag: DocumentView | undefined; // child document view being dragged. needed to update drop areas of groups when a group item is dragged.
- @observable storedMovements: object[] = []; // stores the movement if in recoding mode
-
@computed get views() { return this._layoutElements.filter(ele => ele.bounds && !ele.bounds.z).map(ele => ele.ele); }
@computed get backgroundEvents() { return this.props.layerProvider?.(this.layoutDoc) === false && SnappingManager.GetIsDragging(); }
@computed get backgroundActive() { return this.props.layerProvider?.(this.layoutDoc) === false && this.props.isContentActive(); }