272 lines
5.2 KiB
TypeScript
272 lines
5.2 KiB
TypeScript
import { Data } from 'types'
|
|
import clipboard from './clipboard'
|
|
import state from './state'
|
|
import { isDraft, current } from 'immer'
|
|
import { setToArray } from 'utils'
|
|
import tld from 'utils/tld'
|
|
import inputs from './inputs'
|
|
|
|
interface LogEntry {
|
|
eventName: string
|
|
payload: any
|
|
time: number
|
|
didCauseUpdate: boolean
|
|
}
|
|
|
|
class Logger {
|
|
filters = new Set([
|
|
// 'MOVED_OVER_SHAPE',
|
|
// 'RESIZED_WINDOW',
|
|
// 'HOVERED_SHAPE',
|
|
// 'UNHOVERED_SHAPE',
|
|
// 'PANNED_CAMERA',
|
|
'STARTED_LOGGING',
|
|
'STOPPED_LOGGING',
|
|
])
|
|
|
|
snapshotStart: Data
|
|
snapshotEnd: Data
|
|
|
|
log: LogEntry[] = []
|
|
|
|
isSimulating = false
|
|
|
|
isRunning = false
|
|
|
|
speed = 0
|
|
|
|
startTime = 0
|
|
|
|
/**
|
|
* Start the logger.
|
|
*
|
|
* ### Example
|
|
*
|
|
*```ts
|
|
* logger.start()
|
|
*```
|
|
*/
|
|
start = (data: Data): Logger => {
|
|
if (this.isRunning) return
|
|
|
|
this.isRunning = true
|
|
|
|
this.snapshotStart = isDraft(data) ? current(data) : data
|
|
|
|
this.log = []
|
|
|
|
this.startTime = Date.now()
|
|
|
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
// @ts-ignore
|
|
this.snapshotStart.pageStates[data.currentPageId].selectedIds = setToArray(
|
|
tld.getSelectedIds(data)
|
|
)
|
|
|
|
return this
|
|
}
|
|
|
|
/**
|
|
* Stop the logger.
|
|
*
|
|
* ### Example
|
|
*
|
|
*```ts
|
|
* logger.stop()
|
|
*```
|
|
*/
|
|
stop = (data: Data): Logger => {
|
|
if (!this.isRunning) return
|
|
|
|
this.isRunning = false
|
|
|
|
this.snapshotEnd = isDraft(data) ? current(data) : data
|
|
|
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
// @ts-ignore
|
|
this.snapshotEnd.pageStates[data.currentPageId].selectedIds = setToArray(
|
|
tld.getSelectedIds(data)
|
|
)
|
|
|
|
// if (window.confirm('Stopped logging. Copy to clipboard?')) {
|
|
// this.copyToJson()
|
|
// }
|
|
|
|
return this
|
|
}
|
|
|
|
/**
|
|
* Add an event and payload to the log.
|
|
*
|
|
* ### Example
|
|
*
|
|
*```ts
|
|
* logger.addToLog(eventName, payload)
|
|
*```
|
|
*/
|
|
addToLog(event: string, payload: any, didCauseUpdate = false) {
|
|
if (!this.isRunning) return
|
|
|
|
if (this.filters.has(event)) return
|
|
|
|
didCauseUpdate
|
|
// if (!didCauseUpdate) return
|
|
|
|
this.log.push({
|
|
eventName: event,
|
|
payload: payload,
|
|
time: Date.now() - this.startTime,
|
|
didCauseUpdate: true,
|
|
})
|
|
}
|
|
|
|
/**
|
|
* Play back a log entry.
|
|
*
|
|
* ### Example
|
|
*
|
|
*```ts
|
|
* logger.playback(log)
|
|
*```
|
|
*/
|
|
playback = (data: Data, log: string): Logger => {
|
|
const parsed: { start: Data; end: Data; events: LogEntry[] } =
|
|
JSON.parse(log)
|
|
|
|
const { start, events } = parsed
|
|
|
|
this.isSimulating = true
|
|
|
|
try {
|
|
data.pageStates[data.currentPageId].selectedIds = new Set(
|
|
start.pageStates[start.currentPageId].selectedIds
|
|
)
|
|
|
|
state.send('RESET_DOCUMENT_STATE').forceData(start)
|
|
|
|
const pointerDowns = [
|
|
'POINTED_CANVAS',
|
|
'POINTED_SHAPE',
|
|
'POINTED_BOUNDS',
|
|
'DOUBLE_POINTED_SHAPE',
|
|
'POINTED_HANDLE',
|
|
'RIGHT_POINTED',
|
|
]
|
|
|
|
const pointerChanges = [
|
|
...pointerDowns,
|
|
'MOVED_POINTER',
|
|
'MOVED_OVER_SHAPE',
|
|
'STOPPED_POINTING',
|
|
'DOUBLE_POINTED_CANVAS',
|
|
]
|
|
|
|
const pointerUps = ['STOPPED_POINTING']
|
|
|
|
for (const event of events) {
|
|
if (pointerDowns.includes(event.eventName)) {
|
|
inputs.pointer.origin = event.payload.point
|
|
}
|
|
|
|
if (pointerChanges.includes(event.eventName)) {
|
|
inputs.activePointerId = event.payload.pointerId
|
|
inputs.pointer = { ...inputs.pointer, ...event.payload }
|
|
inputs.points[event.payload.pointerId] = inputs.pointer
|
|
}
|
|
|
|
if (pointerUps.includes(event.eventName)) {
|
|
delete inputs.points[event.payload.pointerId]
|
|
delete inputs.activePointerId
|
|
|
|
// TODO: Double pointing
|
|
}
|
|
|
|
state.send(event.eventName, event.payload)
|
|
}
|
|
|
|
setTimeout(
|
|
() => (this.isSimulating = false),
|
|
events[events.length - 1].time + 1
|
|
)
|
|
} catch (e) {
|
|
console.warn('Could not play back that state.')
|
|
}
|
|
|
|
return this
|
|
}
|
|
|
|
/**
|
|
* Export the log as JSON.
|
|
*
|
|
* ### Example
|
|
*
|
|
*```ts
|
|
* logger.toJson()
|
|
*```
|
|
*/
|
|
copyToJson = () => {
|
|
const logAsString = JSON.stringify({
|
|
start: this.snapshotStart,
|
|
end: this.snapshotEnd,
|
|
events: this.log,
|
|
})
|
|
|
|
clipboard.copyStringToClipboard(logAsString)
|
|
|
|
return logAsString
|
|
}
|
|
|
|
/**
|
|
* Add a new event filter. Filtered events will not be logged.
|
|
*
|
|
* ### Example
|
|
*
|
|
*```ts
|
|
* logger.addFilter("SOME_EVENT")
|
|
*```
|
|
*/
|
|
addFilter(eventName: string) {
|
|
this.filters.add(eventName)
|
|
}
|
|
|
|
/**
|
|
* Remove an event from the filters. Filtered events will not be logged.
|
|
*
|
|
* ### Example
|
|
*
|
|
*```ts
|
|
* logger.removeFilter("SOME_EVENT")
|
|
*```
|
|
*/
|
|
removeFilter(eventName: string) {
|
|
this.filters.delete(eventName)
|
|
}
|
|
|
|
/**
|
|
* Replace all of the filtered events.
|
|
*
|
|
* ### Example
|
|
*
|
|
*```ts
|
|
* logger.setFilters(["SOME_EVENT", "SOME_OTHER_EVENT"])
|
|
*```
|
|
*/
|
|
setFilters(eventNames: string[]) {
|
|
this.filters = new Set(eventNames)
|
|
}
|
|
|
|
/**
|
|
* Clear the current set of event filters.
|
|
*
|
|
* ### Example
|
|
*
|
|
*```ts
|
|
* logger.clear()
|
|
*```
|
|
*/
|
|
clearFilters() {
|
|
this.filters.clear()
|
|
}
|
|
}
|
|
|
|
export default new Logger()
|