fix(ui): refactor state types, prep to integrate lscache, fix session types
This commit is contained in:
parent
0dea805366
commit
cf9cad7f75
12 changed files with 213 additions and 181 deletions
|
|
@ -458,7 +458,6 @@ function Performance({
|
|||
const availableCount = [fps, cpu, heap, nodes].reduce((c, av) => (av ? c + 1 : c), 0);
|
||||
const height = availableCount === 0 ? '0' : `${100 / availableCount}%`;
|
||||
|
||||
console.log(_data)
|
||||
return (
|
||||
<BottomBlock>
|
||||
<BottomBlock.Header>
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import { Store } from './types'
|
||||
|
||||
export default class SimpleSore<G, S=G> implements Store<G, S> {
|
||||
export default class SimpleSore<G extends Object, S extends Object = G> implements Store<G, S> {
|
||||
constructor(private state: G){}
|
||||
get(): G {
|
||||
return this.state
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
import { Message } from "Player/web/messages";
|
||||
|
||||
export interface Timed {
|
||||
time: number
|
||||
/** present in mobile events and in db events */
|
||||
|
|
@ -33,7 +35,10 @@ export interface SessionFilesInfo {
|
|||
sessionId: string
|
||||
isMobile: boolean
|
||||
agentToken?: string
|
||||
duration: number
|
||||
duration: {
|
||||
milliseconds: number
|
||||
valueOf: () => number
|
||||
}
|
||||
domURL: string[]
|
||||
devtoolsURL: string[]
|
||||
/** deprecated */
|
||||
|
|
@ -45,3 +50,5 @@ export interface SessionFilesInfo {
|
|||
errors: Record<string, any>[]
|
||||
agentInfo?: { email: string, name: string }
|
||||
}
|
||||
|
||||
export type PlayerMsg = Message & { tabId: string }
|
||||
|
|
@ -1,63 +1,64 @@
|
|||
import * as lstore from './localStorage'
|
||||
import * as lstore from './localStorage';
|
||||
import SimpleStore from 'App/player/common/SimpleStore';
|
||||
|
||||
import type { SimpleState } from './PlayerState'
|
||||
const SPEED_STORAGE_KEY = '__$player-speed$__';
|
||||
const SKIP_STORAGE_KEY = '__$player-skip$__';
|
||||
const SKIP_TO_ISSUE_STORAGE_KEY = '__$session-skipToIssue$__';
|
||||
const AUTOPLAY_STORAGE_KEY = '__$player-autoplay$__';
|
||||
const SHOW_EVENTS_STORAGE_KEY = '__$player-show-events$__';
|
||||
|
||||
const SPEED_STORAGE_KEY = "__$player-speed$__";
|
||||
const SKIP_STORAGE_KEY = "__$player-skip$__";
|
||||
const SKIP_TO_ISSUE_STORAGE_KEY = "__$session-skipToIssue$__";
|
||||
const AUTOPLAY_STORAGE_KEY = "__$player-autoplay$__";
|
||||
const SHOW_EVENTS_STORAGE_KEY = "__$player-show-events$__";
|
||||
const storedSpeed = lstore.number(SPEED_STORAGE_KEY, 1);
|
||||
const initialSpeed = [0.5, 1, 2, 4, 8, 16].includes(storedSpeed) ? storedSpeed : 1;
|
||||
const initialSkip = lstore.boolean(SKIP_STORAGE_KEY);
|
||||
const initialSkipToIssue = lstore.boolean(SKIP_TO_ISSUE_STORAGE_KEY);
|
||||
const initialAutoplay = lstore.boolean(AUTOPLAY_STORAGE_KEY);
|
||||
const initialShowEvents = lstore.boolean(SHOW_EVENTS_STORAGE_KEY);
|
||||
|
||||
|
||||
const storedSpeed = lstore.number(SPEED_STORAGE_KEY, 1)
|
||||
const initialSpeed = [1,2,4,8,16].includes(storedSpeed) ? storedSpeed : 1;
|
||||
const initialSkip = lstore.boolean(SKIP_STORAGE_KEY)
|
||||
const initialSkipToIssue = lstore.boolean(SKIP_TO_ISSUE_STORAGE_KEY)
|
||||
const initialAutoplay = lstore.boolean(AUTOPLAY_STORAGE_KEY)
|
||||
const initialShowEvents = lstore.boolean(SHOW_EVENTS_STORAGE_KEY)
|
||||
|
||||
export const INITIAL_STATE = {
|
||||
const INITIAL_STATE = {
|
||||
skipToIssue: initialSkipToIssue,
|
||||
autoplay: initialAutoplay,
|
||||
showEvents: initialShowEvents,
|
||||
skip: initialSkip,
|
||||
speed: initialSpeed,
|
||||
}
|
||||
};
|
||||
|
||||
const KEY_MAP = {
|
||||
speed: SPEED_STORAGE_KEY,
|
||||
skip: SKIP_STORAGE_KEY,
|
||||
skipToIssue: SKIP_TO_ISSUE_STORAGE_KEY,
|
||||
autoplay: AUTOPLAY_STORAGE_KEY,
|
||||
showEvents: SHOW_EVENTS_STORAGE_KEY,
|
||||
skip: SKIP_STORAGE_KEY,
|
||||
speed: SPEED_STORAGE_KEY,
|
||||
} as const
|
||||
|
||||
const keys = Object.keys(KEY_MAP) as (keyof typeof KEY_MAP)[];
|
||||
const booleanKeys = ['skipToIssue', 'autoplay', 'showEvents', 'skip'] as const;
|
||||
type LSCState = typeof INITIAL_STATE
|
||||
|
||||
export default class LSCache {
|
||||
static readonly INITIAL_STATE = INITIAL_STATE;
|
||||
private readonly state: SimpleStore<typeof LSCache.INITIAL_STATE>;
|
||||
|
||||
constructor() {
|
||||
this.state = new SimpleStore<typeof LSCache.INITIAL_STATE>(LSCache.INITIAL_STATE);
|
||||
}
|
||||
|
||||
type KeysOfBoolean<T> = keyof T & keyof { [ K in keyof T as T[K] extends boolean ? K : never ] : K };
|
||||
update(newState: Partial<LSCState>) {
|
||||
for (let [k, v] of Object.entries(newState) as [keyof LSCState, LSCState[keyof LSCState]][]) {
|
||||
if (k in keys) {
|
||||
localStorage.setItem(KEY_MAP[k], String(v));
|
||||
}
|
||||
}
|
||||
this.state.update(newState);
|
||||
}
|
||||
|
||||
type Entries<T> = {
|
||||
[K in keyof T]: [K, T[K]];
|
||||
}[keyof T][];
|
||||
|
||||
export default class LSCache<G extends Record<string, boolean | number | string>> {
|
||||
constructor(private state: SimpleState<G>, private keyMap: Record<keyof Partial<G>, string>) {
|
||||
}
|
||||
update(newState: Partial<G>) {
|
||||
for (let [k, v] of Object.entries(newState) as Entries<Partial<G>>) {
|
||||
if (k in this.keyMap) {
|
||||
// @ts-ignore TODO: nice typing
|
||||
//lstore[typeof v](this.keyMap[k], v)
|
||||
localStorage.setItem(this.keyMap[k], String(v))
|
||||
}
|
||||
}
|
||||
this.state.update(newState)
|
||||
}
|
||||
toggle(key: KeysOfBoolean<G>) {
|
||||
toggle(key: typeof booleanKeys[number]) {
|
||||
// @ts-ignore TODO: nice typing
|
||||
this.update({
|
||||
[key]: !this.get()[key]
|
||||
})
|
||||
[key]: !this.get()[key],
|
||||
});
|
||||
}
|
||||
|
||||
get() {
|
||||
return this.state.get()
|
||||
return this.state.get();
|
||||
}
|
||||
}
|
||||
|
|
@ -1,8 +1,7 @@
|
|||
import type { Store, SessionFilesInfo } from 'Player';
|
||||
import type { Store, SessionFilesInfo, PlayerMsg } from "Player";
|
||||
import { decryptSessionBytes } from './network/crypto';
|
||||
import MFileReader from './messages/MFileReader';
|
||||
import { loadFiles, requestEFSDom, requestEFSDevtools } from './network/loadFiles';
|
||||
import type { Message } from './messages';
|
||||
import logger from 'App/logger';
|
||||
import unpack from 'Player/common/unpack';
|
||||
import MessageManager from 'Player/web/MessageManager';
|
||||
|
|
@ -33,21 +32,21 @@ export default class MessageLoader {
|
|||
|
||||
createNewParser(
|
||||
shouldDecrypt = true,
|
||||
onMessagesDone: (msgs: PlayerMsg[], file?: string) => void,
|
||||
file?: string
|
||||
) {
|
||||
const decrypt =
|
||||
shouldDecrypt && this.session.fileKey
|
||||
? (b: Uint8Array) => decryptSessionBytes(b, this.session.fileKey!)
|
||||
: (b: Uint8Array) => Promise.resolve(b);
|
||||
// Each time called - new fileReader created
|
||||
const fileReader = new MFileReader(new Uint8Array(), this.session.startedAt);
|
||||
return (b: Uint8Array) => {
|
||||
return decrypt(b)
|
||||
.then((b) => {
|
||||
const data = unpack(b);
|
||||
return async (b: Uint8Array) => {
|
||||
try {
|
||||
const mobBytes = await decrypt(b);
|
||||
const data = unpack(mobBytes);
|
||||
fileReader.append(data);
|
||||
fileReader.checkForIndexes();
|
||||
const msgs: Array<Message & { tabId: string }> = [];
|
||||
const msgs: Array<PlayerMsg> = [];
|
||||
let finished = false;
|
||||
while (!finished) {
|
||||
const msg = fileReader.readNext();
|
||||
|
|
@ -59,29 +58,32 @@ export default class MessageLoader {
|
|||
}
|
||||
}
|
||||
|
||||
const sortedMessages = msgs.sort((m1, m2) => {
|
||||
const sortedMsgs = msgs.sort((m1, m2) => {
|
||||
return m1.time - m2.time;
|
||||
});
|
||||
|
||||
sortedMessages.forEach((msg) => {
|
||||
this.messageManager.distributeMessage(msg);
|
||||
});
|
||||
logger.info('Messages count: ', msgs.length, sortedMessages, file);
|
||||
|
||||
this.messageManager.sortDomRemoveMessages(sortedMessages);
|
||||
this.messageManager.setMessagesLoading(false);
|
||||
})
|
||||
.catch((e) => {
|
||||
onMessagesDone(sortedMsgs, file);
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
this.uiErrorHandler?.error('Error parsing file: ' + e.message);
|
||||
});
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
loadDomFiles(urls: string[], parser: (b: Uint8Array) => Promise<void>) {
|
||||
processMessages = (msgs: PlayerMsg[], file?: string) => {
|
||||
msgs.forEach((msg) => {
|
||||
this.messageManager.distributeMessage(msg);
|
||||
});
|
||||
logger.info('Messages count: ', msgs.length, msgs, file);
|
||||
|
||||
this.messageManager.sortDomRemoveMessages(msgs);
|
||||
this.messageManager.setMessagesLoading(false);
|
||||
};
|
||||
|
||||
async loadDomFiles(urls: string[], parser: (b: Uint8Array) => Promise<void>) {
|
||||
if (urls.length > 0) {
|
||||
this.store.update({ domLoading: true });
|
||||
return loadFiles(urls, parser, true).then(() => this.store.update({ domLoading: false }));
|
||||
await loadFiles(urls, parser, true);
|
||||
return this.store.update({ domLoading: false });
|
||||
} else {
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
|
@ -111,11 +113,17 @@ export default class MessageLoader {
|
|||
|
||||
const loadMethod =
|
||||
this.session.domURL && this.session.domURL.length > 0
|
||||
? { mobUrls: this.session.domURL, parser: () => this.createNewParser(true, 'dom') }
|
||||
: { mobUrls: this.session.mobsUrl, parser: () => this.createNewParser(false, 'dom') };
|
||||
? {
|
||||
mobUrls: this.session.domURL,
|
||||
parser: () => this.createNewParser(true, this.processMessages, 'dom'),
|
||||
}
|
||||
: {
|
||||
mobUrls: this.session.mobsUrl,
|
||||
parser: () => this.createNewParser(false, this.processMessages, 'dom'),
|
||||
};
|
||||
|
||||
const parser = loadMethod.parser();
|
||||
const devtoolsParser = this.createNewParser(true, 'devtools');
|
||||
const devtoolsParser = this.createNewParser(true, this.processMessages, 'devtools');
|
||||
/**
|
||||
* to speed up time to replay
|
||||
* we load first dom mob file before the rest
|
||||
|
|
@ -140,8 +148,8 @@ export default class MessageLoader {
|
|||
efsDomFilePromise,
|
||||
efsDevtoolsFilePromise,
|
||||
]);
|
||||
const domParser = this.createNewParser(false, 'domEFS');
|
||||
const devtoolsParser = this.createNewParser(false, 'devtoolsEFS');
|
||||
const domParser = this.createNewParser(false, this.processMessages, 'domEFS');
|
||||
const devtoolsParser = this.createNewParser(false, this.processMessages, 'devtoolsEFS');
|
||||
const parseDomPromise: Promise<any> =
|
||||
domData.status === 'fulfilled'
|
||||
? domParser(domData.value)
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
import { Decoder } from 'syncod';
|
||||
import logger from 'App/logger';
|
||||
|
||||
import type { Store, ILog } from 'Player';
|
||||
import type { Store, ILog, SessionFilesInfo } from 'Player';
|
||||
import ListWalker from '../common/ListWalker';
|
||||
|
||||
import MouseMoveManager from './managers/MouseMoveManager';
|
||||
|
|
@ -108,7 +108,7 @@ export default class MessageManager {
|
|||
private activeTab = '';
|
||||
|
||||
constructor(
|
||||
private readonly session: Record<string, any>,
|
||||
private readonly session: SessionFilesInfo,
|
||||
private readonly state: Store<State & { time: number }>,
|
||||
private readonly screen: Screen,
|
||||
private readonly initialLists?: Partial<InitialLists>,
|
||||
|
|
|
|||
|
|
@ -125,6 +125,9 @@ export default class TabSessionManager {
|
|||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Because we use main state (from messageManager), we have to update it this way
|
||||
* */
|
||||
updateLocalState(state: Partial<TabState>) {
|
||||
this.state.update({
|
||||
tabStates: {
|
||||
|
|
@ -283,22 +286,23 @@ export default class TabSessionManager {
|
|||
// TODO: page-wise resources list // setListsStartTime(lastLoadedLocationMsg.time)
|
||||
this.navigationStartOffset = lastLoadedLocationMsg.navigationStart - this.sessionStart;
|
||||
}
|
||||
const llEvent = this.locationEventManager.moveGetLast(t, index);
|
||||
if (!!llEvent) {
|
||||
if (llEvent.domContentLoadedTime != null) {
|
||||
const lastLocationEvent = this.locationEventManager.moveGetLast(t, index);
|
||||
if (!!lastLocationEvent) {
|
||||
if (lastLocationEvent.domContentLoadedTime != null) {
|
||||
stateToUpdate.domContentLoadedTime = {
|
||||
time: llEvent.domContentLoadedTime + this.navigationStartOffset, //TODO: predefined list of load event for the network tab (merge events & SetPageLocation: add navigationStart to db)
|
||||
value: llEvent.domContentLoadedTime,
|
||||
time: lastLocationEvent.domContentLoadedTime + this.navigationStartOffset,
|
||||
// TODO: predefined list of load event for the network tab (merge events & SetPageLocation: add navigationStart to db)
|
||||
value: lastLocationEvent.domContentLoadedTime,
|
||||
};
|
||||
}
|
||||
if (llEvent.loadTime != null) {
|
||||
if (lastLocationEvent.loadTime != null) {
|
||||
stateToUpdate.loadTime = {
|
||||
time: llEvent.loadTime + this.navigationStartOffset,
|
||||
value: llEvent.loadTime,
|
||||
time: lastLocationEvent.loadTime + this.navigationStartOffset,
|
||||
value: lastLocationEvent.loadTime,
|
||||
};
|
||||
}
|
||||
if (llEvent.domBuildingTime != null) {
|
||||
stateToUpdate.domBuildingTime = llEvent.domBuildingTime;
|
||||
if (lastLocationEvent.domBuildingTime != null) {
|
||||
stateToUpdate.domBuildingTime = lastLocationEvent.domBuildingTime;
|
||||
}
|
||||
}
|
||||
/* === */
|
||||
|
|
@ -307,12 +311,7 @@ export default class TabSessionManager {
|
|||
// @ts-ignore comes from parent state
|
||||
this.state.update({ location: lastLocationMsg.url });
|
||||
}
|
||||
// ConnectionInformation message is not used at this moment
|
||||
// const lastConnectionInfoMsg = this.connectionInfoManger.moveGetLast(t, index);
|
||||
// if (!!lastConnectionInfoMsg) {
|
||||
// stateToUpdate.connType = lastConnectionInfoMsg.type;
|
||||
// stateToUpdate.connBandwidth = lastConnectionInfoMsg.downlink;
|
||||
// }
|
||||
|
||||
const lastPerformanceTrackMessage = this.performanceTrackManager.moveGetLast(t, index);
|
||||
if (!!lastPerformanceTrackMessage) {
|
||||
stateToUpdate.performanceChartTime = lastPerformanceTrackMessage.time;
|
||||
|
|
@ -347,6 +346,9 @@ export default class TabSessionManager {
|
|||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Used to decode state messages, because they can be large we only want to decode whats rendered atm
|
||||
* */
|
||||
public decodeMessage(msg: Message) {
|
||||
return this.decoder.decode(msg);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,23 +1,19 @@
|
|||
import type { Store, SessionFilesInfo } from 'Player'
|
||||
import type { Message } from './messages'
|
||||
|
||||
import WebPlayer from './WebPlayer'
|
||||
import AssistManager from './assist/AssistManager'
|
||||
|
||||
import MFileReader from './messages/MFileReader'
|
||||
import { requestEFSDom } from './network/loadFiles'
|
||||
import type { Store, SessionFilesInfo, PlayerMsg } from 'Player';
|
||||
import WebPlayer from './WebPlayer';
|
||||
import AssistManager from './assist/AssistManager';
|
||||
import { requestEFSDom } from './network/loadFiles';
|
||||
|
||||
export default class WebLivePlayer extends WebPlayer {
|
||||
static readonly INITIAL_STATE = {
|
||||
...WebPlayer.INITIAL_STATE,
|
||||
...AssistManager.INITIAL_STATE,
|
||||
liveTimeTravel: false,
|
||||
}
|
||||
};
|
||||
|
||||
assistManager: AssistManager // public so far
|
||||
private readonly incomingMessages: Message[] = []
|
||||
private historyFileIsLoading = false
|
||||
private lastMessageInFileTime = 0
|
||||
assistManager: AssistManager; // public so far
|
||||
private readonly incomingMessages: PlayerMsg[] = [];
|
||||
private historyFileIsLoading = false;
|
||||
private lastMessageInFileTime = 0;
|
||||
|
||||
constructor(
|
||||
wpState: Store<typeof WebLivePlayer.INITIAL_STATE>,
|
||||
|
|
@ -25,79 +21,91 @@ export default class WebLivePlayer extends WebPlayer {
|
|||
config: RTCIceServer[] | null,
|
||||
agentId: number,
|
||||
projectId: number,
|
||||
uiErrorHandler?: { error: (msg: string) => void },
|
||||
uiErrorHandler?: { error: (msg: string) => void }
|
||||
) {
|
||||
super(wpState, session, true, false, uiErrorHandler)
|
||||
super(wpState, session, true, false, uiErrorHandler);
|
||||
|
||||
this.assistManager = new AssistManager(
|
||||
session,
|
||||
f => this.messageManager.setMessagesLoading(f),
|
||||
(f) => this.messageManager.setMessagesLoading(f),
|
||||
(msg) => {
|
||||
this.incomingMessages.push(msg)
|
||||
this.incomingMessages.push(msg);
|
||||
if (!this.historyFileIsLoading) {
|
||||
// TODO: fix index-ing after historyFile-load
|
||||
this.messageManager.distributeMessage(msg)
|
||||
this.messageManager.distributeMessage(msg);
|
||||
}
|
||||
},
|
||||
this.screen,
|
||||
config,
|
||||
wpState,
|
||||
(id) => this.messageManager.getNode(id),
|
||||
uiErrorHandler,
|
||||
)
|
||||
this.assistManager.connect(session.agentToken!, agentId, projectId)
|
||||
uiErrorHandler
|
||||
);
|
||||
this.assistManager.connect(session.agentToken!, agentId, projectId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads in-progress dom file from EFS directly
|
||||
* then reads it to add everything happened before "now" to message manager
|
||||
* to be able to replay it like usual
|
||||
* */
|
||||
toggleTimetravel = async () => {
|
||||
if (this.wpState.get().liveTimeTravel) {
|
||||
return
|
||||
if ((this.wpState.get() as typeof WebLivePlayer.INITIAL_STATE).liveTimeTravel) {
|
||||
return;
|
||||
}
|
||||
let result = false;
|
||||
this.historyFileIsLoading = true
|
||||
this.messageManager.setMessagesLoading(true) // do it in one place. update unique loading states each time instead
|
||||
this.messageManager.resetMessageManagers()
|
||||
this.historyFileIsLoading = true;
|
||||
this.messageManager.setMessagesLoading(true); // do it in one place. update unique loading states each time instead
|
||||
this.messageManager.resetMessageManagers();
|
||||
|
||||
try {
|
||||
const bytes = await requestEFSDom(this.session.sessionId)
|
||||
const fileReader = new MFileReader(bytes, this.session.startedAt)
|
||||
for (let msg = fileReader.readNext();msg !== null;msg = fileReader.readNext()) {
|
||||
this.messageManager.distributeMessage(msg)
|
||||
}
|
||||
const bytes = await requestEFSDom(this.session.sessionId);
|
||||
const reader = this.messageLoader.createNewParser(
|
||||
false,
|
||||
(msgs) => {
|
||||
msgs.forEach((msg) => {
|
||||
this.messageManager.distributeMessage(msg);
|
||||
});
|
||||
},
|
||||
'cobrowse dom'
|
||||
);
|
||||
await reader(bytes);
|
||||
|
||||
this.wpState.update({
|
||||
liveTimeTravel: true,
|
||||
})
|
||||
result = true
|
||||
// here we need to update also lists state, if we gonna use them this.messageManager.onFileReadSuccess
|
||||
});
|
||||
result = true;
|
||||
// here we need to update also lists state, if we're going use them this.messageManager.onFileReadSuccess
|
||||
} catch (e) {
|
||||
this.uiErrorHandler?.error('Error requesting a session file')
|
||||
console.error("EFS file download error:", e)
|
||||
this.uiErrorHandler?.error('Error requesting a session file');
|
||||
console.error('EFS file download error:', e);
|
||||
}
|
||||
|
||||
// Append previously received messages
|
||||
this.incomingMessages
|
||||
.filter(msg => msg.time >= this.lastMessageInFileTime)
|
||||
.forEach((msg) => this.messageManager.distributeMessage(msg))
|
||||
this.incomingMessages.length = 0
|
||||
.filter((msg) => msg.time >= this.lastMessageInFileTime)
|
||||
.forEach((msg) => this.messageManager.distributeMessage(msg));
|
||||
this.incomingMessages.length = 0;
|
||||
|
||||
this.historyFileIsLoading = false
|
||||
this.messageManager.setMessagesLoading(false)
|
||||
this.historyFileIsLoading = false;
|
||||
this.messageManager.setMessagesLoading(false);
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
jumpToLive = () => {
|
||||
this.wpState.update({
|
||||
live: true,
|
||||
livePlay: true,
|
||||
})
|
||||
this.jump(this.wpState.get().lastMessageTime)
|
||||
}
|
||||
});
|
||||
this.jump(this.wpState.get().lastMessageTime);
|
||||
};
|
||||
|
||||
clean = () => {
|
||||
this.incomingMessages.length = 0
|
||||
this.assistManager.clean()
|
||||
this.screen?.clean?.()
|
||||
this.incomingMessages.length = 0;
|
||||
this.assistManager.clean();
|
||||
this.screen?.clean?.();
|
||||
// @ts-ignore
|
||||
this.screen = undefined;
|
||||
super.clean()
|
||||
}
|
||||
super.clean();
|
||||
};
|
||||
}
|
||||
|
|
@ -16,7 +16,7 @@ export default class WebPlayer extends Player {
|
|||
...TargetMarker.INITIAL_STATE,
|
||||
...MessageManager.INITIAL_STATE,
|
||||
...MessageLoader.INITIAL_STATE,
|
||||
|
||||
liveTimeTravel: false,
|
||||
inspectorMode: false,
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,8 +1,7 @@
|
|||
import MessageManager from 'Player/web/MessageManager';
|
||||
import type { Socket } from 'socket.io-client';
|
||||
import type Screen from '../Screen/Screen';
|
||||
import type { Store } from '../../common/types';
|
||||
import type { Message } from '../messages';
|
||||
import type { PlayerMsg, Store } from 'App/player';
|
||||
import MStreamReader from '../messages/MStreamReader';
|
||||
import JSONRawMessageReader from '../messages/JSONRawMessageReader';
|
||||
import Call, { CallingState } from './Call';
|
||||
|
|
@ -71,7 +70,7 @@ export default class AssistManager {
|
|||
constructor(
|
||||
private session: any,
|
||||
private setMessagesLoading: (flag: boolean) => void,
|
||||
private handleMessage: (m: Message, index: number) => void,
|
||||
private handleMessage: (m: PlayerMsg, index: number) => void,
|
||||
private screen: Screen,
|
||||
private config: RTCIceServer[] | null,
|
||||
private store: Store<typeof AssistManager.INITIAL_STATE>,
|
||||
|
|
@ -159,7 +158,7 @@ export default class AssistManager {
|
|||
const reader = new MStreamReader(jmr, this.session.startedAt);
|
||||
let waitingForMessages = true;
|
||||
|
||||
const now = +new Date();
|
||||
const now = new Date().getTime();
|
||||
this.store.update({ assistStart: now });
|
||||
|
||||
// @ts-ignore
|
||||
|
|
@ -168,7 +167,8 @@ export default class AssistManager {
|
|||
return;
|
||||
}
|
||||
// @ts-ignore
|
||||
const urlObject = new URL(window.env.API_EDP || window.location.origin); // does it handle ssl automatically?
|
||||
const urlObject = new URL(window.env.API_EDP || window.location.origin);
|
||||
// does it handle ssl automatically?
|
||||
|
||||
const socket: Socket = (this.socket = io(urlObject.origin, {
|
||||
withCredentials: true,
|
||||
|
|
@ -192,7 +192,8 @@ export default class AssistManager {
|
|||
}));
|
||||
socket.on('connect', () => {
|
||||
waitingForMessages = true;
|
||||
this.setStatus(ConnectionStatus.WaitingMessages); // TODO: reconnect happens frequently on bad network
|
||||
// TODO: reconnect happens frequently on bad network
|
||||
this.setStatus(ConnectionStatus.WaitingMessages);
|
||||
});
|
||||
|
||||
socket.on('messages', (messages) => {
|
||||
|
|
@ -229,9 +230,12 @@ export default class AssistManager {
|
|||
const { tabId } = meta;
|
||||
const usedData = this.assistVersion === 1 ? evData : data;
|
||||
const { active } = usedData;
|
||||
|
||||
const currentTab = this.store.get().currentTab;
|
||||
|
||||
this.clearDisconnectTimeout();
|
||||
!this.inactiveTimeout && this.setStatus(ConnectionStatus.Connected);
|
||||
|
||||
if (typeof active === 'boolean') {
|
||||
this.clearInactiveTimeout();
|
||||
if (active) {
|
||||
|
|
@ -299,6 +303,9 @@ export default class AssistManager {
|
|||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends event ping to stats service
|
||||
* */
|
||||
public ping(event: StatsEvent, id: number) {
|
||||
this.socket?.emit(event, id);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ export default class MStreamReader {
|
|||
private idx: number = 0
|
||||
|
||||
currentTab = 'back-compatability'
|
||||
readNext(): Message & { _index: number } | null {
|
||||
readNext(): Message & { _index: number, tabId: string } | null {
|
||||
let msg = this.r.readMessage()
|
||||
if (msg === null) { return null }
|
||||
if (msg.tp === MType.Timestamp) {
|
||||
|
|
|
|||
|
|
@ -166,7 +166,7 @@ export default class Session {
|
|||
canvasURL: ISession['canvasURL'];
|
||||
live: ISession['live'];
|
||||
startedAt: ISession['startedAt'];
|
||||
duration: ISession['duration'];
|
||||
duration: Duration;
|
||||
events: ISession['events'];
|
||||
stackEvents: ISession['stackEvents'];
|
||||
metadata: ISession['metadata'];
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue