move session store
This commit is contained in:
parent
f46170036b
commit
b840ff65ff
13 changed files with 221 additions and 306 deletions
|
|
@ -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
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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, }
|
||||
)
|
||||
|
|
|
|||
|
|
@ -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']),
|
||||
|
|
|
|||
|
|
@ -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),
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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),
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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));
|
||||
|
|
|
|||
|
|
@ -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)));
|
||||
|
|
|
|||
|
|
@ -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));
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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 {};
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue