refactor: improved tab recording to improve stability (#1632)
* refactor: improved tab recording to improve stability * feat: enable to import session * improve stability * feat: enable to edit session name * prevent duplicate rrweb player in the dev mode
This commit is contained in:
@@ -1,16 +1,14 @@
|
||||
import Browser from 'webextension-polyfill';
|
||||
import { nanoid } from 'nanoid';
|
||||
import type { eventWithTime } from '@rrweb/types';
|
||||
import {
|
||||
type LocalData,
|
||||
LocalDataKey,
|
||||
RecorderStatus,
|
||||
ServiceName,
|
||||
type Session,
|
||||
type RecordStartedMessage,
|
||||
type RecordStoppedMessage,
|
||||
MessageName,
|
||||
type EmitEventMessage,
|
||||
EventName,
|
||||
} from '~/types';
|
||||
import Channel from '~/utils/channel';
|
||||
import { isInCrossOriginIFrame } from '~/utils';
|
||||
@@ -46,8 +44,6 @@ void (() => {
|
||||
})();
|
||||
|
||||
async function initMainPage() {
|
||||
let bufferedEvents: eventWithTime[] = [];
|
||||
let newEvents: eventWithTime[] = [];
|
||||
let startResponseCb: ((response: RecordStartedMessage) => void) | undefined =
|
||||
undefined;
|
||||
channel.provide(ServiceName.StartRecord, async () => {
|
||||
@@ -58,24 +54,6 @@ async function initMainPage() {
|
||||
};
|
||||
});
|
||||
});
|
||||
channel.provide(ServiceName.ResumeRecord, async (params) => {
|
||||
const { events, pausedTimestamp } = params as {
|
||||
events: eventWithTime[];
|
||||
pausedTimestamp: number;
|
||||
};
|
||||
bufferedEvents = events;
|
||||
startRecord();
|
||||
return new Promise((resolve) => {
|
||||
startResponseCb = (response) => {
|
||||
const pausedTime = response.startTimestamp - pausedTimestamp;
|
||||
// Decrease the time spent in the pause state and make them look like a continuous recording.
|
||||
bufferedEvents.forEach((event) => {
|
||||
event.timestamp += pausedTime;
|
||||
});
|
||||
resolve(response);
|
||||
};
|
||||
});
|
||||
});
|
||||
let stopResponseCb: ((response: RecordStoppedMessage) => void) | undefined =
|
||||
undefined;
|
||||
channel.provide(ServiceName.StopRecord, () => {
|
||||
@@ -83,29 +61,7 @@ async function initMainPage() {
|
||||
return new Promise((resolve) => {
|
||||
stopResponseCb = (response: RecordStoppedMessage) => {
|
||||
stopResponseCb = undefined;
|
||||
const newSession = generateSession();
|
||||
response.session = newSession;
|
||||
bufferedEvents = [];
|
||||
newEvents = [];
|
||||
resolve(response);
|
||||
// clear cache
|
||||
void Browser.storage.local.set({
|
||||
[LocalDataKey.bufferedEvents]: [],
|
||||
});
|
||||
};
|
||||
});
|
||||
});
|
||||
channel.provide(ServiceName.PauseRecord, () => {
|
||||
window.postMessage({ message: MessageName.StopRecord });
|
||||
return new Promise((resolve) => {
|
||||
stopResponseCb = (response: RecordStoppedMessage) => {
|
||||
stopResponseCb = undefined;
|
||||
bufferedEvents = [];
|
||||
newEvents = [];
|
||||
resolve(response);
|
||||
void Browser.storage.local.set({
|
||||
[LocalDataKey.bufferedEvents]: response.events,
|
||||
});
|
||||
};
|
||||
});
|
||||
});
|
||||
@@ -132,15 +88,14 @@ async function initMainPage() {
|
||||
event.data.message === MessageName.RecordStopped &&
|
||||
stopResponseCb
|
||||
) {
|
||||
const data = event.data as RecordStoppedMessage;
|
||||
// On firefox, the event.data is immutable, so we need to clone it to avoid errors.
|
||||
const newData = {
|
||||
...data,
|
||||
};
|
||||
newData.events = bufferedEvents.concat(data.events);
|
||||
stopResponseCb(newData);
|
||||
const data = { ...(event.data as RecordStoppedMessage) };
|
||||
stopResponseCb(data);
|
||||
} else if (event.data.message === MessageName.EmitEvent)
|
||||
newEvents.push((event.data as EmitEventMessage).event);
|
||||
channel.emit(
|
||||
EventName.ContentScriptEmitEvent,
|
||||
(event.data as EmitEventMessage).event,
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
@@ -150,17 +105,7 @@ async function initMainPage() {
|
||||
RecorderStatus.RECORDING
|
||||
) {
|
||||
startRecord();
|
||||
bufferedEvents = localData[LocalDataKey.bufferedEvents] || [];
|
||||
}
|
||||
|
||||
// Before unload pages, cache the new events in the local storage.
|
||||
window.addEventListener('beforeunload', (event) => {
|
||||
if (!newEvents.length) return;
|
||||
event.preventDefault();
|
||||
void Browser.storage.local.set({
|
||||
[LocalDataKey.bufferedEvents]: bufferedEvents.concat(newEvents),
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
async function initCrossOriginIframe() {
|
||||
@@ -193,15 +138,3 @@ function startRecord() {
|
||||
document.documentElement.removeChild(scriptEl);
|
||||
};
|
||||
}
|
||||
|
||||
function generateSession() {
|
||||
const newSession: Session = {
|
||||
id: nanoid(),
|
||||
name: document.title,
|
||||
tags: [],
|
||||
createTimestamp: Date.now(),
|
||||
modifyTimestamp: Date.now(),
|
||||
recorderVersion: Browser.runtime.getManifest().version_name || 'unknown',
|
||||
};
|
||||
return newSession;
|
||||
}
|
||||
|
||||
@@ -8,15 +8,12 @@ import { isInCrossOriginIFrame } from '~/utils';
|
||||
* This script is injected into both main page and cross-origin IFrames through <script> tags.
|
||||
*/
|
||||
|
||||
const events: eventWithTime[] = [];
|
||||
let stopFn: (() => void) | null = null;
|
||||
|
||||
function startRecord(config: recordOptions<eventWithTime>) {
|
||||
events.length = 0;
|
||||
stopFn =
|
||||
record({
|
||||
emit: (event) => {
|
||||
events.push(event);
|
||||
postMessage({
|
||||
message: MessageName.EmitEvent,
|
||||
event,
|
||||
@@ -52,7 +49,6 @@ const messageHandler = (
|
||||
}
|
||||
postMessage({
|
||||
message: MessageName.RecordStopped,
|
||||
events,
|
||||
endTimestamp: Date.now(),
|
||||
});
|
||||
window.removeEventListener('message', messageHandler);
|
||||
|
||||
Reference in New Issue
Block a user