change(ui): split session info into separate calls for faster replay time
This commit is contained in:
parent
89d45d2247
commit
d7dc9b684f
8 changed files with 375 additions and 179 deletions
|
|
@ -2,7 +2,7 @@ import React from 'react';
|
|||
import { useEffect, useState } from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
import usePageTitle from 'App/hooks/usePageTitle';
|
||||
import { fetch as fetchSession } from 'Duck/sessions';
|
||||
import { fetchV2 } from "Duck/sessions";
|
||||
import { fetchList as fetchSlackList } from 'Duck/integrations/slack';
|
||||
import { Link, NoContent, Loader } from 'UI';
|
||||
import { sessions as sessionsRoute } from 'App/routes';
|
||||
|
|
@ -17,14 +17,14 @@ function Session({
|
|||
sessionId,
|
||||
loading,
|
||||
hasErrors,
|
||||
fetchSession,
|
||||
fetchV2,
|
||||
}) {
|
||||
usePageTitle("OpenReplay Session Player");
|
||||
const [ initializing, setInitializing ] = useState(true)
|
||||
const { sessionStore } = useStore();
|
||||
useEffect(() => {
|
||||
if (sessionId != null) {
|
||||
fetchSession(sessionId)
|
||||
fetchV2(sessionId)
|
||||
} else {
|
||||
console.error("No sessionID in route.")
|
||||
}
|
||||
|
|
@ -63,6 +63,6 @@ export default withPermissions(['SESSION_REPLAY'], '', true)(connect((state, pro
|
|||
session: state.getIn([ 'sessions', 'current' ]),
|
||||
};
|
||||
}, {
|
||||
fetchSession,
|
||||
fetchSlackList,
|
||||
fetchV2,
|
||||
})(Session));
|
||||
|
|
|
|||
|
|
@ -64,6 +64,12 @@ function WebPlayer(props: any) {
|
|||
return () => WebPlayerInst.clean();
|
||||
}, [session.sessionId]);
|
||||
|
||||
React.useEffect(() => {
|
||||
if (session.events.length > 0 || session.errors.length > 0) {
|
||||
contextValue.player.updateLists(session)
|
||||
}
|
||||
}, [session.events, session.errors])
|
||||
|
||||
const isPlayerReady = contextValue.store?.get().ready
|
||||
|
||||
React.useEffect(() => {
|
||||
|
|
|
|||
|
|
@ -91,6 +91,7 @@ function Timeline(props: IProps) {
|
|||
}
|
||||
|
||||
const time = getTime(e);
|
||||
if (!time) return;
|
||||
const tz = settingsStore.sessionSettings.timezone.value
|
||||
const timeStr = DateTime.fromMillis(props.startedAt + time).setZone(tz).toFormat(`hh:mm:ss a`)
|
||||
const timeLineTooltip = {
|
||||
|
|
|
|||
|
|
@ -1,18 +1,25 @@
|
|||
import { List, Map } from 'immutable';
|
||||
import Session from 'Types/session';
|
||||
import ErrorStack from 'Types/session/errorStack';
|
||||
import { Location } from 'Types/session/event'
|
||||
import { EventData, Location } from "Types/session/event";
|
||||
import Watchdog from 'Types/watchdog';
|
||||
import { clean as cleanParams } from 'App/api_client';
|
||||
import withRequestState, { RequestTypes } from './requestStateCreator';
|
||||
import { getRE, setSessionFilter, getSessionFilter, compareJsonObjects, cleanSessionFilters } from 'App/utils';
|
||||
import { LAST_7_DAYS } from 'Types/app/period';
|
||||
import { getDateRangeFromValue } from 'App/dateRange';
|
||||
import APIClient from 'App/api_client';
|
||||
import { FETCH_ACCOUNT, UPDATE_JWT } from "Duck/user";
|
||||
import logger from "App/logger";
|
||||
|
||||
const name = 'sessions';
|
||||
const FETCH_LIST = new RequestTypes('sessions/FETCH_LIST');
|
||||
const FETCH_AUTOPLAY_LIST = new RequestTypes('sessions/FETCH_AUTOPLAY_LIST');
|
||||
const FETCH = new RequestTypes('sessions/FETCH');
|
||||
const FETCH = new RequestTypes('sessions/FETCH')
|
||||
const FETCHV2 = new RequestTypes('sessions/FETCHV2')
|
||||
const FETCH_EVENTS = new RequestTypes('sessions/FETCH_EVENTS');
|
||||
const FETCH_NOTES = new RequestTypes('sessions/FETCH_NOTES');
|
||||
|
||||
const FETCH_FAVORITE_LIST = new RequestTypes('sessions/FETCH_FAVORITE_LIST');
|
||||
const FETCH_LIVE_LIST = new RequestTypes('sessions/FETCH_LIVE_LIST');
|
||||
const TOGGLE_FAVORITE = new RequestTypes('sessions/TOGGLE_FAVORITE');
|
||||
|
|
@ -160,11 +167,82 @@ const reducer = (state = initialState, action: IAction) => {
|
|||
}
|
||||
});
|
||||
});
|
||||
return state
|
||||
.set('current', session)
|
||||
.set('eventsIndex', matching)
|
||||
.set('visitedEvents', visitedEvents)
|
||||
.set('host', visitedEvents[0] && visitedEvents[0].host);
|
||||
}
|
||||
case FETCHV2.SUCCESS: {
|
||||
const session = new Session(action.data);
|
||||
|
||||
return state
|
||||
.set('current', session)
|
||||
.set('eventsIndex', matching)
|
||||
.set('visitedEvents', visitedEvents)
|
||||
.set('host', visitedEvents[0] && visitedEvents[0].host);
|
||||
}
|
||||
case FETCH_EVENTS.SUCCESS: {
|
||||
const {
|
||||
errors,
|
||||
events,
|
||||
issues,
|
||||
resources,
|
||||
stackEvents,
|
||||
userEvents
|
||||
} = action.data as { errors: any[], events: any[], issues: any[], resources: any[], stackEvents: any[], userEvents: EventData[] };
|
||||
const filterEvents = action.filter.events as Record<string, any>[];
|
||||
const session = state.get('current') as Session;
|
||||
const matching: number[] = [];
|
||||
|
||||
const visitedEvents: Location[] = [];
|
||||
const tmpMap = new Set();
|
||||
events.forEach((event) => {
|
||||
// @ts-ignore assume that event is LocationEvent
|
||||
if (event.type === 'LOCATION' && !tmpMap.has(event.url)) {
|
||||
// @ts-ignore assume that event is LocationEvent
|
||||
tmpMap.add(event.url);
|
||||
// @ts-ignore assume that event is LocationEvent
|
||||
visitedEvents.push(event);
|
||||
}
|
||||
});
|
||||
|
||||
filterEvents.forEach(({ key, operator, value }) => {
|
||||
events.forEach((e, index) => {
|
||||
if (key === e.type) {
|
||||
// @ts-ignore assume that event is LocationEvent
|
||||
const val = e.type === 'LOCATION' ? e.url : e.value;
|
||||
if (operator === 'is' && value === val) {
|
||||
matching.push(index);
|
||||
}
|
||||
if (operator === 'contains' && val.includes(value)) {
|
||||
matching.push(index);
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
const newSession = session.addEvents(
|
||||
events,
|
||||
errors,
|
||||
issues,
|
||||
resources,
|
||||
stackEvents,
|
||||
userEvents
|
||||
);
|
||||
|
||||
const forceUpdate = state.set('current', {})
|
||||
return forceUpdate
|
||||
.set('current', newSession)
|
||||
.set('eventsIndex', matching)
|
||||
.set('visitedEvents', visitedEvents)
|
||||
.set('host', visitedEvents[0] && visitedEvents[0].host);
|
||||
}
|
||||
case FETCH_NOTES.SUCCESS: {
|
||||
const notes = action.data;
|
||||
if (notes.length > 0) {
|
||||
const session = state.get('current') as Session;
|
||||
const newSession = session.addNotes(notes);
|
||||
return state.set('current', newSession);
|
||||
}
|
||||
return state
|
||||
}
|
||||
case FETCH_FAVORITE_LIST.SUCCESS:
|
||||
return state.set('favoriteList', action.data.map(s => new Session(s)));
|
||||
|
|
@ -321,6 +399,59 @@ export const fetch =
|
|||
});
|
||||
};
|
||||
|
||||
function parseError(e: any) {
|
||||
try {
|
||||
return [...JSON.parse(e).errors] || [];
|
||||
} catch {
|
||||
return Array.isArray(e) ? e : [e];
|
||||
}
|
||||
}
|
||||
|
||||
// implementing custom middleware-like request to keep the behavior
|
||||
// TODO: move all to mobx
|
||||
export const fetchV2 = (sessionId: string) =>
|
||||
(dispatch, getState) => {
|
||||
const apiClient = new APIClient()
|
||||
const apiGet = (url: string, dispatch: any, FAILURE: string) => apiClient.get(url)
|
||||
.then(async (response) => {
|
||||
if (response.status === 403) {
|
||||
dispatch({ type: FETCH_ACCOUNT.FAILURE });
|
||||
}
|
||||
if (!response.ok) {
|
||||
const text = await response.text();
|
||||
return Promise.reject(text);
|
||||
}
|
||||
return response.json();
|
||||
})
|
||||
.then((json) => json || {})
|
||||
.catch(async (e) => {
|
||||
const data = await e.response?.json();
|
||||
logger.error('Error during API request. ', e);
|
||||
return dispatch({ type: FAILURE, errors: data ? parseError(data.errors) : [] });
|
||||
});
|
||||
|
||||
const filter = getState().getIn(['filters', 'appliedFilter'])
|
||||
apiGet(`/sessions/${sessionId}/replay`, dispatch, FETCH.FAILURE)
|
||||
.then(async ({ jwt, errors, data }) => {
|
||||
if (errors) {
|
||||
dispatch({ type: FETCH.FAILURE, errors, data });
|
||||
} else {
|
||||
dispatch({ type: FETCHV2.SUCCESS, data, ...filter });
|
||||
|
||||
let [events, notes] = await Promise.all([
|
||||
apiGet(`/sessions/${sessionId}/events`, dispatch, FETCH_EVENTS.FAILURE),
|
||||
apiGet(`/sessions/${sessionId}/notes`, dispatch, FETCH_NOTES.FAILURE),
|
||||
]);
|
||||
dispatch({ type: FETCH_EVENTS.SUCCESS, data: events.data, filter });
|
||||
dispatch({ type: FETCH_NOTES.SUCCESS, data: notes.data });
|
||||
}
|
||||
if (jwt) {
|
||||
dispatch({ type: UPDATE_JWT, data: jwt });
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
export function clearCurrentSession() {
|
||||
return {
|
||||
type: CLEAR_CURRENT_SESSION
|
||||
|
|
|
|||
|
|
@ -108,7 +108,7 @@ export default class MessageManager {
|
|||
private scrollManager: ListWalker<SetViewportScroll> = new ListWalker();
|
||||
|
||||
public readonly decoder = new Decoder();
|
||||
private readonly lists: Lists;
|
||||
private lists: Lists;
|
||||
|
||||
private activityManager: ActivityManager | null = null;
|
||||
|
||||
|
|
@ -137,6 +137,18 @@ export default class MessageManager {
|
|||
this.activityManager = new ActivityManager(this.session.duration.milliseconds) // only if not-live
|
||||
}
|
||||
|
||||
public updateLists(lists: Partial<InitialLists>) {
|
||||
this.lists = new Lists(lists)
|
||||
|
||||
lists?.event?.forEach((e: Record<string, string>) => {
|
||||
if (e.type === EVENT_TYPES.LOCATION) {
|
||||
this.locationEventManager.append(e);
|
||||
}
|
||||
})
|
||||
|
||||
this.state.update({ ...this.lists.getFullListsState() });
|
||||
}
|
||||
|
||||
private setCSSLoading = (cssLoading: boolean) => {
|
||||
this.screen.displayFrame(!cssLoading)
|
||||
this.state.update({ cssLoading, ready: !this.state.get().messagesLoading && !cssLoading })
|
||||
|
|
|
|||
|
|
@ -68,6 +68,21 @@ export default class WebPlayer extends Player {
|
|||
|
||||
}
|
||||
|
||||
updateLists = (session: any) => {
|
||||
let lists = {
|
||||
event: session.events || [],
|
||||
stack: session.stackEvents || [],
|
||||
exceptions: session.errors?.map(({ name, ...rest }: any) =>
|
||||
Log({
|
||||
level: LogLevel.ERROR,
|
||||
value: name,
|
||||
...rest,
|
||||
})
|
||||
) || [],
|
||||
}
|
||||
this.messageManager.updateLists(lists)
|
||||
}
|
||||
|
||||
attach = (parent: HTMLElement, isClickmap?: boolean) => {
|
||||
this.screen.attach(parent)
|
||||
if (!isClickmap) {
|
||||
|
|
|
|||
|
|
@ -32,7 +32,7 @@ interface InputEvent extends IEvent {
|
|||
value: string;
|
||||
}
|
||||
|
||||
interface LocationEvent extends IEvent {
|
||||
export interface LocationEvent extends IEvent {
|
||||
url: string;
|
||||
host: string;
|
||||
pageLoad: boolean;
|
||||
|
|
|
|||
|
|
@ -3,7 +3,8 @@ import SessionEvent, { TYPES, EventData, InjectedEvent } from './event';
|
|||
import StackEvent from './stackEvent';
|
||||
import SessionError, { IError } from './error';
|
||||
import Issue, { IIssue } from './issue';
|
||||
import { Note } from 'App/services/NotesService'
|
||||
import { Note } from 'App/services/NotesService';
|
||||
import { toJS } from 'mobx';
|
||||
|
||||
const HASH_MOD = 1610612741;
|
||||
const HASH_P = 53;
|
||||
|
|
@ -19,70 +20,70 @@ function hashString(s: string): number {
|
|||
}
|
||||
|
||||
export interface ISession {
|
||||
sessionId: string,
|
||||
pageTitle: string,
|
||||
active: boolean,
|
||||
siteId: string,
|
||||
projectKey: string,
|
||||
peerId: string,
|
||||
live: boolean,
|
||||
startedAt: number,
|
||||
duration: number,
|
||||
events: InjectedEvent[],
|
||||
stackEvents: StackEvent[],
|
||||
metadata: [],
|
||||
favorite: boolean,
|
||||
filterId?: string,
|
||||
domURL: string[],
|
||||
devtoolsURL: string[],
|
||||
sessionId: string;
|
||||
pageTitle: string;
|
||||
active: boolean;
|
||||
siteId: string;
|
||||
projectKey: string;
|
||||
peerId: string;
|
||||
live: boolean;
|
||||
startedAt: number;
|
||||
duration: number;
|
||||
events: InjectedEvent[];
|
||||
stackEvents: StackEvent[];
|
||||
metadata: [];
|
||||
favorite: boolean;
|
||||
filterId?: string;
|
||||
domURL: string[];
|
||||
devtoolsURL: string[];
|
||||
/**
|
||||
* @deprecated
|
||||
*/
|
||||
mobsUrl: string[],
|
||||
userBrowser: string,
|
||||
userBrowserVersion: string,
|
||||
userCountry: string,
|
||||
userDevice: string,
|
||||
userDeviceType: string,
|
||||
isMobile: boolean,
|
||||
userOs: string,
|
||||
userOsVersion: string,
|
||||
userId: string,
|
||||
userAnonymousId: string,
|
||||
userUuid: string,
|
||||
userDisplayName: string,
|
||||
userNumericHash: number,
|
||||
viewed: boolean,
|
||||
consoleLogCount: number,
|
||||
eventsCount: number,
|
||||
pagesCount: number,
|
||||
errorsCount: number,
|
||||
issueTypes: string[],
|
||||
issues: [],
|
||||
referrer: string | null,
|
||||
userDeviceHeapSize: number,
|
||||
userDeviceMemorySize: number,
|
||||
errors: SessionError[],
|
||||
crashes?: [],
|
||||
socket: string,
|
||||
isIOS: boolean,
|
||||
revId: string | null,
|
||||
agentIds?: string[],
|
||||
isCallActive?: boolean,
|
||||
agentToken: string,
|
||||
notes: Note[],
|
||||
notesWithEvents: Array<Note | InjectedEvent>,
|
||||
fileKey: string,
|
||||
platform: string,
|
||||
projectId: string,
|
||||
startTs: number,
|
||||
timestamp: number,
|
||||
backendErrors: number,
|
||||
consoleErrors: number,
|
||||
sessionID?: string,
|
||||
userID: string,
|
||||
userUUID: string,
|
||||
userEvents: any[],
|
||||
mobsUrl: string[];
|
||||
userBrowser: string;
|
||||
userBrowserVersion: string;
|
||||
userCountry: string;
|
||||
userDevice: string;
|
||||
userDeviceType: string;
|
||||
isMobile: boolean;
|
||||
userOs: string;
|
||||
userOsVersion: string;
|
||||
userId: string;
|
||||
userAnonymousId: string;
|
||||
userUuid: string;
|
||||
userDisplayName: string;
|
||||
userNumericHash: number;
|
||||
viewed: boolean;
|
||||
consoleLogCount: number;
|
||||
eventsCount: number;
|
||||
pagesCount: number;
|
||||
errorsCount: number;
|
||||
issueTypes: string[];
|
||||
issues: IIssue[];
|
||||
referrer: string | null;
|
||||
userDeviceHeapSize: number;
|
||||
userDeviceMemorySize: number;
|
||||
errors: SessionError[];
|
||||
crashes?: [];
|
||||
socket: string;
|
||||
isIOS: boolean;
|
||||
revId: string | null;
|
||||
agentIds?: string[];
|
||||
isCallActive?: boolean;
|
||||
agentToken: string;
|
||||
notes: Note[];
|
||||
notesWithEvents: Array<Note | InjectedEvent>;
|
||||
fileKey: string;
|
||||
platform: string;
|
||||
projectId: string;
|
||||
startTs: number;
|
||||
timestamp: number;
|
||||
backendErrors: number;
|
||||
consoleErrors: number;
|
||||
sessionID?: string;
|
||||
userID: string;
|
||||
userUUID: string;
|
||||
userEvents: any[];
|
||||
}
|
||||
|
||||
const emptyValues = {
|
||||
|
|
@ -102,67 +103,67 @@ const emptyValues = {
|
|||
notes: [],
|
||||
metadata: {},
|
||||
startedAt: 0,
|
||||
}
|
||||
};
|
||||
|
||||
export default class Session {
|
||||
sessionId: ISession["sessionId"]
|
||||
pageTitle: ISession["pageTitle"]
|
||||
active: ISession["active"]
|
||||
siteId: ISession["siteId"]
|
||||
projectKey: ISession["projectKey"]
|
||||
peerId: ISession["peerId"]
|
||||
live: ISession["live"]
|
||||
startedAt: ISession["startedAt"]
|
||||
duration: ISession["duration"]
|
||||
events: ISession["events"]
|
||||
stackEvents: ISession["stackEvents"]
|
||||
metadata: ISession["metadata"]
|
||||
favorite: ISession["favorite"]
|
||||
filterId?: ISession["filterId"]
|
||||
domURL: ISession["domURL"]
|
||||
devtoolsURL: ISession["devtoolsURL"]
|
||||
sessionId: ISession['sessionId'];
|
||||
pageTitle: ISession['pageTitle'];
|
||||
active: ISession['active'];
|
||||
siteId: ISession['siteId'];
|
||||
projectKey: ISession['projectKey'];
|
||||
peerId: ISession['peerId'];
|
||||
live: ISession['live'];
|
||||
startedAt: ISession['startedAt'];
|
||||
duration: ISession['duration'];
|
||||
events: ISession['events'];
|
||||
stackEvents: ISession['stackEvents'];
|
||||
metadata: ISession['metadata'];
|
||||
favorite: ISession['favorite'];
|
||||
filterId?: ISession['filterId'];
|
||||
domURL: ISession['domURL'];
|
||||
devtoolsURL: ISession['devtoolsURL'];
|
||||
/**
|
||||
* @deprecated
|
||||
*/
|
||||
mobsUrl: ISession["mobsUrl"]
|
||||
userBrowser: ISession["userBrowser"]
|
||||
userBrowserVersion: ISession["userBrowserVersion"]
|
||||
userCountry: ISession["userCountry"]
|
||||
userDevice: ISession["userDevice"]
|
||||
userDeviceType: ISession["userDeviceType"]
|
||||
isMobile: ISession["isMobile"]
|
||||
userOs: ISession["userOs"]
|
||||
userOsVersion: ISession["userOsVersion"]
|
||||
userId: ISession["userId"]
|
||||
userAnonymousId: ISession["userAnonymousId"]
|
||||
userUuid: ISession["userUuid"]
|
||||
userDisplayName: ISession["userDisplayName"]
|
||||
userNumericHash: ISession["userNumericHash"]
|
||||
viewed: ISession["viewed"]
|
||||
consoleLogCount: ISession["consoleLogCount"]
|
||||
eventsCount: ISession["eventsCount"]
|
||||
pagesCount: ISession["pagesCount"]
|
||||
errorsCount: ISession["errorsCount"]
|
||||
issueTypes: ISession["issueTypes"]
|
||||
issues: ISession["issues"]
|
||||
referrer: ISession["referrer"]
|
||||
userDeviceHeapSize: ISession["userDeviceHeapSize"]
|
||||
userDeviceMemorySize: ISession["userDeviceMemorySize"]
|
||||
errors: ISession["errors"]
|
||||
crashes?: ISession["crashes"]
|
||||
socket: ISession["socket"]
|
||||
isIOS: ISession["isIOS"]
|
||||
revId: ISession["revId"]
|
||||
agentIds?: ISession["agentIds"]
|
||||
isCallActive?: ISession["isCallActive"]
|
||||
agentToken: ISession["agentToken"]
|
||||
notes: ISession["notes"]
|
||||
notesWithEvents: ISession["notesWithEvents"]
|
||||
fileKey: ISession["fileKey"]
|
||||
durationSeconds: number
|
||||
mobsUrl: ISession['mobsUrl'];
|
||||
userBrowser: ISession['userBrowser'];
|
||||
userBrowserVersion: ISession['userBrowserVersion'];
|
||||
userCountry: ISession['userCountry'];
|
||||
userDevice: ISession['userDevice'];
|
||||
userDeviceType: ISession['userDeviceType'];
|
||||
isMobile: ISession['isMobile'];
|
||||
userOs: ISession['userOs'];
|
||||
userOsVersion: ISession['userOsVersion'];
|
||||
userId: ISession['userId'];
|
||||
userAnonymousId: ISession['userAnonymousId'];
|
||||
userUuid: ISession['userUuid'];
|
||||
userDisplayName: ISession['userDisplayName'];
|
||||
userNumericHash: ISession['userNumericHash'];
|
||||
viewed: ISession['viewed'];
|
||||
consoleLogCount: ISession['consoleLogCount'];
|
||||
eventsCount: ISession['eventsCount'];
|
||||
pagesCount: ISession['pagesCount'];
|
||||
errorsCount: ISession['errorsCount'];
|
||||
issueTypes: ISession['issueTypes'];
|
||||
issues: Issue[];
|
||||
referrer: ISession['referrer'];
|
||||
userDeviceHeapSize: ISession['userDeviceHeapSize'];
|
||||
userDeviceMemorySize: ISession['userDeviceMemorySize'];
|
||||
errors: ISession['errors'];
|
||||
crashes?: ISession['crashes'];
|
||||
socket: ISession['socket'];
|
||||
isIOS: ISession['isIOS'];
|
||||
revId: ISession['revId'];
|
||||
agentIds?: ISession['agentIds'];
|
||||
isCallActive?: ISession['isCallActive'];
|
||||
agentToken: ISession['agentToken'];
|
||||
notes: ISession['notes'];
|
||||
notesWithEvents: ISession['notesWithEvents'];
|
||||
fileKey: ISession['fileKey'];
|
||||
durationSeconds: number;
|
||||
|
||||
constructor(plainSession?: ISession) {
|
||||
const sessionData = plainSession || (emptyValues as unknown as ISession)
|
||||
const sessionData = plainSession || (emptyValues as unknown as ISession);
|
||||
const {
|
||||
startTs = 0,
|
||||
timestamp = 0,
|
||||
|
|
@ -179,7 +180,7 @@ export default class Session {
|
|||
mobsUrl = [],
|
||||
notes = [],
|
||||
...session
|
||||
} = sessionData
|
||||
} = sessionData;
|
||||
const duration = Duration.fromMillis(session.duration < 1000 ? 1000 : session.duration);
|
||||
const durationSeconds = duration.valueOf();
|
||||
const startedAt = +startTs || +timestamp;
|
||||
|
|
@ -188,44 +189,46 @@ export default class Session {
|
|||
const userDeviceType = session.userDeviceType || 'other';
|
||||
const isMobile = ['console', 'mobile', 'tablet'].includes(userDeviceType);
|
||||
|
||||
const events: InjectedEvent[] = []
|
||||
const rawEvents: (EventData & { key: number })[] = []
|
||||
const events: InjectedEvent[] = [];
|
||||
const rawEvents: (EventData & { key: number })[] = [];
|
||||
|
||||
if (session.events?.length) {
|
||||
(session.events as EventData[]).forEach((event: EventData, k) => {
|
||||
const time = event.timestamp - startedAt
|
||||
const time = event.timestamp - startedAt;
|
||||
if (event.type !== TYPES.CONSOLE && time <= durationSeconds) {
|
||||
const EventClass = SessionEvent({ ...event, time, key: k })
|
||||
const EventClass = SessionEvent({ ...event, time, key: k });
|
||||
if (EventClass) {
|
||||
events.push(EventClass);
|
||||
}
|
||||
rawEvents.push({ ...event, time, key: k });
|
||||
}
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
const stackEventsList: StackEvent[] = []
|
||||
const stackEventsList: StackEvent[] = [];
|
||||
if (stackEvents?.length || session.userEvents?.length) {
|
||||
const mergedArrays = [...stackEvents, ...session.userEvents]
|
||||
.sort((a, b) => a.timestamp - b.timestamp)
|
||||
.map((se) => new StackEvent({ ...se, time: se.timestamp - startedAt }))
|
||||
.map((se) => new StackEvent({ ...se, time: se.timestamp - startedAt }));
|
||||
stackEventsList.push(...mergedArrays);
|
||||
}
|
||||
|
||||
const exceptions = (errors as IError[]).map(e => new SessionError(e)) || [];
|
||||
const exceptions = (errors as IError[]).map((e) => new SessionError(e)) || [];
|
||||
|
||||
const issuesList = (issues as IIssue[]).map(
|
||||
(i, k) => new Issue({ ...i, time: i.timestamp - startedAt, key: k })) || [];
|
||||
const issuesList =
|
||||
(issues as IIssue[]).map(
|
||||
(i, k) => new Issue({ ...i, time: i.timestamp - startedAt, key: k })
|
||||
) || [];
|
||||
|
||||
const rawNotes = notes;
|
||||
const notesWithEvents = [...rawEvents, ...rawNotes].sort((a, b) => {
|
||||
// @ts-ignore just in case
|
||||
const aTs = a.timestamp || a.time;
|
||||
// @ts-ignore
|
||||
const bTs = b.timestamp || b.time;
|
||||
const notesWithEvents =
|
||||
[...rawEvents, ...notes].sort((a, b) => {
|
||||
// @ts-ignore just in case
|
||||
const aTs = a.timestamp || a.time;
|
||||
// @ts-ignore
|
||||
const bTs = b.timestamp || b.time;
|
||||
|
||||
return aTs - bTs;
|
||||
}) || [];
|
||||
return aTs - bTs;
|
||||
}) || [];
|
||||
|
||||
Object.assign(this, {
|
||||
...session,
|
||||
|
|
@ -242,11 +245,11 @@ export default class Session {
|
|||
durationSeconds,
|
||||
userNumericHash: hashString(
|
||||
session.userId ||
|
||||
session.userAnonymousId ||
|
||||
session.userUuid ||
|
||||
session.userID ||
|
||||
session.userUUID ||
|
||||
''
|
||||
session.userAnonymousId ||
|
||||
session.userUuid ||
|
||||
session.userID ||
|
||||
session.userUUID ||
|
||||
''
|
||||
),
|
||||
userDisplayName:
|
||||
session.userId || session.userAnonymousId || session.userID || 'Anonymous User',
|
||||
|
|
@ -258,47 +261,75 @@ export default class Session {
|
|||
devtoolsURL,
|
||||
notes,
|
||||
notesWithEvents: notesWithEvents,
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
addIssues(issues: IIssue[]) {
|
||||
const issuesList = issues.map(
|
||||
(i, k) => new Issue({ ...i, time: i.timestamp - this.startedAt, key: k })) || [];
|
||||
addEvents(
|
||||
sessionEvents: EventData[],
|
||||
errors: any[],
|
||||
issues: any[],
|
||||
resources: any[],
|
||||
userEvents: any[],
|
||||
stackEvents: any[]
|
||||
) {
|
||||
const exceptions = (errors as IError[]).map((e) => new SessionError(e)) || [];
|
||||
const issuesList =
|
||||
(issues as IIssue[]).map(
|
||||
(i, k) => new Issue({ ...i, time: i.timestamp - this.startedAt, key: k })
|
||||
) || [];
|
||||
const stackEventsList: StackEvent[] = [];
|
||||
if (stackEvents?.length || userEvents?.length) {
|
||||
const mergedArrays = [...stackEvents, ...userEvents]
|
||||
.sort((a, b) => a.timestamp - b.timestamp)
|
||||
.map((se) => new StackEvent({ ...se, time: se.timestamp - this.startedAt }));
|
||||
stackEventsList.push(...mergedArrays);
|
||||
}
|
||||
|
||||
// @ts-ignore
|
||||
this.issues = issuesList;
|
||||
}
|
||||
|
||||
addEvents(sessionEvents: EventData[], sessionNotes: Note[]) {
|
||||
const events: InjectedEvent[] = []
|
||||
const rawEvents: (EventData & { key: number })[] = []
|
||||
const events: InjectedEvent[] = [];
|
||||
const rawEvents: (EventData & { key: number })[] = [];
|
||||
|
||||
if (sessionEvents.length) {
|
||||
sessionEvents.forEach((event, k) => {
|
||||
const time = event.timestamp - this.startedAt
|
||||
const time = event.timestamp - this.startedAt;
|
||||
if (event.type !== TYPES.CONSOLE && time <= this.durationSeconds) {
|
||||
const EventClass = SessionEvent({ ...event, time, key: k })
|
||||
const EventClass = SessionEvent({ ...event, time, key: k });
|
||||
if (EventClass) {
|
||||
events.push(EventClass);
|
||||
}
|
||||
rawEvents.push({ ...event, time, key: k });
|
||||
}
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
const rawNotes = sessionNotes;
|
||||
const notesWithEvents = [...rawEvents, ...rawNotes].sort((a, b) => {
|
||||
// @ts-ignore just in case
|
||||
const aTs = a.timestamp || a.time;
|
||||
// @ts-ignore
|
||||
const bTs = b.timestamp || b.time;
|
||||
|
||||
return aTs - bTs;
|
||||
}) || [];
|
||||
|
||||
this.events = events;
|
||||
// @ts-ignore
|
||||
this.notesWithEvents = notesWithEvents;
|
||||
this.notes = sessionNotes
|
||||
this.events = events
|
||||
this.notesWithEvents = rawEvents;
|
||||
this.errors = exceptions;
|
||||
this.issues = issuesList;
|
||||
// @ts-ignore legacy code? no idea
|
||||
this.resources = resources;
|
||||
this.stackEvents = stackEventsList;
|
||||
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
addNotes(sessionNotes: Note[]) {
|
||||
// @ts-ignore
|
||||
this.notesWithEvents =
|
||||
[...this.notesWithEvents, ...sessionNotes].sort((a, b) => {
|
||||
// @ts-ignore just in case
|
||||
const aTs = a.timestamp || a.time;
|
||||
// @ts-ignore
|
||||
const bTs = b.timestamp || b.time;
|
||||
|
||||
return aTs - bTs;
|
||||
}) || [];
|
||||
this.notes = sessionNotes;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
toJS() {
|
||||
return { ...toJS(this) };
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue