Skip to content

Commit

Permalink
feat(replay): remove ui tabs; nav to session tabs
Browse files Browse the repository at this point in the history
  • Loading branch information
blakebyrnes committed Sep 25, 2020
1 parent 8ae0d75 commit df8e21c
Show file tree
Hide file tree
Showing 87 changed files with 1,616 additions and 2,742 deletions.
2 changes: 1 addition & 1 deletion core/lib/Session.ts
Expand Up @@ -127,7 +127,7 @@ export default class Session {
}
await this.mitmRequestSession.close();
await this.proxy.close();
await this.sessionState.saveState();
await this.sessionState.close();
try {
await this.browserContext?.close();
} catch (error) {
Expand Down
1 change: 0 additions & 1 deletion injected-scripts/interfaces/IScrollEvent.ts
@@ -1,4 +1,3 @@
// tslint:disable-next-line
import { CommandId, ISOTimestamp } from './GenericTypes';

type ScrollX = number;
Expand Down
18 changes: 17 additions & 1 deletion injected-scripts/scripts/pageEventsRecorder.ts
Expand Up @@ -51,7 +51,6 @@ class NodeTracker {
return this.nodeIds.get(node);
}

recorder.extractChanges();
this.nextId += 1;
const id = this.nextId;
this.nodeIds.set(node, id);
Expand Down Expand Up @@ -91,6 +90,23 @@ class PageEventsRecorder {

constructor() {
this.observer = new MutationObserver(this.onMutation.bind(this));
if (document && document.childNodes.length) {
const mutations: MutationRecord[] = [
{
type: 'childList' as any,
addedNodes: document.childNodes,
removedNodes: [] as any,
nextSibling: null,
previousSibling: null,
oldValue: null,
attributeNamespace: null,
attributeName: null,
target: document,
},
];

this.onMutation(mutations);
}
this.observer.observe(document, {
attributes: true,
childList: true,
Expand Down
2 changes: 1 addition & 1 deletion injected-scripts/tsconfig.json
@@ -1,7 +1,7 @@
{
"compilerOptions": {
"target": "ES2018",
"lib": ["DOM", "DOM.Iterable", "ES2018"],
"lib": ["dom", "dom.iterable", "es2018"],
"declaration": true,
"skipLibCheck": true,
"rootDir": ".",
Expand Down
157 changes: 49 additions & 108 deletions replay-app/backend/Application.ts
Expand Up @@ -8,6 +8,7 @@ import storage from './storage';
import Window from './models/Window';
import IReplayMeta from '../shared/interfaces/IReplayMeta';

// NOTE: this has to come before app load
protocol.registerSchemesAsPrivileged([
{ scheme: 'app', privileges: { secure: true, standard: true } },
]);
Expand Down Expand Up @@ -70,13 +71,16 @@ export default class Application {

ReplayApi.serverStartPath = replayApiPackagePath;

await this.loadSessionReplay({
dataLocation,
sessionName,
sessionId,
scriptInstanceId,
sessionStateApi,
});
await this.loadSessionReplay(
{
dataLocation,
sessionName,
sessionId,
scriptInstanceId,
sessionStateApi,
},
true,
);
}

private createWindowIfNeeded() {
Expand All @@ -85,7 +89,7 @@ export default class Application {
}
}

private async loadSessionReplay(replay: IReplayMeta, useCurrentTab = false) {
private async loadSessionReplay(replay: IReplayMeta, findOpenReplayScriptWindow = false) {
let replayApi: ReplayApi;
try {
replayApi = await ReplayApi.connect(replay);
Expand All @@ -102,17 +106,18 @@ export default class Application {
scriptEntrypoint: replayApi.saSession.scriptEntrypoint,
});

if (Window.noneOpen()) {
return Window.create(replayApi);
if (findOpenReplayScriptWindow) {
const match = Window.list.find(
x => x.replayApi?.saSession?.scriptEntrypoint === replay.scriptEntrypoint,
);
if (match) return match.openReplayApi(replayApi);
}

// ToDo: need to search windows/tabs for same session
const window = Window.current;
if (useCurrentTab) {
window.openReplayApi(replayApi);
} else {
window.createReplayTab(replayApi);
if (Window.noneOpen()) {
return Window.create({ replayApi });
}

await Window.current.openReplayApi(replayApi);
}

private bindEventHandlers() {
Expand Down Expand Up @@ -153,31 +158,8 @@ export default class Application {
window.browserWindow.close();
});

ipcMain.on('window:fix-dragging', () => {
const window = Window.current;
window.fixDragging();
});

// TABS

ipcMain.handle('tab:create', (e, options, sendToRenderer) => {
return Window.current.createAppTab(options, false, sendToRenderer);
});

ipcMain.on('tab:print', () => {
Window.current.selectedTab.webContents.print();
});

ipcMain.handle('tab:select', (e, tabId: number) => {
Window.current.selectedTabId = tabId;
});

ipcMain.on('tab:destroy', (e, tabId: number) => {
Window.current.destroyTab(tabId);
});

ipcMain.on('tab:reload', () => {
Window.current.selectedTab.webContents.reload();
ipcMain.on('window:print', () => {
Window.current.activeView.webContents.print();
});

// OVERLAYS
Expand All @@ -196,50 +178,45 @@ export default class Application {
this.overlayManager.getByWebContentsId(webContentsId).hide();
});

ipcMain.handle('overlay:is-visible', (e, overlay) => {
return Application.instance.overlayManager.isVisible(overlay);
});

// GOTO
ipcMain.on('go-back', (e, location) => {
Window.current.goBack();
});

ipcMain.on('navigate-to-location', (e, location, useCurrentTab) => {
const currentWindow = Window.current;
if (useCurrentTab) {
currentWindow.openAppLocation(location);
} else {
currentWindow.createAppTab({ location, active: true });
}
ipcMain.on('go-forward', (e, location) => {
Window.current.goForward();
});

ipcMain.on('navigate-to-location', (e, location) => {
Window.current.openAppLocation(location);
});

ipcMain.on('navigate-to-history', async (e, replayMeta, useCurrentTab) => {
await this.loadSessionReplay(replayMeta, useCurrentTab);
ipcMain.on('navigate-to-history', async (e, replayMeta) => {
await this.loadSessionReplay(replayMeta);
});

ipcMain.on('navigate-to-session-page', async (e, page: { id: number; url: string }) => {
const replayTab = Window.current?.selectedReplayTab;
if (!replayTab) return;
const offset = replayTab.tabState.getPageOffset(page);
replayTab.changeTickOffset(offset);
ipcMain.on('navigate-to-session', (e, session: { id: number; name: string }) => {});

ipcMain.on('navigate-to-session-tab', (e, tab: { id: string }) => {
Window.current?.loadReplayTab(tab.id);
});

// TICKS
let tickDebounce: NodeJS.Timeout;
ipcMain.on('on-tick', (e, tickValue) => {
clearTimeout(tickDebounce);
const replayTab = Window.current?.selectedReplayTab;
if (!replayTab) return;
tickDebounce = setTimeout((tab, val) => tab.onTick(val), 10, replayTab, tickValue);
const replayView = Window.current?.replayView;
if (!replayView) return;
tickDebounce = setTimeout(() => replayView.onTick(tickValue), 10);
});

ipcMain.handle('next-tick', () => {
const replayTab = Window.current?.selectedReplayTab;
if (!replayTab) return;
return replayTab.nextTick();
return Window.current?.replayView?.nextTick();
});

ipcMain.on('on-tick-hover', (e, containerRect, tickValue) => {
const replayTab = Window.current?.selectedReplayTab;
if (!replayTab) return;
replayTab.onTickHover(containerRect, tickValue);
Window.current?.replayView?.onTickHover(containerRect, tickValue);
});

// SETTINGS
Expand Down Expand Up @@ -267,19 +244,16 @@ export default class Application {
if (result.filePaths.length) {
const [filename] = result.filePaths;
if (filename.endsWith('.db')) {
return this.loadSessionReplay({ dataLocation: filename }, true);
return this.loadSessionReplay({ dataLocation: filename });
}
let sessionContainerDir = Path.dirname(filename);
while (Fs.existsSync(sessionContainerDir)) {
const sessionsDir = Fs.existsSync(`${sessionContainerDir}/.sessions`);
if (sessionsDir) {
return this.loadSessionReplay(
{
dataLocation: `${sessionContainerDir}/.sessions`,
scriptEntrypoint: filename,
},
true,
);
return this.loadSessionReplay({
dataLocation: `${sessionContainerDir}/.sessions`,
scriptEntrypoint: filename,
});
}
sessionContainerDir = Path.resolve(sessionContainerDir, '..');
}
Expand All @@ -294,39 +268,6 @@ export default class Application {
ipcMain.handle('fetch-history', () => {
return storage.fetchHistory();
});

ipcMain.handle('fetch-script-instances', () => {
const replayTab = Window.current?.selectedReplayTab;
if (!replayTab) return;

const replayApi = replayTab.replayApi;
return replayApi.saSession.relatedScriptInstances.map(x => {
return {
...x,
scriptInstanceId: x.id,
isActive: replayApi.saSession.scriptInstanceId === x.id,
dataLocation: replayApi.saSession.dataLocation,
sessionName: replayApi.saSession.name,
};
});
});

ipcMain.handle('fetch-sessions', () => {
const replayApi = Window.current?.selectedReplayTab?.replayApi;
if (!replayApi) return;
return replayApi.saSession.relatedSessions;
});

ipcMain.handle('fetch-session-pages', () => {
const tab = Window.current.selectedReplayTab;
if (!tab) return;
return tab.tabState.pages.map(x => {
return {
...x,
isActive: tab.tabState.urlOrigin === x.url || `${tab.tabState.urlOrigin}/` === x.url,
};
});
});
}

private registerFileProtocol() {
Expand Down
16 changes: 6 additions & 10 deletions replay-app/backend/api/ReplayResources.ts
@@ -1,7 +1,6 @@
import { ClientHttp2Stream } from 'http2';
import * as zlib from 'zlib';
import { PassThrough } from 'stream';
import getResolvable from '~shared/utils/promise';
import * as zlib from "zlib";
import { PassThrough } from "stream";
import getResolvable from "~shared/utils/promise";

export default class ReplayResources {
private resources: {
Expand All @@ -11,8 +10,8 @@ export default class ReplayResources {
};
} = {};

public async onResource(
http2Stream: ClientHttp2Stream,
public onResource(
data: Buffer,
resourceMeta: {
url: string;
headers: any;
Expand All @@ -24,16 +23,13 @@ export default class ReplayResources {
const { url, headers, statusCode, type, tabId } = resourceMeta;
this.initResource(url);

const data: Buffer[] = [];
for await (const chunk of http2Stream) data.push(chunk);

const headerMap = new Map<string, string>();
for (const [k, v] of Object.entries(headers)) {
headerMap.set(k.toLowerCase(), v as string);
}

this.resources[url].resolve({
data: Buffer.concat(data),
data,
headers: headerMap,
tabId,
statusCode,
Expand Down

0 comments on commit df8e21c

Please sign in to comment.