* fix(ui): fix up mobile recordings display * fix(ui): some messages * fix(ui): some messages * fix(player): fix msg generation for ios messages * feat(player): add generic mmanager interface for ios player impl * feat(player): mobile player and message manager; touch manager; videoplayer * feat(player): add iphone shells, add log panel, * feat(player): detect ios sessions and inject correct player * feat(player): move screen mapper to utils * feat(player): events panel for mobile, map shell sizes to device type data, * feat(player): added network tab to mobile player; unify network message (ios and web) * feat(player): resize canvas up to phone screen size, fix capitalize util * feat(player): some general changes to support mobile events and network entries * feat(player): remove swipes from timeline * feat(player): more stuff for list walker * fix(ui): performance tab, mobile project typings and form * fix(ui):some ui touches for ios replayer shell * fix(ui): more fixes for ui, new onboarding screen for mobile projects * feat(ui): mobile overview panel (xray) * feat(ui): fixes for phone shell and tap events * fix(tracker): change phone shells and sizes * fix(tracker): fix border on replay screen * feat(ui): use crashes from db to show in session * feat(ui): use event name for xray * feat(ui): some overall ui fixes * feat(ui): IOS -> iOS * feat(ui): change tags to ant d * fix(ui): fast fix * fix(ui): fix for capitalizer * fix(ui): fix for browser display * fix(ui): fix for note popup * fix(ui): change exceptions display * fix(ui): add click rage to ios xray * fix(ui): some icons and resizing * fix(ui): fix ios context menu overlay, fix console logs creation for ios * feat(ui): added icons * feat(ui): performance warnings * feat(ui): performance warnings * feat(ui): different styles * feat(ui): rm debug true * feat(ui): fix warnings display * feat(ui): some styles for animation * feat(ui): add some animations to warnings * feat(ui): move perf warnings to performance graph * feat(ui): hide/show warns dynamically * feat(ui): new mobile touch animation * fix(tracker): update msg for ios * fix(ui): taprage fixes * fix(ui): regenerate icons and messages * fix(ui): fix msgs * fix(backend): fix events gen * fix(backend): fix userid msg
228 lines
5.3 KiB
TypeScript
228 lines
5.3 KiB
TypeScript
import {Message} from "Player/web/messages";
|
|
import type { Store, Interval } from 'Player';
|
|
|
|
|
|
const fps = 60
|
|
const performance: { now: () => number } = window.performance || { now: Date.now.bind(Date) }
|
|
const requestAnimationFrame: typeof window.requestAnimationFrame =
|
|
window.requestAnimationFrame ||
|
|
// @ts-ignore
|
|
window.webkitRequestAnimationFrame ||
|
|
// @ts-ignore
|
|
window.mozRequestAnimationFrame ||
|
|
// @ts-ignore
|
|
window.oRequestAnimationFrame ||
|
|
// @ts-ignore
|
|
window.msRequestAnimationFrame ||
|
|
(callback => window.setTimeout(() => { callback(performance.now()) }, 1000 / fps))
|
|
const cancelAnimationFrame =
|
|
window.cancelAnimationFrame ||
|
|
// @ts-ignore
|
|
window.mozCancelAnimationFrame ||
|
|
window.clearTimeout
|
|
|
|
export interface IMessageManager {
|
|
onFileReadSuccess(): void;
|
|
onFileReadFailed(e: any): void;
|
|
onFileReadFinally(): void;
|
|
startLoading(): void;
|
|
resetMessageManagers(): void;
|
|
move(t: number): any;
|
|
distributeMessage(msg: Message): void;
|
|
setMessagesLoading(messagesLoading: boolean): void;
|
|
clean(): void;
|
|
_sortMessagesHack: (msgs: Message[]) => void;
|
|
}
|
|
|
|
export interface SetState {
|
|
time: number
|
|
playing: boolean
|
|
completed: boolean
|
|
live: boolean
|
|
livePlay: boolean
|
|
freeze: boolean
|
|
|
|
endTime: number
|
|
}
|
|
|
|
export interface GetState extends SetState {
|
|
skip: boolean
|
|
speed: number
|
|
skipIntervals: Interval[]
|
|
ready: boolean
|
|
|
|
lastMessageTime: number
|
|
}
|
|
|
|
export default class Animator {
|
|
static INITIAL_STATE: SetState = {
|
|
time: 0,
|
|
playing: false,
|
|
completed: false,
|
|
live: false,
|
|
livePlay: false,
|
|
freeze: false,
|
|
|
|
endTime: 0,
|
|
} as const
|
|
|
|
private animationFrameRequestId: number = 0
|
|
|
|
constructor(private store: Store<GetState>, private mm: IMessageManager) {
|
|
|
|
// @ts-ignore
|
|
window.playerJump = this.jump.bind(this)
|
|
}
|
|
|
|
private setTime(time: number) {
|
|
this.store.update({
|
|
time,
|
|
completed: false,
|
|
})
|
|
this.mm.move(time)
|
|
}
|
|
|
|
private startAnimation() {
|
|
let prevTime = this.store.get().time
|
|
let animationPrevTime = performance.now()
|
|
|
|
const frameHandler = (animationCurrentTime: number) => {
|
|
const {
|
|
speed,
|
|
skip,
|
|
skipIntervals,
|
|
endTime,
|
|
live,
|
|
livePlay,
|
|
ready, // = messagesLoading || cssLoading || disconnected
|
|
|
|
lastMessageTime,
|
|
} = this.store.get()
|
|
|
|
const diffTime = !ready
|
|
? 0
|
|
: Math.max(animationCurrentTime - animationPrevTime, 0) * (live ? 1 : speed)
|
|
|
|
let time = prevTime + diffTime
|
|
|
|
const skipInterval = skip && skipIntervals.find(si => si.contains(time))
|
|
if (skipInterval) time = skipInterval.end
|
|
|
|
if (time < 0) { time = 0 } // ?
|
|
//const fmt = getFirstMessageTime();
|
|
//if (time < fmt) time = fmt; // ?
|
|
|
|
// if (livePlay && time < endTime) { time = endTime }
|
|
// === live only
|
|
if (livePlay && time < lastMessageTime) { time = lastMessageTime }
|
|
if (endTime < lastMessageTime) {
|
|
this.store.update({
|
|
endTime: lastMessageTime,
|
|
})
|
|
}
|
|
// ===
|
|
|
|
prevTime = time
|
|
animationPrevTime = animationCurrentTime
|
|
|
|
const completed = !live && time >= endTime
|
|
if (completed) {
|
|
this.setTime(endTime)
|
|
return this.store.update({
|
|
playing: false,
|
|
completed: true,
|
|
})
|
|
}
|
|
|
|
// === live only
|
|
if (live && time > endTime) {
|
|
this.store.update({
|
|
endTime: time,
|
|
})
|
|
}
|
|
// ===
|
|
|
|
this.setTime(time)
|
|
this.animationFrameRequestId = requestAnimationFrame(frameHandler)
|
|
}
|
|
this.animationFrameRequestId = requestAnimationFrame(frameHandler)
|
|
}
|
|
|
|
play() {
|
|
if (this.store.get().freeze) return this.pause()
|
|
if (this.store.get().ready) {
|
|
cancelAnimationFrame(this.animationFrameRequestId)
|
|
this.store.update({ playing: true })
|
|
this.startAnimation()
|
|
} else {
|
|
setTimeout(() => {
|
|
this.play()
|
|
}, 250)
|
|
}
|
|
}
|
|
|
|
pause() {
|
|
cancelAnimationFrame(this.animationFrameRequestId)
|
|
this.store.update({ playing: false })
|
|
}
|
|
|
|
freeze() {
|
|
return new Promise<void>(res => {
|
|
if (this.store.get().ready) {
|
|
// making sure that replay is displayed completely
|
|
setTimeout(() => {
|
|
this.store.update({ freeze: true })
|
|
this.pause()
|
|
res()
|
|
}, 250)
|
|
} else {
|
|
setTimeout(() => res(this.freeze()), 500)
|
|
}
|
|
})
|
|
}
|
|
|
|
togglePlay = () => {
|
|
const { playing, completed } = this.store.get()
|
|
if (playing) {
|
|
this.pause()
|
|
} else if (completed) {
|
|
this.setTime(0)
|
|
this.play()
|
|
} else {
|
|
this.play()
|
|
}
|
|
}
|
|
|
|
// jump by index?
|
|
jump = (time: number) => {
|
|
if (this.store.get().playing) {
|
|
cancelAnimationFrame(this.animationFrameRequestId)
|
|
this.setTime(time)
|
|
this.startAnimation()
|
|
this.store.update({ livePlay: time === this.store.get().endTime })
|
|
} else {
|
|
this.setTime(time)
|
|
this.store.update({ livePlay: time === this.store.get().endTime })
|
|
}
|
|
}
|
|
|
|
jumpInterval(interval: number) {
|
|
const { endTime, time } = this.store.get()
|
|
|
|
if (interval > 0) {
|
|
return this.jump(
|
|
Math.min(
|
|
endTime,
|
|
time + interval
|
|
)
|
|
);
|
|
} else {
|
|
return this.jump(
|
|
Math.max(
|
|
0,
|
|
time + interval
|
|
)
|
|
);
|
|
}
|
|
}
|
|
}
|