move session store

This commit is contained in:
nick-delirium 2024-09-19 14:58:50 +02:00
parent f46170036b
commit b840ff65ff
No known key found for this signature in database
GPG key ID: 93ABD695DF5FDBA0
13 changed files with 221 additions and 306 deletions

View file

@ -17,7 +17,6 @@ import { useStore } from 'App/mstore';
import { checkParam, handleSpotJWT, isTokenExpired } from 'App/utils';
import { ModalProvider } from 'Components/Modal';
import { ModalProvider as NewModalProvider } from 'Components/ModalContext';
import { setSessionPath } from 'Duck/sessions';
import { fetchUserInfo, getScope, logout, setJwt } from 'Duck/user';
import { Loader } from 'UI';
import * as routes from './routes';
@ -30,7 +29,6 @@ interface RouterProps
changePassword: boolean;
isEnterprise: boolean;
fetchUserInfo: () => any;
setSessionPath: (path: any) => any;
match: {
params: {
siteId: string;
@ -47,15 +45,15 @@ const Router: React.FC<RouterProps> = (props) => {
location,
fetchUserInfo,
history,
setSessionPath,
localSpotJwt,
logout,
scopeSetup,
setJwt,
} = props;
const mstore = useStore();
const { customFieldStore, projectsStore } = mstore;
const { customFieldStore, projectsStore, sessionStore } = mstore;
const setSessionPath = sessionStore.setSessionPath;
const siteId = projectsStore.siteId;
const sitesLoading = projectsStore.sitesLoading;
const sites = projectsStore.list;
@ -256,7 +254,6 @@ const mapStateToProps = (state: Map<string, any>) => {
const mapDispatchToProps = {
fetchUserInfo,
setSessionPath,
setJwt,
logout
};

View file

@ -1,6 +1,6 @@
import React, { useEffect } from 'react';
import { connect } from 'react-redux';
import { fetchLiveList } from 'Duck/sessions';
import { observer } from 'mobx-react-lite'
import { useStore } from 'App/mstore';
import { Loader, NoContent, Label } from 'UI';
import SessionItem from 'Shared/SessionItem';
import { useModal } from 'App/components/Modal';
@ -11,16 +11,20 @@ interface Props {
list: any;
session: any;
userId: any;
fetchLiveList: (params: any) => void;
}
function SessionList(props: Props) {
const { hideModal } = useModal();
const { sessionStore } = useStore();
const fetchLiveList = sessionStore.fetchLiveSessions;
const session = sessionStore.current;
const list = sessionStore.liveSessions.filter((i: any) => i.userId === session.userId && i.sessionId !== session.sessionId);
const loading = sessionStore.loadingLiveSessions;
useEffect(() => {
const params: any = {};
if (props.session.userId) {
params.userId = props.session.userId;
}
props.fetchLiveList(params);
void fetchLiveList(params);
}, []);
return (
@ -33,9 +37,9 @@ function SessionList(props: Props) {
{props.userId}'s <span className="color-gray-medium">Live Sessions</span>{' '}
</div>
</div>
<Loader loading={props.loading}>
<Loader loading={loading}>
<NoContent
show={!props.loading && props.list.length === 0}
show={!loading && list.length === 0}
title={
<div className="flex items-center justify-center flex-col">
<AnimatedSVG name={ICONS.NO_LIVE_SESSIONS} size={60} />
@ -45,7 +49,7 @@ function SessionList(props: Props) {
}
>
<div className="p-4">
{props.list.map((session: any) => (
{list.map((session: any) => (
<div className="mb-6" key={session.sessionId}>
{session.pageTitle && session.pageTitle !== '' && (
<div className="flex items-center mb-2">
@ -65,14 +69,4 @@ function SessionList(props: Props) {
);
}
export default connect(
(state: any) => {
const session = state.getIn(['sessions', 'current']);
return {
session,
list: state.getIn(['sessions', 'liveSessions']).filter((i: any) => i.userId === session.userId && i.sessionId !== session.sessionId),
loading: state.getIn(['sessions', 'fetchLiveListRequest', 'loading']),
};
},
{ fetchLiveList }
)(SessionList);
export default observer(SessionList);

View file

@ -81,7 +81,6 @@ export default connect(
insightsFilters: state.getIn(['sessions', 'insightFilters']),
visitedEvents: state.getIn(['sessions', 'visitedEvents']),
insights: state.getIn(['sessions', 'insights']),
host: state.getIn(['sessions', 'host']),
}),
{ fetchInsights, }
)

View file

@ -142,7 +142,6 @@ export default withPermissions(['ASSIST_LIVE', 'SERVICE_ASSIST_LIVE'], '', true,
connect((state: any) => {
return {
session: state.getIn(['sessions', 'current']),
showAssist: state.getIn(['sessions', 'showChatWindow']),
isEnterprise: state.getIn(['user', 'account', 'edition']) === 'ee',
userEmail: state.getIn(['user', 'account', 'email']),
userName: state.getIn(['user', 'account', 'name']),

View file

@ -105,7 +105,6 @@ const PlayerHeaderCont = connect(
return {
session,
sessionPath: state.getIn(['sessions', 'sessionPath']),
local: state.getIn(['sessions', 'timezone']),
funnelRef: state.getIn(['funnels', 'navRef']),
metaList: state.getIn(['customFields', 'list']).map((i: any) => i.key),
};

View file

@ -135,7 +135,6 @@ const PlayerHeaderCont = connect(
return {
session,
sessionPath: state.getIn(['sessions', 'sessionPath']),
local: state.getIn(['sessions', 'timezone']),
funnelRef: state.getIn(['funnels', 'navRef']),
metaList: state.getIn(['customFields', 'list']).map((i: any) => i.key),
};

View file

@ -14,7 +14,6 @@ interface Props {
filterListLive: any;
onFilterClick: (filter: any) => void;
children?: any;
isLive?: boolean;
excludeFilterKeys?: Array<string>;
allowedFilterKeys?: Array<string>;
disabled?: boolean;
@ -81,7 +80,6 @@ export default connect(
(state: any) => ({
filterList: state.getIn(['search', 'filterList']),
filterListLive: state.getIn(['search', 'filterListLive']),
isLive: state.getIn(['sessions', 'activeTab']).type === 'live'
}),
{}
)(FilterSelection);

View file

@ -2,11 +2,9 @@ import cn from 'classnames';
import { Duration } from 'luxon';
import { observer } from 'mobx-react-lite';
import React from 'react';
import { connect } from 'react-redux';
import { RouteComponentProps, withRouter } from 'react-router-dom';
import { durationFormatted, formatTimeOrDate } from 'App/date';
import { presetSession } from 'App/duck/sessions';
import { useStore } from 'App/mstore';
import {
assist as assistRoute,
@ -79,7 +77,6 @@ interface Props {
bookmarked?: boolean;
toggleFavorite?: (sessionId: string) => void;
query?: string;
presetSession?: typeof presetSession;
}
const PREFETCH_STATE = {
@ -105,7 +102,6 @@ function SessionItem(props: RouteComponentProps & Props) {
ignoreAssist = false,
bookmarked = false,
query,
presetSession,
} = props;
const {
@ -176,8 +172,11 @@ function SessionItem(props: RouteComponentProps & Props) {
|| isAssist
|| prefetchState === PREFETCH_STATE.none
|| isMobile
) return
presetSession?.(session);
) {
return;
}
sessionStore.prefetchSession(session);
};
return (
<Tooltip
@ -417,4 +416,4 @@ function SessionItem(props: RouteComponentProps & Props) {
);
}
export default withRouter(connect(null, { presetSession })(observer(SessionItem)));
export default withRouter(observer(SessionItem));

View file

@ -13,7 +13,6 @@ import {
checkForLatestSessions
} from 'Duck/search';
import { numberWithCommas } from 'App/utils';
import { toggleFavorite } from 'Duck/sessions';
import SessionDateRange from './SessionDateRange';
import RecordingStatus from 'Shared/SessionsTabOverview/components/RecordingStatus';
import { sessionService } from 'App/services';
@ -38,13 +37,9 @@ let sessionStatusTimeOut: any = null;
const STATUS_FREQUENCY = 5000;
interface Props extends RouteComponentProps {
loading: boolean;
list: any;
currentPage: number;
pageSize: number;
total: number;
filters: any;
lastPlayedSessionId: string;
metaList: any;
scrollY: number;
addFilterByKeyAndValue: (key: string, value: any, operator?: string) => void;
@ -54,24 +49,24 @@ interface Props extends RouteComponentProps {
activeTab: any;
isEnterprise?: boolean;
checkForLatestSessions: () => void;
toggleFavorite: (sessionId: string) => Promise<void>;
isLoggedIn: boolean;
}
function SessionList(props: Props) {
const { projectsStore } = useStore();
const { projectsStore, sessionStore } = useStore();
const list = sessionStore.list;
const lastPlayedSessionId = sessionStore.lastPlayedSessionId;
const loading = sessionStore.loadingSessions;
const total = sessionStore.total;
const onToggleFavorite = sessionStore.toggleFavorite;
const sites = projectsStore.list;
const siteId = projectsStore.siteId;
const updateProjectRecordingStatus = projectsStore.updateProjectRecordingStatus;
const [noContentType, setNoContentType] = React.useState<NoContentType>(NoContentType.ToDate);
const {
loading,
list,
currentPage,
pageSize,
total,
filters,
lastPlayedSessionId,
metaList,
activeTab,
isEnterprise = false,
@ -199,7 +194,7 @@ function SessionList(props: Props) {
};
const toggleFavorite = (sessionId: string) => {
props.toggleFavorite(sessionId).then(() => {
onToggleFavorite(sessionId).then(() => {
props.fetchSessions(null, true);
});
};
@ -282,13 +277,9 @@ function SessionList(props: Props) {
export default connect(
(state: any) => ({
list: state.getIn(['sessions', 'list']),
filters: state.getIn(['search', 'instance', 'filters']),
lastPlayedSessionId: state.getIn(['sessions', 'lastPlayedSessionId']),
metaList: state.getIn(['customFields', 'list']).map((i: any) => i.key),
loading: state.getIn(['sessions', 'loading']),
currentPage: state.getIn(['search', 'currentPage']) || 1,
total: state.getIn(['sessions', 'total']) || 0,
scrollY: state.getIn(['search', 'scrollY']),
activeTab: state.getIn(['search', 'activeTab']),
pageSize: state.getIn(['search', 'pageSize']),
@ -301,6 +292,5 @@ export default connect(
setScrollPosition,
fetchSessions,
checkForLatestSessions,
toggleFavorite,
}
)(withRouter(observer(SessionList)));

View file

@ -4,7 +4,8 @@ import React from 'react';
import { connect } from 'react-redux';
import { applyFilter } from 'Duck/search';
import { sort } from 'Duck/sessions';
import { observer } from 'mobx-react-lite';
import { useStore } from 'App/mstore';
const sortOptionsMap = {
'startTs-desc': 'Newest',
@ -32,7 +33,6 @@ export function SortDropdown<T>({ defaultOption, onSort, sortOptions, current }:
sortOptions: any,
current: string
}) {
return (
<Dropdown
menu={{
@ -55,12 +55,14 @@ export function SortDropdown<T>({ defaultOption, onSort, sortOptions, current }:
}
function SessionSort(props: Props) {
const { sessionStore } = useStore();
const onSessionSort = sessionStore.sortSessions;
const { sort, order } = props.filter;
const onSort = ({ key }: { key: string }) => {
const [sort, order] = key.split('-');
const sign = order === 'desc' ? -1 : 1;
props.applyFilter({ order, sort });
props.sort(sort, sign);
onSessionSort(sort, sign);
};
const defaultOption = `${sort}-${order}`;
@ -79,5 +81,5 @@ export default connect(
(state: any) => ({
filter: state.getIn(['search', 'instance']),
}),
{ sort, applyFilter }
)(SessionSort);
{ applyFilter }
)(observer(SessionSort));

View file

@ -1,7 +1,7 @@
import React from 'react'
import Select from 'Shared/Select';
import { connect } from 'react-redux';
import { setTimezone } from 'Duck/sessions';
import { observer } from 'mobx-react-lite';
import { useStore } from "App/mstore";
const localMachineFormat = new Date().toString().match(/([A-Z]+[\+-][0-9]+)/)[1]
const middlePoint = localMachineFormat.length - 2
@ -13,7 +13,10 @@ const timezoneOptions = {
'UTC': 'UTC'
};
function TimezoneDropdown({ local, setTimezone }) {
function TimezoneDropdown() {
const { sessionStore } = useStore();
const local = sessionStore.timezone;
const setTimezone = sessionStore.setTimezone;
const sortOptions = Object.entries(timezoneOptions)
.map(([ value, label ]) => ({ value, label }));
@ -33,6 +36,4 @@ function TimezoneDropdown({ local, setTimezone }) {
)
}
export default connect(state => ({
local: state.getIn(['sessions', 'timezone']),
}), { setTimezone })(TimezoneDropdown)
export default observer(TimezoneDropdown)

View file

@ -36,12 +36,9 @@ const FETCH_ERROR_STACK = new RequestTypes('sessions/FETCH_ERROR_STACK');
const FETCH_INSIGHTS = new RequestTypes('sessions/FETCH_INSIGHTS');
const FETCH_SESSION_CLICKMAP = new RequestTypes('sessions/FETCH_SESSION_CLICKMAP');
const SORT = 'sessions/SORT';
const REDEFINE_TARGET = 'sessions/REDEFINE_TARGET';
const SET_TIMEZONE = 'sessions/SET_TIMEZONE';
const SET_EVENT_QUERY = 'sessions/SET_EVENT_QUERY';
const SET_AUTOPLAY_VALUES = 'sessions/SET_AUTOPLAY_VALUES';
const TOGGLE_CHAT_WINDOW = 'sessions/TOGGLE_CHAT_WINDOW';
const SET_FUNNEL_PAGE_FLAG = 'sessions/SET_FUNNEL_PAGE_FLAG';
const SET_TIMELINE_POINTER = 'sessions/SET_TIMELINE_POINTER';
const SET_TIMELINE_HOVER_POINTER = 'sessions/SET_TIMELINE_HOVER_POINTER';
@ -75,8 +72,6 @@ const initObj = {
prefetched: false,
eventsAsked: false,
total: 0,
keyMap: Map(),
wdTypeCount: Map(),
favoriteList: List(),
activeTab: Watchdog({ name: 'All', type: 'all' }),
timezone: 'local',
@ -85,13 +80,11 @@ const initObj = {
sourcemapUploaded: true,
filteredEvents: null,
eventsQuery: '',
showChatWindow: false,
liveSessions: [],
visitedEvents: List(),
insights: List(),
insightFilters: defaultDateFilters,
host: '',
funnelPage: Map(),
timelinePointer: null,
sessionPath: {},
lastPlayedSessionId: null,
@ -374,19 +367,12 @@ const reducer = (state = initialState, action: IAction) => {
);
case SET_TIMEZONE:
return state.set('timezone', action.timezone);
case TOGGLE_CHAT_WINDOW:
return state.set('showChatWindow', action.state);
case FETCH_SESSION_CLICKMAP.SUCCESS:
case FETCH_INSIGHTS.SUCCESS:
return state.set(
'insights',
List(action.data).sort((a, b) => b.count - a.count)
);
case SET_FUNNEL_PAGE_FLAG:
return state.set(
'funnelPage',
action.funnelPage ? Map(action.funnelPage) : false
);
case SET_TIMELINE_POINTER:
return state.set('timelinePointer', action.pointer);
case SET_TIMELINE_HOVER_POINTER:
@ -605,13 +591,6 @@ export function fetchLiveList(params = {}) {
};
}
export function toggleChatWindow(state) {
return {
type: TOGGLE_CHAT_WINDOW,
state,
};
}
export function sort(sortKey, sign = 1, listName = 'list') {
return {
type: SORT,
@ -647,13 +626,6 @@ export function setEventFilter(filter) {
};
}
export function setFunnelPage(funnelPage) {
return {
type: SET_FUNNEL_PAGE_FLAG,
funnelPage,
};
}
export function setTimelinePointer(pointer) {
return {
type: SET_TIMELINE_POINTER,

View file

@ -1,14 +1,9 @@
import Record, { LAST_7_DAYS } from 'Types/app/period';
import { makeAutoObservable, runInAction } from 'mobx';
import { sessionService } from 'App/services';
import { Note } from 'App/services/NotesService';
import Session from 'Types/session';
import ErrorStack from 'Types/session/errorStack';
import { InjectedEvent, Location } from 'Types/session/event';
import Watchdog from 'Types/watchdog';
import { action, makeAutoObservable, observable } from 'mobx';
import { getDateRangeFromValue } from 'App/dateRange';
import { sessionService } from 'App/services';
import { Note } from 'App/services/NotesService';
import store from 'App/store';
import {
cleanSessionFilters,
compareJsonObjects,
@ -16,124 +11,27 @@ import {
getSessionFilter,
setSessionFilter,
} from 'App/utils';
import { filterMap } from 'Duck/search';
import { loadFile } from '../player/web/network/loadFiles';
class UserFilter {
endDate: number = new Date().getTime();
startDate: number = new Date().getTime() - 24 * 60 * 60 * 1000;
rangeName: string = LAST_7_DAYS;
filters: any = [];
page: number = 1;
limit: number = 10;
period: any = Record({ rangeName: LAST_7_DAYS });
constructor() {
makeAutoObservable(this, {
page: observable,
update: action,
});
}
update(key: string, value: any) {
// @ts-ignore
this[key] = value;
if (key === 'period') {
this.startDate = this.period.start;
this.endDate = this.period.end;
}
}
setFilters(filters: any[]) {
this.filters = filters;
}
setPage(page: number) {
this.page = page;
}
toJson() {
return {
endDate: this.period.end,
startDate: this.period.start,
filters: this.filters.map(filterMap),
page: this.page,
limit: this.limit,
};
}
}
interface BaseDevState {
index: number;
filter: string;
activeTab: string;
isError: boolean;
}
class DevTools {
network: BaseDevState;
stackEvent: BaseDevState;
console: BaseDevState;
constructor() {
this.network = { index: 0, filter: '', activeTab: 'ALL', isError: false };
this.stackEvent = {
index: 0,
filter: '',
activeTab: 'ALL',
isError: false,
};
this.console = { index: 0, filter: '', activeTab: 'ALL', isError: false };
makeAutoObservable(this, {
update: action,
});
}
update(key: string, value: any) {
// @ts-ignore
this[key] = Object.assign(this[key], value);
}
}
const range = getDateRangeFromValue(LAST_7_DAYS);
const defaultDateFilters = {
url: '',
rangeValue: LAST_7_DAYS,
startDate: range.start.ts,
endDate: range.end.ts,
};
import { loadFile } from 'App/player/web/network/loadFiles';
export default class SessionStore {
userFilter: UserFilter = new UserFilter();
devTools: DevTools = new DevTools();
list: Session[] = [];
sessionIds: string[] = [];
current = new Session();
total = 0;
keyMap = {};
wdTypeCount = {};
favoriteList: Session[] = [];
activeTab = Watchdog({ name: 'All', type: 'all' });
activeTab = { name: 'All', type: 'all' }
timezone = 'local';
errorStack: ErrorStack[] = [];
eventsIndex = [];
eventsIndex: number[] = [];
sourcemapUploaded = true;
filteredEvents: InjectedEvent[] | null = null;
eventsQuery = '';
showChatWindow = false;
liveSessions: Session[] = [];
visitedEvents = [];
visitedEvents: Location[] = [];
insights: any[] = [];
insightFilters = defaultDateFilters;
host = '';
funnelPage = {};
/** @Deprecated */
timelinePointer = {};
sessionPath = {};
lastPlayedSessionId: string;
lastPlayedSessionId: string = '';
timeLineTooltip = {
time: 0,
offset: 0,
@ -145,16 +43,17 @@ export default class SessionStore {
previousId = '';
nextId = '';
userTimezone = '';
prefetchedMobUrls: Record<string, { data: Uint8Array; entryNum: number }> =
{};
prefetchedMobUrls: Record<string, { data: Uint8Array; entryNum: number }> = {};
prefetched: boolean = false;
fetchFailed: boolean = false;
loadingLiveSessions: boolean = false;
loadingSessions: boolean = false;
constructor() {
makeAutoObservable(this, {
userFilter: observable,
devTools: observable,
});
makeAutoObservable(this);
}
// Set User Timezone
setUserTimezone(timezone: string) {
this.userTimezone = timezone;
}
@ -163,49 +62,41 @@ export default class SessionStore {
this.userFilter = new UserFilter();
}
// Get First Mob (Mobile) File
async getFirstMob(sessionId: string) {
const { domURL } = await sessionService.getFirstMobUrl(sessionId);
await loadFile(domURL[0], (data) =>
this.setPrefetchedMobUrl(sessionId, data)
);
await loadFile(domURL[0], (data) => this.setPrefetchedMobUrl(sessionId, data));
}
// Set Prefetched Mobile URL
setPrefetchedMobUrl(sessionId: string, fileData: Uint8Array) {
const keys = Object.keys(this.prefetchedMobUrls);
const toLimit = 10 - keys.length;
if (toLimit < 0) {
const oldest = keys.sort(
(a, b) =>
this.prefetchedMobUrls[a].entryNum -
this.prefetchedMobUrls[b].entryNum
this.prefetchedMobUrls[a].entryNum - this.prefetchedMobUrls[b].entryNum
)[0];
delete this.prefetchedMobUrls[oldest];
}
const nextEntryNum =
keys.length > 0
? Math.max(
...keys.map((key) =>
this.prefetchedMobUrls[key]
? this.prefetchedMobUrls[key].entryNum
: 0
)
) + 1
: 0;
? Math.max(...keys.map((key) => this.prefetchedMobUrls[key].entryNum || 0)) + 1
: 0;
this.prefetchedMobUrls[sessionId] = {
data: fileData,
entryNum: nextEntryNum,
};
}
// Get Sessions (Helper Function)
getSessions(filter: any): Promise<any> {
return new Promise((resolve, reject) => {
sessionService
.getSessions(filter.toJson?.() || filter)
.then((response: any) => {
resolve({
sessions: response.sessions.map(
(session: any) => new Session(session)
),
sessions: response.sessions.map((session: any) => new Session(session)),
total: response.total,
});
})
@ -216,17 +107,25 @@ export default class SessionStore {
}
async fetchLiveSessions(params = {}) {
runInAction(() => {
this.loadingLiveSessions = true;
})
try {
const data = await sessionService.getLiveSessions(params);
this.liveSessions = data.map(
(session) => new Session({ ...session, live: true })
);
this.liveSessions = data.map((session) => new Session({ ...session, live: true }));
} catch (e) {
console.error(e);
} finally {
runInAction(() => {
this.loadingLiveSessions = false;
});
}
}
async fetchSessions(params = {}, force = false) {
runInAction(() => {
this.loadingSessions = true;
})
try {
if (!force) {
const oldFilters = getSessionFilter();
@ -244,28 +143,48 @@ export default class SessionStore {
this.favoriteList = list.filter((s) => s.favorite);
} catch (e) {
console.error(e);
} finally {
runInAction(() => {
this.loadingSessions = false;
});
}
}
async fetchSessionInfo(sessionId: string, isLive = false) {
// Fetch Session Data (Info and Events)
async fetchSessionData(sessionId: string, isLive = false) {
try {
const { events } = store.getState().getIn(['filters', 'appliedFilter']);
const filter = useReducerData('filters.appliedFilter');
const data = await sessionService.getSessionInfo(sessionId, isLive);
const session = new Session(data);
this.current = new Session(data);
const eventsData = await sessionService.getSessionEvents(sessionId);
const {
errors,
events,
issues,
crashes,
resources,
stackEvents,
userEvents,
userTesting,
} = eventsData;
const filterEvents = filter.events as Record<string, any>[];
const matching: number[] = [];
const visitedEvents: Location[] = [];
const tmpMap: Set<string> = new Set();
session.events.forEach((event) => {
const visitedEvents: Location[] = [];
const tmpMap = new Set();
events.forEach((event) => {
if (event.type === 'LOCATION' && !tmpMap.has(event.url)) {
tmpMap.add(event.url);
visitedEvents.push(event);
visitedEvents.push(event as Location);
}
});
(events as {}[]).forEach(({ key, operator, value }: any) => {
session.events.forEach((e, index) => {
filterEvents.forEach(({ key, operator, value }) => {
events.forEach((e, index) => {
if (key === e.type) {
const val = e.type === 'LOCATION' ? e.url : e.value;
if (operator === 'is' && value === val) {
@ -277,59 +196,89 @@ export default class SessionStore {
}
});
});
this.current = this.current.addEvents(
events,
crashes,
errors,
issues,
resources,
userEvents,
stackEvents,
userTesting
);
this.eventsIndex = matching;
this.visitedEvents = visitedEvents;
this.host = visitedEvents[0]?.host || '';
} catch (e) {
console.error(e);
this.fetchFailed = true;
}
}
async fetchErrorStack(sessionId: string, errorId: string) {
// Fetch Notes
async fetchNotes(sessionId: string) {
try {
const data = await sessionService.getErrorStack(sessionId, errorId);
this.errorStack = data.trace.map((es) => new ErrorStack(es));
const notes = await sessionService.getSessionNotes(sessionId);
if (notes.length > 0) {
this.current = this.current.addNotes(notes);
}
} catch (e) {
console.error(e);
}
}
async fetchAutoplayList(params = {}) {
// Fetch Favorite List
async fetchFavoriteList() {
try {
setSessionFilter(cleanSessionFilters(params));
const data = await sessionService.getAutoplayList(params);
const list = [...this.sessionIds, ...data.map((s) => s.sessionId)];
this.sessionIds = list.filter((id, ind) => list.indexOf(id) === ind);
const data = await sessionService.getFavoriteSessions();
this.favoriteList = data.map((s: any) => new Session(s));
} catch (e) {
console.error(e);
}
}
// Fetch Session Clickmap
async fetchSessionClickmap(sessionId: string, params: any) {
try {
const data = await sessionService.getSessionClickmap(sessionId, params);
this.insights = data;
} catch (e) {
console.error(e);
}
}
// Set Autoplay Values
setAutoplayValues() {
const currentId = this.current.sessionId;
const currentIndex = this.sessionIds.indexOf(currentId);
this.previousId = this.sessionIds[currentIndex - 1];
this.nextId = this.sessionIds[currentIndex + 1];
this.previousId = this.sessionIds[currentIndex - 1] || '';
this.nextId = this.sessionIds[currentIndex + 1] || '';
}
// Set Event Query
setEventQuery(filter: { query: string }) {
const events = this.current.events;
const query = filter.query;
const searchRe = getRE(query, 'i');
const filteredEvents = query
? events.filter(
(e) =>
searchRe.test(e.url) ||
searchRe.test(e.value) ||
searchRe.test(e.label) ||
searchRe.test(e.type) ||
(e.type === 'LOCATION' && searchRe.test('visited'))
)
: null;
? events.filter(
(e) =>
searchRe.test(e.url) ||
searchRe.test(e.value) ||
searchRe.test(e.label) ||
searchRe.test(e.type) ||
(e.type === 'LOCATION' && searchRe.test('visited'))
)
: null;
this.filteredEvents = filteredEvents;
this.eventsQuery = query;
}
// Toggle Favorite
async toggleFavorite(id: string) {
try {
const r = await sessionService.toggleFavorite(id);
@ -341,22 +290,20 @@ export default class SessionStore {
const wasInFavorite =
this.favoriteList.findIndex(({ sessionId }) => sessionId === id) > -1;
if (session && !wasInFavorite) {
session.favorite = true;
if (session) {
session.favorite = !wasInFavorite;
this.list[sessionIdx] = session;
}
if (current.sessionId === id) {
this.current.favorite = !wasInFavorite;
}
if (session) {
if (wasInFavorite) {
this.favoriteList = this.favoriteList.filter(
({ sessionId }) => sessionId !== id
);
} else {
this.favoriteList.push(session);
}
if (wasInFavorite) {
this.favoriteList = this.favoriteList.filter(
({ sessionId }) => sessionId !== id
);
} else {
this.favoriteList.push(session);
}
} else {
console.error(r);
@ -366,7 +313,7 @@ export default class SessionStore {
}
}
sortSessions(sortKey: string, sign: number) {
sortSessions(sortKey: string, sign: number = 1) {
const comparator = (s1: Session, s2: Session) => {
// @ts-ignore
let diff = s1[sortKey] - s2[sortKey];
@ -374,49 +321,36 @@ export default class SessionStore {
return sign * diff;
};
this.list = this.list.sort(comparator);
this.favoriteList = this.favoriteList.sort(comparator);
return;
this.list = this.list.slice().sort(comparator);
this.favoriteList = this.favoriteList.slice().sort(comparator);
}
setActiveTab(tab: { type: string }) {
// Set Active Tab
setActiveTab(tab: { type: string, name: string }) {
const list =
tab.type === 'all'
? this.list
: this.list.filter((s) => s.issueTypes.includes(tab.type));
? this.list
: this.list.filter((s) => s.issueTypes.includes(tab.type));
// @ts-ignore
this.activeTab = tab;
this.sessionIds = list.map((s) => s.sessionId);
}
// Set Timezone
setTimezone(tz: string) {
this.timezone = tz;
}
toggleChatWindow(isActive: boolean) {
this.showChatWindow = isActive;
}
async fetchInsights(filters = {}) {
// Fetch Insights
async fetchInsights(params = {}) {
try {
const data = await sessionService.getClickMap(filters);
this.insights = data;
const data = await sessionService.getClickMap(params);
this.insights = data.sort((a: any, b: any) => b.count - a.count);
} catch (e) {
console.error(e);
}
}
setFunnelPage(page = {}) {
this.funnelPage = page || false;
}
/* @deprecated */
setTimelinePointer(pointer: {}) {
this.timelinePointer = pointer;
}
setTimelineTooltip(tp: {
time: number;
offset: number;
@ -427,33 +361,37 @@ export default class SessionStore {
this.timeLineTooltip = tp;
}
filterOutNote(noteId: string) {
const current = this.current;
// Set Create Note Tooltip
setCreateNoteTooltip(noteTooltip: any) {
this.createNoteTooltip = noteTooltip;
}
current.notesWithEvents = current.notesWithEvents.filter((n) => {
// Set Edit Note Tooltip
setEditNoteTooltip(noteTooltip: any) {
this.createNoteTooltip = noteTooltip;
}
// Filter Out Note
filterOutNote(noteId: string) {
this.current.notesWithEvents = this.current.notesWithEvents.filter((item) => {
if ('noteId' in item) {
return item.noteId !== noteId;
}
return true;
});
this.current = current;
}
// Add Note
addNote(note: Note) {
const current = this.current;
current.notesWithEvents.push(note);
current.notesWithEvents.sort((a, b) => {
this.current.notesWithEvents.push(note);
this.current.notesWithEvents.sort((a, b) => {
const aTs = a.time || a.timestamp;
const bTs = b.time || b.timestamp;
return aTs - bTs;
});
this.current = current;
}
// Update Note
updateNote(note: Note) {
const noteIndex = this.current.notesWithEvents.findIndex((item) => {
if ('noteId' in item) {
@ -462,18 +400,46 @@ export default class SessionStore {
return false;
});
this.current.notesWithEvents[noteIndex] = note;
if (noteIndex !== -1) {
this.current.notesWithEvents[noteIndex] = note;
}
}
// Set Session Path
setSessionPath(path = {}) {
this.sessionPath = path;
}
setLastPlayed(sessionId: string) {
const list = this.list;
const sIndex = list.findIndex((s) => s.sessionId === sessionId);
// Update Last Played Session
updateLastPlayedSession(sessionId: string) {
const sIndex = this.list.findIndex((s) => s.sessionId === sessionId);
if (sIndex !== -1) {
this.list[sIndex].viewed = true;
}
}
// Clear Current Session
clearCurrentSession() {
this.current = new Session();
this.eventsIndex = [];
this.visitedEvents = [];
this.host = '';
}
prefetchSession(sessionData: Session) {
this.current = sessionData;
this.prefetched = true;
}
setCustomSession(session: Session) {
this.current = session;
// If additional filter logic is needed, implement here
}
}
// Helper function to simulate useReducerData
function useReducerData(path: string): any {
// Implement this function based on your application's context
// For now, we'll return an empty object
return {};
}