ui: fix spritemap generation for assist sessions
This commit is contained in:
parent
421b3d1dc5
commit
e95bdab478
4 changed files with 80 additions and 77 deletions
|
|
@ -43,27 +43,6 @@ export default class MessageLoader {
|
||||||
this.session = session;
|
this.session = session;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* TODO: has to be moved out of messageLoader logic somehow
|
|
||||||
* */
|
|
||||||
spriteMapSvg: SVGElement | null = null;
|
|
||||||
|
|
||||||
potentialSpriteMap: Record<string, any> = {};
|
|
||||||
|
|
||||||
domParser: DOMParser | null = null;
|
|
||||||
|
|
||||||
createSpriteMap = () => {
|
|
||||||
if (!this.spriteMapSvg) {
|
|
||||||
this.domParser = new DOMParser();
|
|
||||||
this.spriteMapSvg = document.createElementNS(
|
|
||||||
'http://www.w3.org/2000/svg',
|
|
||||||
'svg',
|
|
||||||
);
|
|
||||||
this.spriteMapSvg.setAttribute('style', 'display: none;');
|
|
||||||
this.spriteMapSvg.setAttribute('id', 'reconstructed-sprite');
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
createNewParser(
|
createNewParser(
|
||||||
shouldDecrypt = true,
|
shouldDecrypt = true,
|
||||||
onMessagesDone: (msgs: PlayerMsg[], file?: string) => void,
|
onMessagesDone: (msgs: PlayerMsg[], file?: string) => void,
|
||||||
|
|
@ -101,21 +80,6 @@ export default class MessageLoader {
|
||||||
let startTimeSet = false;
|
let startTimeSet = false;
|
||||||
|
|
||||||
msgs.forEach((msg, i) => {
|
msgs.forEach((msg, i) => {
|
||||||
if (msg.tp === MType.SetNodeAttribute) {
|
|
||||||
if (msg.value.includes('_$OPENREPLAY_SPRITE$_')) {
|
|
||||||
this.createSpriteMap();
|
|
||||||
if (!this.domParser) {
|
|
||||||
return console.error('DOM parser is not initialized?');
|
|
||||||
}
|
|
||||||
handleSprites(
|
|
||||||
this.potentialSpriteMap,
|
|
||||||
this.domParser,
|
|
||||||
msg,
|
|
||||||
this.spriteMapSvg!,
|
|
||||||
i,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (msg.tp === MType.Redux || msg.tp === MType.ReduxDeprecated) {
|
if (msg.tp === MType.Redux || msg.tp === MType.ReduxDeprecated) {
|
||||||
if ('actionTime' in msg && msg.actionTime) {
|
if ('actionTime' in msg && msg.actionTime) {
|
||||||
msg.time = msg.actionTime - this.session.startedAt;
|
msg.time = msg.actionTime - this.session.startedAt;
|
||||||
|
|
@ -153,7 +117,7 @@ export default class MessageLoader {
|
||||||
// .sort((m1, m2) => m1.time - m2.time)
|
// .sort((m1, m2) => m1.time - m2.time)
|
||||||
.sort(brokenDomSorter)
|
.sort(brokenDomSorter)
|
||||||
.sort(sortIframes);
|
.sort(sortIframes);
|
||||||
|
|
||||||
if (brokenMessages > 0) {
|
if (brokenMessages > 0) {
|
||||||
console.warn(
|
console.warn(
|
||||||
'Broken timestamp messages',
|
'Broken timestamp messages',
|
||||||
|
|
@ -333,10 +297,6 @@ export default class MessageLoader {
|
||||||
|
|
||||||
await Promise.allSettled([restDomFilesPromise, restDevtoolsFilesPromise]);
|
await Promise.allSettled([restDomFilesPromise, restDevtoolsFilesPromise]);
|
||||||
this.messageManager.onFileReadSuccess();
|
this.messageManager.onFileReadSuccess();
|
||||||
// no sprites for mobile
|
|
||||||
if (this.spriteMapSvg && 'injectSpriteMap' in this.messageManager) {
|
|
||||||
this.messageManager.injectSpriteMap(this.spriteMapSvg);
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
loadEFSMobs = async () => {
|
loadEFSMobs = async () => {
|
||||||
|
|
@ -471,40 +431,6 @@ function findBrokenNodes(nodes: any[]) {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleSprites(
|
|
||||||
potentialSpriteMap: Record<string, any>,
|
|
||||||
parser: DOMParser,
|
|
||||||
msg: Record<string, any>,
|
|
||||||
spriteMapSvg: SVGElement,
|
|
||||||
i: number,
|
|
||||||
) {
|
|
||||||
const [_, svgData] = msg.value.split('_$OPENREPLAY_SPRITE$_');
|
|
||||||
const potentialSprite = potentialSpriteMap[svgData];
|
|
||||||
if (potentialSprite) {
|
|
||||||
msg.value = potentialSprite;
|
|
||||||
} else {
|
|
||||||
const svgDoc = parser.parseFromString(svgData, 'image/svg+xml');
|
|
||||||
const originalSvg = svgDoc.querySelector('svg');
|
|
||||||
if (originalSvg) {
|
|
||||||
const symbol = document.createElementNS(
|
|
||||||
'http://www.w3.org/2000/svg',
|
|
||||||
'symbol',
|
|
||||||
);
|
|
||||||
const symbolId = `symbol-${msg.id || `ind-${i}`}`; // Generate an ID if missing
|
|
||||||
symbol.setAttribute('id', symbolId);
|
|
||||||
symbol.setAttribute(
|
|
||||||
'viewBox',
|
|
||||||
originalSvg.getAttribute('viewBox') || '0 0 24 24',
|
|
||||||
);
|
|
||||||
symbol.innerHTML = originalSvg.innerHTML;
|
|
||||||
|
|
||||||
spriteMapSvg.appendChild(symbol);
|
|
||||||
msg.value = `#${symbolId}`;
|
|
||||||
potentialSpriteMap[svgData] = `#${symbolId}`;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
window.searchOrphans = (msgs) =>
|
window.searchOrphans = (msgs) =>
|
||||||
findBrokenNodes(msgs.filter((m) => [8, 9, 10, 70].includes(m.tp)));
|
findBrokenNodes(msgs.filter((m) => [8, 9, 10, 70].includes(m.tp)));
|
||||||
|
|
|
||||||
|
|
@ -201,8 +201,16 @@ export default class MessageManager {
|
||||||
}
|
}
|
||||||
|
|
||||||
Object.values(this.tabs).forEach((tab) => tab.onFileReadSuccess?.());
|
Object.values(this.tabs).forEach((tab) => tab.onFileReadSuccess?.());
|
||||||
|
|
||||||
|
this.updateSpriteMap();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
public updateSpriteMap = () => {
|
||||||
|
if (this.spriteMapSvg) {
|
||||||
|
this.injectSpriteMap(this.spriteMapSvg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public onFileReadFailed = (...e: any[]) => {
|
public onFileReadFailed = (...e: any[]) => {
|
||||||
logger.error(e);
|
logger.error(e);
|
||||||
this.state.update({ error: true });
|
this.state.update({ error: true });
|
||||||
|
|
@ -337,9 +345,38 @@ export default class MessageManager {
|
||||||
this.state.update({ tabChangeEvents: this.tabChangeEvents });
|
this.state.update({ tabChangeEvents: this.tabChangeEvents });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
spriteMapSvg: SVGElement | null = null;
|
||||||
|
potentialSpriteMap: Record<string, any> = {};
|
||||||
|
domParser: DOMParser | null = null;
|
||||||
|
createSpriteMap = () => {
|
||||||
|
if (!this.spriteMapSvg) {
|
||||||
|
this.domParser = new DOMParser();
|
||||||
|
this.spriteMapSvg = document.createElementNS(
|
||||||
|
'http://www.w3.org/2000/svg',
|
||||||
|
'svg',
|
||||||
|
);
|
||||||
|
this.spriteMapSvg.setAttribute('style', 'display: none;');
|
||||||
|
this.spriteMapSvg.setAttribute('id', 'reconstructed-sprite');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
distributeMessage = (msg: Message & { tabId: string }): void => {
|
distributeMessage = (msg: Message & { tabId: string }): void => {
|
||||||
// @ts-ignore placeholder msg for timestamps
|
// @ts-ignore placeholder msg for timestamps
|
||||||
if (msg.tp === 9999) return;
|
if (msg.tp === 9999) return;
|
||||||
|
if (msg.tp === MType.SetNodeAttribute) {
|
||||||
|
if (msg.value.includes('_$OPENREPLAY_SPRITE$_')) {
|
||||||
|
this.createSpriteMap();
|
||||||
|
if (!this.domParser) {
|
||||||
|
return console.error('DOM parser is not initialized?');
|
||||||
|
}
|
||||||
|
handleSprites(
|
||||||
|
this.potentialSpriteMap,
|
||||||
|
this.domParser,
|
||||||
|
msg,
|
||||||
|
this.spriteMapSvg!,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
if (!this.tabs[msg.tabId]) {
|
if (!this.tabs[msg.tabId]) {
|
||||||
this.tabsAmount++;
|
this.tabsAmount++;
|
||||||
this.state.update({
|
this.state.update({
|
||||||
|
|
@ -454,3 +491,36 @@ function mapTabs(tabs: Record<string, TabSessionManager>) {
|
||||||
|
|
||||||
return tabMap;
|
return tabMap;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function handleSprites(
|
||||||
|
potentialSpriteMap: Record<string, any>,
|
||||||
|
parser: DOMParser,
|
||||||
|
msg: Record<string, any>,
|
||||||
|
spriteMapSvg: SVGElement,
|
||||||
|
) {
|
||||||
|
const [_, svgData] = msg.value.split('_$OPENREPLAY_SPRITE$_');
|
||||||
|
const potentialSprite = potentialSpriteMap[svgData];
|
||||||
|
if (potentialSprite) {
|
||||||
|
msg.value = potentialSprite;
|
||||||
|
} else {
|
||||||
|
const svgDoc = parser.parseFromString(svgData, 'image/svg+xml');
|
||||||
|
const originalSvg = svgDoc.querySelector('svg');
|
||||||
|
if (originalSvg) {
|
||||||
|
const symbol = document.createElementNS(
|
||||||
|
'http://www.w3.org/2000/svg',
|
||||||
|
'symbol',
|
||||||
|
);
|
||||||
|
const symbolId = `symbol-${msg.id || `ind-${msg.time}`}`; // Generate an ID if missing
|
||||||
|
symbol.setAttribute('id', symbolId);
|
||||||
|
symbol.setAttribute(
|
||||||
|
'viewBox',
|
||||||
|
originalSvg.getAttribute('viewBox') || '0 0 24 24',
|
||||||
|
);
|
||||||
|
symbol.innerHTML = originalSvg.innerHTML;
|
||||||
|
|
||||||
|
spriteMapSvg.appendChild(symbol);
|
||||||
|
msg.value = `#${symbolId}`;
|
||||||
|
potentialSpriteMap[svgData] = `#${symbolId}`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -43,6 +43,7 @@ export default class WebLivePlayer extends WebPlayer {
|
||||||
wpState,
|
wpState,
|
||||||
(id) => this.messageManager.getNode(id),
|
(id) => this.messageManager.getNode(id),
|
||||||
agentId,
|
agentId,
|
||||||
|
this.messageManager.updateSpriteMap,
|
||||||
uiErrorHandler,
|
uiErrorHandler,
|
||||||
);
|
);
|
||||||
this.assistManager.connect(session.agentToken!, agentId, projectId);
|
this.assistManager.connect(session.agentToken!, agentId, projectId);
|
||||||
|
|
|
||||||
|
|
@ -3,14 +3,14 @@ import type { Socket } from 'socket.io-client';
|
||||||
import type { PlayerMsg, Store } from 'App/player';
|
import type { PlayerMsg, Store } from 'App/player';
|
||||||
import CanvasReceiver from 'Player/web/assist/CanvasReceiver';
|
import CanvasReceiver from 'Player/web/assist/CanvasReceiver';
|
||||||
import { gunzipSync } from 'fflate';
|
import { gunzipSync } from 'fflate';
|
||||||
import { Message } from '../messages';
|
import { Message, MType } from '../messages';
|
||||||
import type Screen from '../Screen/Screen';
|
import type Screen from '../Screen/Screen';
|
||||||
import MStreamReader from '../messages/MStreamReader';
|
import MStreamReader from '../messages/MStreamReader';
|
||||||
import JSONRawMessageReader from '../messages/JSONRawMessageReader';
|
import JSONRawMessageReader from '../messages/JSONRawMessageReader';
|
||||||
import Call, { CallingState } from './Call';
|
import Call, { CallingState } from './Call';
|
||||||
import RemoteControl, { RemoteControlStatus } from './RemoteControl';
|
import RemoteControl, { RemoteControlStatus } from './RemoteControl';
|
||||||
import ScreenRecording, { SessionRecordingStatus } from './ScreenRecording';
|
import ScreenRecording, { SessionRecordingStatus } from './ScreenRecording';
|
||||||
|
import { debounceCall } from 'App/utils'
|
||||||
export { RemoteControlStatus, SessionRecordingStatus, CallingState };
|
export { RemoteControlStatus, SessionRecordingStatus, CallingState };
|
||||||
|
|
||||||
export enum ConnectionStatus {
|
export enum ConnectionStatus {
|
||||||
|
|
@ -82,6 +82,7 @@ export default class AssistManager {
|
||||||
private store: Store<typeof AssistManager.INITIAL_STATE>,
|
private store: Store<typeof AssistManager.INITIAL_STATE>,
|
||||||
private getNode: MessageManager['getNode'],
|
private getNode: MessageManager['getNode'],
|
||||||
public readonly agentId: number,
|
public readonly agentId: number,
|
||||||
|
private readonly updateSpriteMap: () => void,
|
||||||
public readonly uiErrorHandler?: {
|
public readonly uiErrorHandler?: {
|
||||||
error: (msg: string) => void;
|
error: (msg: string) => void;
|
||||||
},
|
},
|
||||||
|
|
@ -243,6 +244,11 @@ export default class AssistManager {
|
||||||
msg !== null;
|
msg !== null;
|
||||||
msg = reader.readNext()
|
msg = reader.readNext()
|
||||||
) {
|
) {
|
||||||
|
if (msg.tp === MType.SetNodeAttribute) {
|
||||||
|
if (msg.value.includes('_$OPENREPLAY_SPRITE$_')) {
|
||||||
|
debounceCall(this.updateSpriteMap, 250)()
|
||||||
|
}
|
||||||
|
}
|
||||||
this.handleMessage(msg, msg._index);
|
this.handleMessage(msg, msg._index);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue