diff --git a/frontend/app/components/Assist/RequestingWindow/RequestingWindow.tsx b/frontend/app/components/Assist/RequestingWindow/RequestingWindow.tsx
index 25283bd3b..212fea91c 100644
--- a/frontend/app/components/Assist/RequestingWindow/RequestingWindow.tsx
+++ b/frontend/app/components/Assist/RequestingWindow/RequestingWindow.tsx
@@ -82,5 +82,5 @@ function RequestingWindow({ userDisplayName, getWindowType }: Props) {
}
export default connect((state: any) => ({
- userDisplayName: state.getIn(['sessions', 'current', 'userDisplayName']),
+ userDisplayName: state.getIn(['sessions', 'current']).userDisplayName,
}))(RequestingWindow);
diff --git a/frontend/app/components/Assist/components/AssistActions/AssistActions.tsx b/frontend/app/components/Assist/components/AssistActions/AssistActions.tsx
index a474c9cc8..f324ce433 100644
--- a/frontend/app/components/Assist/components/AssistActions/AssistActions.tsx
+++ b/frontend/app/components/Assist/components/AssistActions/AssistActions.tsx
@@ -255,7 +255,7 @@ const con = connect(
return {
hasPermission: permissions.includes('ASSIST_CALL'),
isEnterprise: state.getIn(['user', 'account', 'edition']) === 'ee',
- userDisplayName: state.getIn(['sessions', 'current', 'userDisplayName']),
+ userDisplayName: state.getIn(['sessions', 'current']).userDisplayName,
};
},
{ toggleChatWindow }
diff --git a/frontend/app/components/Session/PlayerContent.js b/frontend/app/components/Session/PlayerContent.js
index 64d896455..c7ccfc62b 100644
--- a/frontend/app/components/Session/PlayerContent.js
+++ b/frontend/app/components/Session/PlayerContent.js
@@ -21,6 +21,7 @@ function PlayerContent({ session, live, fullscreen, activeTab, setActiveTab }) {
const hasError = !!error
+ console.log(session, 'hi')
const sessionDays = countDaysFrom(session.startedAt);
return (
diff --git a/frontend/app/components/Session/WebPlayer.tsx b/frontend/app/components/Session/WebPlayer.tsx
index 6e169777e..de9e84036 100644
--- a/frontend/app/components/Session/WebPlayer.tsx
+++ b/frontend/app/components/Session/WebPlayer.tsx
@@ -74,8 +74,9 @@ function WebPlayer(props: any) {
contextValue.player.togglePlay();
};
- if (!contextValue.player) return null;
+ if (!contextValue.player || !session) return null;
+ console.log(session)
return (
<>
diff --git a/frontend/app/components/Session_/EventsBlock/Metadata/Metadata.js b/frontend/app/components/Session_/EventsBlock/Metadata/Metadata.js
index f6724385f..ca0a953a2 100644
--- a/frontend/app/components/Session_/EventsBlock/Metadata/Metadata.js
+++ b/frontend/app/components/Session_/EventsBlock/Metadata/Metadata.js
@@ -3,7 +3,7 @@ import { connect } from 'react-redux';
import MetadataItem from './MetadataItem';
export default connect(state => ({
- metadata: state.getIn([ 'sessions', 'current', 'metadata' ]),
+ metadata: state.getIn([ 'sessions', 'current' ]).metadata,
}))(function Metadata ({ metadata }) {
const metaLenth = Object.keys(metadata).length;
diff --git a/frontend/app/components/Session_/EventsBlock/Metadata/SessionList.js b/frontend/app/components/Session_/EventsBlock/Metadata/SessionList.js
index fe414a30d..92ac93432 100644
--- a/frontend/app/components/Session_/EventsBlock/Metadata/SessionList.js
+++ b/frontend/app/components/Session_/EventsBlock/Metadata/SessionList.js
@@ -7,7 +7,7 @@ import stl from './sessionList.module.css';
import AnimatedSVG, { ICONS } from 'Shared/AnimatedSVG/AnimatedSVG';
@connect((state) => ({
- currentSessionId: state.getIn(['sessions', 'current', 'sessionId']),
+ currentSessionId: state.getIn(['sessions', 'current']).sessionId,
}))
class SessionList extends React.PureComponent {
render() {
@@ -17,7 +17,7 @@ class SessionList extends React.PureComponent {
.map(({ sessions, ...rest }) => {
return {
...rest,
- sessions: sessions.map(Session).filter(({ sessionId }) => sessionId !== currentSessionId),
+ sessions: sessions.map(s => new Session(s)).filter(({ sessionId }) => sessionId !== currentSessionId),
};
})
.filter((site) => site.sessions.length > 0);
diff --git a/frontend/app/components/Session_/OverviewPanel/OverviewPanel.tsx b/frontend/app/components/Session_/OverviewPanel/OverviewPanel.tsx
index ef8598ddc..c7e7912b1 100644
--- a/frontend/app/components/Session_/OverviewPanel/OverviewPanel.tsx
+++ b/frontend/app/components/Session_/OverviewPanel/OverviewPanel.tsx
@@ -132,7 +132,7 @@ function OverviewPanel({ issuesList }: { issuesList: Record[] }) {
export default connect(
(state: any) => ({
- issuesList: state.getIn(['sessions', 'current', 'issues']),
+ issuesList: state.getIn(['sessions', 'current']).issues,
}),
{
toggleBottomBlock,
diff --git a/frontend/app/components/Session_/Performance/Performance.tsx b/frontend/app/components/Session_/Performance/Performance.tsx
index eca011fcb..fa4828af1 100644
--- a/frontend/app/components/Session_/Performance/Performance.tsx
+++ b/frontend/app/components/Session_/Performance/Performance.tsx
@@ -492,5 +492,5 @@ function Performance({
}
export const ConnectedPerformance = connect((state: any) => ({
- userDeviceHeapSize: state.getIn(['sessions', 'current', 'userDeviceHeapSize']),
+ userDeviceHeapSize: state.getIn(['sessions', 'current']).userDeviceHeapSize,
}))(observer(Performance));
diff --git a/frontend/app/components/Session_/Player/Controls/Controls.tsx b/frontend/app/components/Session_/Player/Controls/Controls.tsx
index eb283c47a..29ede7fd0 100644
--- a/frontend/app/components/Session_/Player/Controls/Controls.tsx
+++ b/frontend/app/components/Session_/Player/Controls/Controls.tsx
@@ -386,7 +386,7 @@ export default connect(
session: state.getIn(['sessions', 'current']),
totalAssistSessions: state.getIn(['liveSearch', 'total']),
closedLive:
- !!state.getIn(['sessions', 'errors']) || !state.getIn(['sessions', 'current', 'live']),
+ !!state.getIn(['sessions', 'errors']) || !state.getIn(['sessions', 'current']).live,
skipInterval: state.getIn(['components', 'player', 'skipInterval']),
};
},
diff --git a/frontend/app/components/Session_/Player/Controls/Timeline.tsx b/frontend/app/components/Session_/Player/Controls/Timeline.tsx
index 7bd8b7464..4a32d8efd 100644
--- a/frontend/app/components/Session_/Player/Controls/Timeline.tsx
+++ b/frontend/app/components/Session_/Player/Controls/Timeline.tsx
@@ -241,8 +241,8 @@ function Timeline(props: IProps) {
export default connect(
(state: any) => ({
- issues: state.getIn(['sessions', 'current', 'issues']),
- startedAt: state.getIn(['sessions', 'current', 'startedAt']),
+ issues: state.getIn(['sessions', 'current']).issues || [],
+ startedAt: state.getIn(['sessions', 'current']).startedAt || 0,
tooltipVisible: state.getIn(['sessions', 'timeLineTooltip', 'isVisible']),
}),
{ setTimelinePointer, setTimelineHoverTime }
diff --git a/frontend/app/components/Session_/Player/Controls/components/CreateNote.tsx b/frontend/app/components/Session_/Player/Controls/components/CreateNote.tsx
index 68f510de6..e2e50d2e4 100644
--- a/frontend/app/components/Session_/Player/Controls/components/CreateNote.tsx
+++ b/frontend/app/components/Session_/Player/Controls/components/CreateNote.tsx
@@ -298,7 +298,7 @@ export default connect(
} = state.getIn(['sessions', 'createNoteTooltip']);
const slackChannels = state.getIn(['slack', 'list']);
const teamsChannels = state.getIn(['teams', 'list']);
- const sessionId = state.getIn(['sessions', 'current', 'sessionId']);
+ const sessionId = state.getIn(['sessions', 'current']).sessionId;
return { isVisible, time, sessionId, isEdit, editNote, slackChannels, teamsChannels };
},
{ setCreateNoteTooltip, addNote, updateNote, fetchSlack, fetchTeams }
diff --git a/frontend/app/components/Session_/Player/Player.js b/frontend/app/components/Session_/Player/Player.js
index da6fb0eac..e0a467ca2 100644
--- a/frontend/app/components/Session_/Player/Player.js
+++ b/frontend/app/components/Session_/Player/Player.js
@@ -107,11 +107,11 @@ export default connect((state) => {
return {
fullscreen: state.getIn(['components', 'player', 'fullscreen']),
nextId: state.getIn(['sessions', 'nextId']),
- sessionId: state.getIn(['sessions', 'current', 'sessionId']),
+ sessionId: state.getIn(['sessions', 'current']).sessionId,
bottomBlock: state.getIn(['components', 'player', 'bottomBlock']),
closedLive:
!!state.getIn(['sessions', 'errors']) ||
- (isAssist && !state.getIn(['sessions', 'current', 'live'])),
+ (isAssist && !state.getIn(['sessions', 'current']).live),
};
},
{
diff --git a/frontend/app/components/Session_/PlayerBlock.js b/frontend/app/components/Session_/PlayerBlock.js
index 5da29a587..5f805dac2 100644
--- a/frontend/app/components/Session_/PlayerBlock.js
+++ b/frontend/app/components/Session_/PlayerBlock.js
@@ -8,7 +8,7 @@ import styles from './playerBlock.module.css';
@connect((state) => ({
fullscreen: state.getIn(['components', 'player', 'fullscreen']),
- sessionId: state.getIn(['sessions', 'current', 'sessionId']),
+ sessionId: state.getIn(['sessions', 'current']).sessionId,
disabled: state.getIn(['components', 'targetDefiner', 'inspectorMode']),
jiraConfig: state.getIn(['issues', 'list']).first(),
}))
diff --git a/frontend/app/components/Session_/ScreenRecorder/ScreenRecorder.tsx b/frontend/app/components/Session_/ScreenRecorder/ScreenRecorder.tsx
index 5031d2cca..fc5c66eea 100644
--- a/frontend/app/components/Session_/ScreenRecorder/ScreenRecorder.tsx
+++ b/frontend/app/components/Session_/ScreenRecorder/ScreenRecorder.tsx
@@ -120,5 +120,5 @@ function ScreenRecorder({
export default connect((state: any) => ({
siteId: state.getIn(['site', 'siteId']),
- sessionId: state.getIn(['sessions', 'current', 'sessionId']),
+ sessionId: state.getIn(['sessions', 'current']).sessionId,
}))(observer(ScreenRecorder))
diff --git a/frontend/app/components/shared/Bookmark/Bookmark.tsx b/frontend/app/components/shared/Bookmark/Bookmark.tsx
index 04c88e589..d011af5f7 100644
--- a/frontend/app/components/shared/Bookmark/Bookmark.tsx
+++ b/frontend/app/components/shared/Bookmark/Bookmark.tsx
@@ -65,7 +65,7 @@ function Bookmark(props: Props) {
export default connect(
(state: any) => ({
isEnterprise: state.getIn(['user', 'account', 'edition']) === 'ee',
- favorite: state.getIn(['sessions', 'current', 'favorite']),
+ favorite: state.getIn(['sessions', 'current']).favorite,
}),
{ toggleFavorite }
)(Bookmark);
diff --git a/frontend/app/components/shared/DevTools/NetworkPanel/NetworkPanel.tsx b/frontend/app/components/shared/DevTools/NetworkPanel/NetworkPanel.tsx
index f34b3e04e..1f2212152 100644
--- a/frontend/app/components/shared/DevTools/NetworkPanel/NetworkPanel.tsx
+++ b/frontend/app/components/shared/DevTools/NetworkPanel/NetworkPanel.tsx
@@ -384,5 +384,5 @@ function NetworkPanel({ startedAt }: { startedAt: number }) {
}
export default connect((state: any) => ({
- startedAt: state.getIn(['sessions', 'current', 'startedAt']),
+ startedAt: state.getIn(['sessions', 'current']).startedAt,
}))(observer(NetworkPanel));
diff --git a/frontend/app/components/shared/SessionItem/SessionItem.tsx b/frontend/app/components/shared/SessionItem/SessionItem.tsx
index fbfd2202e..a59f8e5aa 100644
--- a/frontend/app/components/shared/SessionItem/SessionItem.tsx
+++ b/frontend/app/components/shared/SessionItem/SessionItem.tsx
@@ -38,7 +38,6 @@ interface Props {
userNumericHash: number;
live: boolean;
metadata: Record;
- userSessionsCount: number;
issueTypes: [];
active: boolean;
isCallActive?: boolean;
diff --git a/frontend/app/components/shared/SharePopup/SharePopup.js b/frontend/app/components/shared/SharePopup/SharePopup.js
index 47f2b68b7..1df16cf6d 100644
--- a/frontend/app/components/shared/SharePopup/SharePopup.js
+++ b/frontend/app/components/shared/SharePopup/SharePopup.js
@@ -12,7 +12,7 @@ import { fetchList as fetchTeams, sendMsTeamsMsg } from 'Duck/integrations/teams
@connect(
(state) => ({
- sessionId: state.getIn(['sessions', 'current', 'sessionId']),
+ sessionId: state.getIn(['sessions', 'current']).sessionId,
channels: state.getIn(['slack', 'list']),
msTeamsChannels: state.getIn(['teams', 'list']),
tenantId: state.getIn(['user', 'account', 'tenantId']),
diff --git a/frontend/app/components/ui/ErrorDetails/ErrorDetails.tsx b/frontend/app/components/ui/ErrorDetails/ErrorDetails.tsx
index 4acd355c2..2aca274ea 100644
--- a/frontend/app/components/ui/ErrorDetails/ErrorDetails.tsx
+++ b/frontend/app/components/ui/ErrorDetails/ErrorDetails.tsx
@@ -80,7 +80,7 @@ export default connect(
(state: any) => ({
// errorStack: state.getIn(['sessions', 'errorStack']),
errorStack: state.getIn(['errors', 'instanceTrace']),
- sessionId: state.getIn(['sessions', 'current', 'sessionId']),
+ sessionId: state.getIn(['sessions', 'current']).sessionId,
}),
{ fetchErrorStackList }
)(ErrorDetails);
diff --git a/frontend/app/duck/customMetrics.js b/frontend/app/duck/customMetrics.js
index 82ce78277..c740f7a57 100644
--- a/frontend/app/duck/customMetrics.js
+++ b/frontend/app/duck/customMetrics.js
@@ -92,7 +92,7 @@ function reducer(state = initialState, action = {}) {
const { data } = action;
return state.set("list", List(data.map(CustomMetric)));
case success(FETCH_SESSION_LIST):
- return state.set("sessionList", List(action.data.map(item => ({ ...item, sessions: item.sessions.map(Session) }))));
+ return state.set("sessionList", List(action.data.map(item => ({ ...item, sessions: item.sessions.map(s => new Session(s)) }))));
case SET_ACTIVE_WIDGET:
return state.set("activeWidget", action.widget).set('sessionList', List());
}
diff --git a/frontend/app/duck/errors.js b/frontend/app/duck/errors.js
index 624c05633..8a23875cc 100644
--- a/frontend/app/duck/errors.js
+++ b/frontend/app/duck/errors.js
@@ -56,7 +56,6 @@ const initialState = Map({
function reducer(state = initialState, action = {}) {
let updError;
- console.log(action)
switch (action.type) {
case EDIT_OPTIONS:
return state.mergeIn(["options"], action.instance).set('currentPage', 1);
diff --git a/frontend/app/duck/funnels.js b/frontend/app/duck/funnels.js
index 3abfcc450..d175b64d5 100644
--- a/frontend/app/duck/funnels.js
+++ b/frontend/app/duck/funnels.js
@@ -99,12 +99,12 @@ const reducer = (state = initialState, action = {}) => {
.set('criticalIssuesCount', action.data.issues.criticalIssuesCount)
case FETCH_SESSIONS_SUCCESS:
return state
- .set('sessions', List(action.data.sessions).map(Session))
+ .set('sessions', List(action.data.sessions).map(s => new Session(s)))
.set('total', action.data.total)
case FETCH_ISSUE_SUCCESS:
return state
.set('issue', FunnelIssue(action.data.issue))
- .set('sessions', List(action.data.sessions.sessions).map(Session))
+ .set('sessions', List(action.data.sessions.sessions).map(s => new Session(s)))
.set('sessionsTotal', action.data.sessions.total)
case RESET_ISSUE:
return state.set('isses', FunnelIssue())
diff --git a/frontend/app/duck/liveSearch.js b/frontend/app/duck/liveSearch.js
index 591dd7084..8500158bd 100644
--- a/frontend/app/duck/liveSearch.js
+++ b/frontend/app/duck/liveSearch.js
@@ -36,7 +36,7 @@ function reducer(state = initialState, action = {}) {
return state.set('currentPage', action.page);
case success(FETCH_SESSION_LIST):
const { sessions, total } = action.data;
- const list = List(sessions).map(Session);
+ const list = List(sessions).map(s => new Session(s));
return state
.set('list', list)
.set('total', total);
diff --git a/frontend/app/duck/sessions.js b/frontend/app/duck/sessions.js
index b78d8fad4..6f015f510 100644
--- a/frontend/app/duck/sessions.js
+++ b/frontend/app/duck/sessions.js
@@ -49,7 +49,7 @@ const defaultDateFilters = {
const initialState = Map({
list: List(),
sessionIds: [],
- current: Session(),
+ current: new Session(),
total: 0,
keyMap: Map(),
wdTypeCount: Map(),
@@ -127,8 +127,8 @@ const reducer = (state = initialState, action = {}) => {
// TODO: more common.. or TEMP
const events = action.filter.events;
// const filters = action.filter.filters;
- const current = state.get('list').find(({ sessionId }) => sessionId === action.data.sessionId) || Session();
- const session = Session(action.data);
+ const current = state.get('list').find(({ sessionId }) => sessionId === action.data.sessionId) || new Session();
+ const session = new Session(action.data);
const matching = [];
@@ -155,7 +155,7 @@ const reducer = (state = initialState, action = {}) => {
});
});
return state
- .set('current', current.merge(session))
+ .set('current', session)
.set('eventsIndex', matching)
.set('visitedEvents', visitedEvents)
.set('host', visitedEvents[0] && visitedEvents[0].host);
@@ -167,11 +167,15 @@ const reducer = (state = initialState, action = {}) => {
const session = state.get('list').find(({ sessionId }) => sessionId === id);
const wasInFavorite = state.get('favoriteList').findIndex(({ sessionId }) => sessionId === id) > -1;
+ if (session && !wasInFavorite) {
+ session.favorite = true
+ }
return state
- .update('current', (currentSession) => (currentSession.sessionId === id ? currentSession.set('favorite', !wasInFavorite) : currentSession))
- .update('list', (list) => list.map((listSession) => (listSession.sessionId === id ? listSession.set('favorite', !wasInFavorite) : listSession)))
+ // TODO returns bool here???
+ .update('current', (currentSession) => (currentSession.sessionId === id ? (currentSession.favorite = !wasInFavorite) : currentSession))
+ .update('list', (list) => list.map((listSession) => (listSession.sessionId === id ? (listSession.favorite = !wasInFavorite) : listSession)))
.update('favoriteList', (list) => session ?
- wasInFavorite ? list.filter(({ sessionId }) => sessionId !== id) : list.push(session.set('favorite', true)) : list
+ wasInFavorite ? list.filter(({ sessionId }) => sessionId !== id) : list.push(session) : list
);
}
case SORT: {
diff --git a/frontend/app/mstore/assistMultiviewStore.ts b/frontend/app/mstore/assistMultiviewStore.ts
index e0895918f..379f852cb 100644
--- a/frontend/app/mstore/assistMultiviewStore.ts
+++ b/frontend/app/mstore/assistMultiviewStore.ts
@@ -95,7 +95,7 @@ export default class AssistMultiviewStore {
const matchingSessions = data.sessions.filter(
(s: Record) => ids.includes(s.sessionID) || ids.includes(s.sessionId)
);
- const immutMatchingSessions = List(matchingSessions).map(Session);
+ const immutMatchingSessions = List(matchingSessions).map(s => new Session(s));
immutMatchingSessions.forEach((session: Record) => {
this.addSession(session);
this.fetchAgentTokenInfo(session.sessionId);
diff --git a/frontend/app/player/web/WebPlayer.ts b/frontend/app/player/web/WebPlayer.ts
index ff6d3d0da..0bb84da42 100644
--- a/frontend/app/player/web/WebPlayer.ts
+++ b/frontend/app/player/web/WebPlayer.ts
@@ -26,10 +26,10 @@ export default class WebPlayer extends Player {
private targetMarker: TargetMarker
constructor(protected wpState: Store, session: any, live: boolean) {
- console.log(session.events, session.resources, session.errors)
+ console.log(session.events, session.stackEvents, session.resources, session.errors)
let initialLists = live ? {} : {
- event: session.events.toJSON(),
- stack: session.stackEvents.toJSON(),
+ event: session.events,
+ stack: session.stackEvents,
resource: session.resources.toJSON(), // MBTODO: put ResourceTiming in file
exceptions: session.errors.map(({ time, errorId, name }: any) =>
Log({
diff --git a/frontend/app/types/dashboard/index.js b/frontend/app/types/dashboard/index.js
index c250d64d6..670f44a2c 100644
--- a/frontend/app/types/dashboard/index.js
+++ b/frontend/app/types/dashboard/index.js
@@ -97,14 +97,14 @@ export const WIDGET_LIST = [{
name: "Recent Frustrations",
description: "List of recent sessions where users experienced some kind of frustrations, such as click rage.",
thumb: 'recent_frustrations.png',
- dataWrapper: list => List(list).map(Session),
+ dataWrapper: list => List(list).map(s => new Session(s)),
},
{
key: "sessionsFeedback",
name: "Recent Negative Feedback",
description: "List of recent sessions where users reported an issue or a bad experience.",
thumb: 'negative_feedback.png',
- dataWrapper: list => List(list).map(Session),
+ dataWrapper: list => List(list).map(s => new Session(s)),
},
{
key: "missingResources",
diff --git a/frontend/app/types/errorInfo.js b/frontend/app/types/errorInfo.js
index b3bb48e1d..7da9c3a9b 100644
--- a/frontend/app/types/errorInfo.js
+++ b/frontend/app/types/errorInfo.js
@@ -37,12 +37,12 @@ const ErrorInfo = Record({
chart30: [],
tags: [],
customTags: [],
- lastHydratedSession: Session(),
+ lastHydratedSession: new Session(),
disabled: false,
}, {
fromJS: ({ stack, lastHydratedSession, ...other }) => ({
...other,
- lastHydratedSession: Session(lastHydratedSession),
+ lastHydratedSession: new Session(lastHydratedSession),
stack0InfoString: getStck0InfoString(stack || []),
})
});
diff --git a/frontend/app/types/session/author.js b/frontend/app/types/session/author.js
deleted file mode 100644
index 12edc3c1e..000000000
--- a/frontend/app/types/session/author.js
+++ /dev/null
@@ -1,11 +0,0 @@
-import Record from 'Types/Record';
-
-export default Record({
- id: undefined,
- avatarUrls: undefined,
- name: undefined,
-}, {
- fromJS: author => ({
- ...author,
- })
-})
diff --git a/frontend/app/types/session/event.js b/frontend/app/types/session/event.js
deleted file mode 100644
index 537de1724..000000000
--- a/frontend/app/types/session/event.js
+++ /dev/null
@@ -1,91 +0,0 @@
-import Record from 'Types/Record';
-import Target from 'Types/target';
-
-const CONSOLE = 'CONSOLE';
-const CLICK = 'CLICK';
-const INPUT = 'INPUT';
-const LOCATION = 'LOCATION';
-const CUSTOM = 'CUSTOM';
-const CLICKRAGE = 'CLICKRAGE';
-const IOS_VIEW = 'VIEW';
-export const TYPES = { CONSOLE, CLICK, INPUT, LOCATION, CUSTOM, CLICKRAGE, IOS_VIEW};
-
-
-const Event = Record({
- time: 0,
- label: ''
-}, {
- fromJS: event => ({
- ...event,
- target: Target(event.target || { path: event.targetPath }),
- })
-})
-
-const Console = Event.extend({
- type: CONSOLE,
- subtype: '', // level ???
- value: '',
-},{
- name: 'Console'
-})
-
-const Click = Event.extend({
- type: CLICK,
- targetContent: '',
- target: Target(),
- count: undefined
-}, {
- name: 'Click'
-});
-
-const Input = Event.extend({
- type: INPUT,
- target: Target(),
- value: '',
-},{
- name: 'Input'
-});
-
-const View = Event.extend({
- type: IOS_VIEW,
- name: '',
-},{
- name: 'View'
-})
-
-const Location = Event.extend({
- type: LOCATION,
- url: '',
- host: '',
- pageLoad: false,
- fcpTime: undefined,
- //fpTime: undefined,
- loadTime: undefined,
- domContentLoadedTime: undefined,
- domBuildingTime: undefined,
- speedIndex: undefined,
- visuallyComplete: undefined,
- timeToInteractive: undefined,
- referrer: '',
-}, {
- fromJS: event => ({
- ...event,
- //fpTime: event.firstPaintTime,
- fcpTime: event.firstContentfulPaintTime || event.firstPaintTime,
- }),
- name: 'Location'
-});
-
-const TYPE_CONSTRUCTOR_MAP = {
- [CONSOLE]: Console,
- [CLICK]: Click,
- [INPUT]: Input,
- [LOCATION]: Location,
- [CLICKRAGE]: Click,
- [IOS_VIEW]: View,
-}
-
-export default function(event = {}) {
- return (TYPE_CONSTRUCTOR_MAP[event.type] || Event)(event);
-}
-
diff --git a/frontend/app/types/session/event.ts b/frontend/app/types/session/event.ts
new file mode 100644
index 000000000..fa62c33d9
--- /dev/null
+++ b/frontend/app/types/session/event.ts
@@ -0,0 +1,162 @@
+import Record from 'Types/Record';
+import Target from 'Types/target';
+
+const CONSOLE = 'CONSOLE';
+const CLICK = 'CLICK';
+const INPUT = 'INPUT';
+const LOCATION = 'LOCATION';
+const CUSTOM = 'CUSTOM';
+const CLICKRAGE = 'CLICKRAGE';
+const IOS_VIEW = 'VIEW';
+export const TYPES = { CONSOLE, CLICK, INPUT, LOCATION, CUSTOM, CLICKRAGE, IOS_VIEW};
+
+interface IEvent {
+ time: number;
+ timestamp: number;
+ type: typeof CONSOLE | typeof CLICK | typeof INPUT | typeof LOCATION | typeof CUSTOM | typeof CLICKRAGE;
+ name: string;
+ key: number;
+ label: string;
+ targetPath: string;
+ target: {
+ path: string;
+ label: string;
+ }
+}
+interface ConsoleEvent extends IEvent {
+ subtype: string
+ value: string
+}
+interface ClickEvent extends IEvent {
+ targetContent: string;
+ count: number;
+}
+interface InputEvent extends IEvent {
+ value: string;
+}
+
+interface LocationEvent extends IEvent {
+ url: string;
+ host: string;
+ pageLoad: boolean;
+ fcpTime: number;
+ loadTime: number;
+ domContentLoadedTime: number;
+ domBuildingTime: number;
+ speedIndex: number;
+ visuallyComplete: number;
+ timeToInteractive: number;
+ referrer: string;
+ firstContentfulPaintTime: number;
+ firstPaintTime: number;
+}
+
+export type EventData = ConsoleEvent | ClickEvent | InputEvent | LocationEvent | IEvent;
+
+class Event {
+ key: IEvent["key"]
+ time: IEvent["time"];
+ label: IEvent["label"];
+ target: IEvent["target"];
+
+
+ constructor(event: IEvent) {
+ Object.assign(this, {
+ time: event.time,
+ label: event.label,
+ key: event.key,
+ target: {
+ path: event.target?.path || event.targetPath,
+ label: event.target?.label
+ }
+ })
+ }
+}
+
+class Console extends Event {
+ readonly type = CONSOLE;
+ readonly name = 'Console'
+ subtype: string;
+ value: string;
+
+ constructor(evt: ConsoleEvent) {
+ super(evt);
+ this.subtype = evt.subtype
+ this.value = evt.value
+ }
+}
+
+class Click extends Event {
+ readonly type = CLICK;
+ readonly name = 'Click'
+ targetContent = '';
+ count: number
+
+ constructor(evt: ClickEvent) {
+ super(evt);
+ this.targetContent = evt.targetContent
+ this.count = evt.count
+ }
+}
+
+class Input extends Event {
+ readonly type = INPUT;
+ readonly name = 'Input'
+ value = ''
+
+ constructor(evt: InputEvent) {
+ super(evt);
+ this.value = evt.value
+ }
+}
+
+
+class Location extends Event {
+ readonly name = 'Location';
+ readonly type = LOCATION;
+ url: LocationEvent["url"]
+ host: LocationEvent["host"];
+ pageLoad: LocationEvent["pageLoad"];
+ fcpTime: LocationEvent["fcpTime"];
+ loadTime: LocationEvent["loadTime"];
+ domContentLoadedTime: LocationEvent["domContentLoadedTime"];
+ domBuildingTime: LocationEvent["domBuildingTime"];
+ speedIndex: LocationEvent["speedIndex"];
+ visuallyComplete: LocationEvent["visuallyComplete"];
+ timeToInteractive: LocationEvent["timeToInteractive"];
+ referrer: LocationEvent["referrer"];
+
+ constructor(evt: LocationEvent) {
+ super(evt);
+ Object.assign(this, {
+ ...evt,
+ fcpTime: evt.firstContentfulPaintTime || evt.firstPaintTime
+ });
+ }
+}
+
+export type InjectedEvent = Console | Click | Input | Location;
+
+export default function(event: EventData) {
+ if (event.type && event.type === CONSOLE) {
+ return new Console(event as ConsoleEvent)
+ }
+ if (event.type && event.type === CLICK) {
+ return new Click(event as ClickEvent)
+ }
+ if (event.type && event.type === INPUT) {
+ return new Input(event as InputEvent)
+ }
+ if (event.type && event.type === LOCATION) {
+ return new Location(event as LocationEvent)
+ }
+ if (event.type && event.type === CLICKRAGE) {
+ return new Click(event as ClickEvent)
+ }
+ // not used right now?
+ // if (event.type === CUSTOM || !event.type) {
+ // return new Event(event)
+ // }
+ console.error(`Unknown event type: ${event.type}`)
+}
+
diff --git a/frontend/app/types/session/profile.js b/frontend/app/types/session/profile.js
deleted file mode 100644
index 98b9bc345..000000000
--- a/frontend/app/types/session/profile.js
+++ /dev/null
@@ -1,20 +0,0 @@
-import { List } from 'immutable';
-import Record from 'Types/Record';
-
-export default Record({
- name: '',
- args: List(),
- result: undefined,
- time: undefined,
- index: undefined,
- duration: undefined,
-}, {
- fromJS: ({ start_time, end_time, args, ...profile }) => ({
- ...profile,
- args: List(args),
- time: Math.round(start_time),
- duration: Math.round(end_time - start_time || 0),
- }),
-});
-
-
diff --git a/frontend/app/types/session/session.ts b/frontend/app/types/session/session.ts
index 35206a5a4..298ec4fa0 100644
--- a/frontend/app/types/session/session.ts
+++ b/frontend/app/types/session/session.ts
@@ -1,11 +1,11 @@
-import Record from 'Types/Record';
-import { List, Map } from 'immutable';
+import { List } from 'immutable';
import { Duration } from 'luxon';
-import SessionEvent, { TYPES } from './event';
+import SessionEvent, { TYPES, EventData, InjectedEvent } from './event';
import StackEvent from './stackEvent';
import Resource from './resource';
import SessionError, { IError } from './error';
import Issue, { IIssue } from './issue';
+import { Note } from 'App/services/NotesService'
const HASH_MOD = 1610612741;
const HASH_P = 53;
@@ -19,163 +19,247 @@ function hashString(s: string): number {
return hash;
}
-export default Record(
- {
- sessionId: '',
- pageTitle: '',
- active: false,
- siteId: '',
- projectKey: '',
- peerId: '',
- live: false,
- startedAt: 0,
- duration: 0,
- events: List(),
- stackEvents: List(),
- resources: [],
- missedResources: [],
- metadata: Map(),
- favorite: false,
- filterId: '',
- messagesUrl: '',
- domURL: [],
- devtoolsURL: [],
- mobsUrl: [], // @depricated
- userBrowser: '',
- userBrowserVersion: '?',
- userCountry: '',
- userDevice: '',
- userDeviceType: '',
- isMobile: false,
- userOs: '',
- userOsVersion: '',
- userId: '',
- userAnonymousId: '',
- userUuid: undefined,
- userDisplayName: '',
- userNumericHash: 0,
- viewed: false,
- consoleLogCount: '?',
- eventsCount: '?',
- pagesCount: '?',
- clickRage: undefined,
- clickRageTime: undefined,
- resourcesScore: 0,
- consoleError: undefined,
- resourceError: undefined,
- returningLocation: undefined,
- returningLocationTime: undefined,
- errorsCount: 0,
- issueTypes: [],
- issues: [],
- userDeviceHeapSize: 0,
- userDeviceMemorySize: 0,
- errors: List(),
- crashes: [],
- socket: null,
- isIOS: false,
- revId: '',
- userSessionsCount: 0,
- agentIds: [],
- isCallActive: false,
- agentToken: '',
- notes: [],
- notesWithEvents: [],
- fileKey: '',
- },
- {
- fromJS: ({
+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[],
+ resources: Resource[],
+ missedResources: Resource[],
+ metadata: [],
+ favorite: boolean,
+ filterId?: string,
+ domURL: string[],
+ devtoolsURL: string[],
+ mobsUrl: string[], // @depricated
+ 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: [],
+ 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: 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 = {
+ startTs: 0,
+ timestamp: 0,
+ backendErrors: 0,
+ consoleErrors: 0,
+ sessionID: '',
+ projectId: '',
+ errors: [],
+ stackEvents: [],
+ issues: [],
+ sessionId: '',
+ domURL: [],
+ devtoolsURL: [],
+ mobsUrl: [],
+ 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"]
+ resources: ISession["resources"]
+ missedResources: ISession["missedResources"]
+ metadata: ISession["metadata"]
+ favorite: ISession["favorite"]
+ filterId?: ISession["filterId"]
+ domURL: ISession["domURL"]
+ devtoolsURL: ISession["devtoolsURL"]
+ mobsUrl: ISession["mobsUrl"] // @depricated
+ 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"]
+
+ constructor(plainSession?: ISession) {
+ const sessionData = plainSession || (emptyValues as unknown as ISession)
+ const {
startTs = 0,
timestamp = 0,
backendErrors = 0,
consoleErrors = 0,
- projectId,
+ sessionID = '',
+ projectId = '',
errors = [],
stackEvents = [],
issues = [],
- sessionId,
- sessionID,
+ sessionId = '',
domURL = [],
devtoolsURL = [],
mobsUrl = [],
notes = [],
...session
- }) => {
- const duration = Duration.fromMillis(session.duration < 1000 ? 1000 : session.duration);
- const durationSeconds = duration.valueOf();
- const startedAt = +startTs || +timestamp;
+ } = sessionData
+ const duration = Duration.fromMillis(session.duration < 1000 ? 1000 : session.duration);
+ const durationSeconds = duration.valueOf();
+ const startedAt = +startTs || +timestamp;
- const userDevice = session.userDevice || session.userDeviceType || 'Other';
- const userDeviceType = session.userDeviceType || 'other';
- const isMobile = ['console', 'mobile', 'tablet'].includes(userDeviceType);
+ const userDevice = session.userDevice || session.userDeviceType || 'Other';
+ const userDeviceType = session.userDeviceType || 'other';
+ const isMobile = ['console', 'mobile', 'tablet'].includes(userDeviceType);
- const events = List(session.events)
- .map((e) => SessionEvent({ ...e, time: e.timestamp - startedAt }))
- .filter(({ type, time }) => type !== TYPES.CONSOLE && time <= durationSeconds);
+ const events: InjectedEvent[] = []
+ const rawEvents: (EventData & { key: number})[] = []
- let resources = List(session.resources).map((r) => new Resource(r as any));
- resources.forEach((r: Resource) => {
- r.time = Math.max(0, r.time - startedAt)
- })
- resources = resources.sort((r1, r2) => r1.time - r2.time);
- const missedResources = resources.filter(({ success }) => !success);
+ if (session.events?.length) {
+ (session.events as EventData[]).forEach((event: EventData, k) => {
+ const time = event.timestamp - startedAt
+ if (event.type !== TYPES.CONSOLE && time <= durationSeconds) {
+ const EventClass = SessionEvent({ ...event, time, key: k })
+ if (EventClass) {
+ events.push(EventClass);
+ }
+ rawEvents.push({ ...event, time, key: k });
+ }
+ })
+ }
- const stackEventsList = List(stackEvents)
- .concat(List(session.userEvents))
- .sortBy((se) => se.timestamp)
- .map((se) => StackEvent({ ...se, time: se.timestamp - startedAt }));
- const exceptions = (errors as IError[]).map(e => new SessionError(e));
+ let resources = List(session.resources).map((r) => new Resource(r as any));
+ resources.forEach((r: Resource) => {
+ r.time = Math.max(0, r.time - startedAt)
+ })
+ resources = resources.sort((r1, r2) => r1.time - r2.time);
+ const missedResources = resources.filter(({ success }) => !success);
- const issuesList = (issues as IIssue[]).map((i, k) => new Issue({ ...i, time: i.timestamp - startedAt, key: k }));
+ 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 }))
+ stackEventsList.push(...mergedArrays);
+ }
- const rawEvents = !session.events
- ? []
- : // @ts-ignore
- session.events
- .map((evt) => ({ ...evt, time: evt.timestamp - startedAt }))
- .filter(({ type, time }) => type !== TYPES.CONSOLE && time <= durationSeconds) || [];
- const rawNotes = notes;
- const notesWithEvents = [...rawEvents, ...rawNotes].sort((a, b) => {
- const aTs = a.time || a.timestamp;
- const bTs = b.time || b.timestamp;
+ const exceptions = (errors as IError[]).map(e => new SessionError(e)) || [];
- return aTs - bTs;
- });
+ const issuesList = (issues as IIssue[]).map((i, k) => new Issue({ ...i, time: i.timestamp - startedAt, key: k })) || [];
- return {
- ...session,
- isIOS: session.platform === 'ios',
- errors: exceptions,
- siteId: projectId,
- events,
- stackEvents: stackEventsList,
- resources,
- missedResources,
- userDevice,
- userDeviceType,
- isMobile,
- startedAt,
- duration,
- userNumericHash: hashString(
- session.userId ||
- session.userAnonymousId ||
- session.userUuid ||
- session.userID ||
- session.userUUID ||
- ''
- ),
- userDisplayName:
- session.userId || session.userAnonymousId || session.userID || 'Anonymous User',
- issues: issuesList,
- sessionId: sessionId || sessionID,
- userId: session.userId || session.userID,
- mobsUrl: Array.isArray(mobsUrl) ? mobsUrl : [mobsUrl],
- domURL,
- devtoolsURL,
- notes,
- notesWithEvents: List(notesWithEvents),
- };
- },
- idKey: 'sessionId',
+ const rawNotes = notes;
+ const notesWithEvents = [...rawEvents, ...rawNotes].sort((a, b) => {
+ const aTs = a.time || a.timestamp;
+ const bTs = b.time || b.timestamp;
+
+ return aTs - bTs;
+ }) || [];
+
+ Object.assign(this, {
+ ...session,
+ isIOS: session.platform === 'ios',
+ errors: exceptions,
+ siteId: projectId,
+ events,
+ stackEvents: stackEventsList,
+ resources,
+ missedResources,
+ userDevice,
+ userDeviceType,
+ isMobile,
+ startedAt,
+ duration,
+ userNumericHash: hashString(
+ session.userId ||
+ session.userAnonymousId ||
+ session.userUuid ||
+ session.userID ||
+ session.userUUID ||
+ ''
+ ),
+ userDisplayName:
+ session.userId || session.userAnonymousId || session.userID || 'Anonymous User',
+ issues: issuesList,
+ sessionId: sessionId || sessionID,
+ userId: session.userId || session.userID,
+ mobsUrl: Array.isArray(mobsUrl) ? mobsUrl : [mobsUrl],
+ domURL,
+ devtoolsURL,
+ notes,
+ notesWithEvents: List(notesWithEvents),
+ })
}
-);
+}
\ No newline at end of file
diff --git a/frontend/app/types/session/stackEvent.js b/frontend/app/types/session/stackEvent.ts
similarity index 61%
rename from frontend/app/types/session/stackEvent.js
rename to frontend/app/types/session/stackEvent.ts
index 762407916..d1c4b7b09 100644
--- a/frontend/app/types/session/stackEvent.js
+++ b/frontend/app/types/session/stackEvent.ts
@@ -1,5 +1,3 @@
-import Record from 'Types/Record';
-
export const OPENREPLAY = 'openreplay';
export const SENTRY = 'sentry';
export const DATADOG = 'datadog';
@@ -37,23 +35,36 @@ export function isRed(event) {
}
}
-export default Record({
- time: undefined,
- index: undefined,
- name: '',
- message: "",
- payload: null,
- source: null,
- level: "",
-}, {
- fromJS: ue => ({
- ...ue,
- source: ue.source || OPENREPLAY,
- }),
- methods: {
- isRed() {
- return isRed(this);
- }
- }
-});
+export interface IStackEvent {
+ time: number;
+ timestamp: number;
+ index: number;
+ name: string;
+ message: string;
+ payload: any;
+ source: any;
+ level: string;
+ isRed: () => boolean;
+}
+
+export default class StackEvent {
+ time: IStackEvent["time"]
+ index: IStackEvent["index"];
+ name: IStackEvent["name"];
+ message: IStackEvent["message"];
+ payload: IStackEvent["payload"];
+ source: IStackEvent["source"];
+ level: IStackEvent["level"];
+
+ constructor(evt: IStackEvent) {
+ Object.assign(this, {
+ ...evt,
+ source: evt.source || OPENREPLAY
+ });
+ }
+
+ isRed() {
+ return isRed(this);
+ }
+}