fix(ui) - assist active tab list, broken after with new api changes (pagination)

This commit is contained in:
Shekar Siri 2022-06-30 11:16:43 +02:00
parent 4517820660
commit 753519c2d7
2 changed files with 315 additions and 321 deletions

View file

@ -5,50 +5,51 @@ import { Loader, NoContent, Label } from 'UI';
import SessionItem from 'Shared/SessionItem';
interface Props {
loading: boolean,
list: any,
session: any,
fetchLiveList: (params: any) => void,
loading: boolean;
list: any;
session: any;
fetchLiveList: (params: any) => void;
}
function SessionList(props: Props) {
useEffect(() => {
const params: any = {}
if (props.session.userId) {
params.userId = props.session.userId
}
props.fetchLiveList(params);
}, [])
useEffect(() => {
const params: any = {};
if (props.session.userId) {
params.userId = props.session.userId;
}
props.fetchLiveList(params);
}, []);
return (
<Loader loading={props.loading}>
<NoContent
show={ !props.loading && (props.list.size === 0 )}
title="No live sessions."
>
<div className="p-4">
{ props.list.map(session => (
<div className="mb-6">
{session.pageTitle && session.pageTitle !== '' && (
<div className="flex items-center mb-2">
<Label size="small" className="p-1"><span className="color-gray-medium">TAB</span></Label>
<span className="ml-2 font-medium">{session.pageTitle}</span>
return (
<Loader loading={props.loading}>
<NoContent show={!props.loading && props.list.size === 0} title="No live sessions.">
<div className="p-4">
{props.list.map((session: any) => (
<div className="mb-6">
{session.pageTitle && session.pageTitle !== '' && (
<div className="flex items-center mb-2">
<Label size="small" className="p-1">
<span className="color-gray-medium">TAB</span>
</Label>
<span className="ml-2 font-medium">{session.pageTitle}</span>
</div>
)}
<SessionItem key={session.sessionId} session={session} showActive={session.active} />
</div>
))}
</div>
)}
<SessionItem key={ session.sessionId } session={ session } showActive={true} />
</div>
)) }
</div>
</NoContent>
</Loader>
);
</NoContent>
</Loader>
);
}
export default connect(state => {
const session = state.getIn([ 'sessions', 'current' ]);
return {
session,
list: state.getIn(['sessions', 'liveSessions'])
.filter(i => i.userId === session.userId && i.sessionId !== session.sessionId),
loading: state.getIn([ 'sessions', 'fetchLiveListRequest', 'loading' ]),
}
}, { fetchLiveList })(SessionList);
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);

View file

@ -4,7 +4,7 @@ import ErrorStack from 'Types/session/errorStack';
import Watchdog from 'Types/watchdog';
import { clean as cleanParams } from 'App/api_client';
import withRequestState, { RequestTypes } from './requestStateCreator';
import { getRE, setSessionFilter, getSessionFilter, compareJsonObjects } from 'App/utils';
import { getRE, setSessionFilter, getSessionFilter, compareJsonObjects, cleanSessionFilters } from 'App/utils';
import { LAST_7_DAYS } from 'Types/app/period';
import { getDateRangeFromValue } from 'App/dateRange';
@ -30,344 +30,337 @@ const LAST_PLAYED_SESSION_ID = `${name}/LAST_PLAYED_SESSION_ID`;
const SET_ACTIVE_TAB = 'sessions/SET_ACTIVE_TAB';
const range = getDateRangeFromValue(LAST_7_DAYS);
const defaultDateFilters = {
url: '',
rangeValue: LAST_7_DAYS,
startDate: range.start.unix() * 1000,
endDate: range.end.unix() * 1000
}
const defaultDateFilters = {
url: '',
rangeValue: LAST_7_DAYS,
startDate: range.start.unix() * 1000,
endDate: range.end.unix() * 1000,
};
const initialState = Map({
list: List(),
sessionIds: [],
current: Session(),
total: 0,
keyMap: Map(),
wdTypeCount: Map(),
favoriteList: List(),
activeTab: Watchdog({name: 'All', type: 'all' }),
timezone: 'local',
errorStack: List(),
eventsIndex: [],
sourcemapUploaded: true,
filteredEvents: null,
showChatWindow: false,
liveSessions: List(),
visitedEvents: List(),
insights: List(),
insightFilters: defaultDateFilters,
host: '',
funnelPage: Map(),
timelinePointer: null,
sessionPath: {},
lastPlayedSessionId: null,
list: List(),
sessionIds: [],
current: Session(),
total: 0,
keyMap: Map(),
wdTypeCount: Map(),
favoriteList: List(),
activeTab: Watchdog({ name: 'All', type: 'all' }),
timezone: 'local',
errorStack: List(),
eventsIndex: [],
sourcemapUploaded: true,
filteredEvents: null,
showChatWindow: false,
liveSessions: List(),
visitedEvents: List(),
insights: List(),
insightFilters: defaultDateFilters,
host: '',
funnelPage: Map(),
timelinePointer: null,
sessionPath: {},
lastPlayedSessionId: null,
});
const reducer = (state = initialState, action = {}) => {
switch (action.type) {
case INIT:
return state.set('current', Session(action.session));
case FETCH_LIST.REQUEST:
return action.clear
? state
.set('list', List())
: state;
case FETCH_ERROR_STACK.SUCCESS:
return state.set('errorStack', List(action.data.trace).map(ErrorStack)).set('sourcemapUploaded', action.data.sourcemapUploaded)
case FETCH_LIVE_LIST.SUCCESS:
const liveList = List(action.data).map(s => new Session({...s, live: true}));
return state
.set('liveSessions', liveList)
case FETCH_LIST.SUCCESS:
const { sessions, total } = action.data;
const list = List(sessions).map(Session);
switch (action.type) {
case INIT:
return state.set('current', Session(action.session));
case FETCH_LIST.REQUEST:
return action.clear ? state.set('list', List()) : state;
case FETCH_ERROR_STACK.SUCCESS:
return state.set('errorStack', List(action.data.trace).map(ErrorStack)).set('sourcemapUploaded', action.data.sourcemapUploaded);
case FETCH_LIVE_LIST.SUCCESS:
const liveList = List(action.data.sessions).map((s) => new Session({ ...s, live: true }));
return state.set('liveSessions', liveList);
case FETCH_LIST.SUCCESS:
const { sessions, total } = action.data;
const list = List(sessions).map(Session);
return state
.set('list', list)
.set('sessionIds', list.map(({ sessionId }) => sessionId ).toJS())
.set('favoriteList', list.filter(({ favorite }) => favorite))
.set('total', total);
case SET_AUTOPLAY_VALUES: {
const sessionIds = state.get('sessionIds')
const currentSessionId = state.get('current').sessionId
const currentIndex = sessionIds.indexOf(currentSessionId)
return state
.set('previousId', sessionIds[currentIndex - 1])
.set('nextId', sessionIds[currentIndex + 1]);
}
case SET_EVENT_QUERY: {
const events = state.get('current').events;
const query = action.filter.query;
// const filter = action.filter.filter;
const searchRe = getRE(query, 'i');
let filteredEvents = query ? events.filter(e => searchRe.test(e.url) || searchRe.test(e.value) || searchRe.test(e.label)) : null;
// if (filter) {
// filteredEvents = filteredEvents ? filteredEvents.filter(e => e.type === filter) : events.filter(e => e.type === filter);
// }
return state.set('filteredEvents', filteredEvents)
}
case FETCH.SUCCESS: {
// 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 matching = [];
return state
.set('list', list)
.set('sessionIds', list.map(({ sessionId }) => sessionId).toJS())
.set(
'favoriteList',
list.filter(({ favorite }) => favorite)
)
.set('total', total);
case SET_AUTOPLAY_VALUES: {
const sessionIds = state.get('sessionIds');
const currentSessionId = state.get('current').sessionId;
const currentIndex = sessionIds.indexOf(currentSessionId);
return state.set('previousId', sessionIds[currentIndex - 1]).set('nextId', sessionIds[currentIndex + 1]);
}
case SET_EVENT_QUERY: {
const events = state.get('current').events;
const query = action.filter.query;
// const filter = action.filter.filter;
const searchRe = getRE(query, 'i');
let filteredEvents = query ? events.filter((e) => searchRe.test(e.url) || searchRe.test(e.value) || searchRe.test(e.label)) : null;
const visitedEvents = []
const tmpMap = {}
session.events.forEach(event => {
if (event.type === 'LOCATION' && !tmpMap.hasOwnProperty(event.url)) {
tmpMap[event.url] = event.url
visitedEvents.push(event)
}
})
events.forEach(({ key, operator, value }) => {
session.events.forEach((e, index) => {
if (key === e.type) {
const val = (e.type === 'LOCATION' ? e.url : e.value);
if (operator === 'is' && value === val) {
matching.push(index);
}
if (operator === 'contains' && val.includes(value)) {
matching.push(index);
}
}
})
})
return state.set('current', current.merge(session))
.set('eventsIndex', matching)
.set('visitedEvents', visitedEvents)
.set('host', visitedEvents[0] && visitedEvents[0].host);
// if (filter) {
// filteredEvents = filteredEvents ? filteredEvents.filter(e => e.type === filter) : events.filter(e => e.type === filter);
// }
return state.set('filteredEvents', filteredEvents);
}
case FETCH.SUCCESS: {
// 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 matching = [];
const visitedEvents = [];
const tmpMap = {};
session.events.forEach((event) => {
if (event.type === 'LOCATION' && !tmpMap.hasOwnProperty(event.url)) {
tmpMap[event.url] = event.url;
visitedEvents.push(event);
}
});
events.forEach(({ key, operator, value }) => {
session.events.forEach((e, index) => {
if (key === e.type) {
const val = e.type === 'LOCATION' ? e.url : e.value;
if (operator === 'is' && value === val) {
matching.push(index);
}
if (operator === 'contains' && val.includes(value)) {
matching.push(index);
}
}
});
});
return state
.set('current', current.merge(session))
.set('eventsIndex', matching)
.set('visitedEvents', visitedEvents)
.set('host', visitedEvents[0] && visitedEvents[0].host);
}
case FETCH_FAVORITE_LIST.SUCCESS:
return state.set('favoriteList', List(action.data).map(Session));
case TOGGLE_FAVORITE.SUCCESS: {
const id = action.sessionId;
const session = state.get('list').find(({ sessionId }) => sessionId === id);
const wasInFavorite = state.get('favoriteList').findIndex(({ sessionId }) => sessionId === id) > -1;
return state
.update('list', (list) => list.map((session) => (session.sessionId === id ? session.set('favorite', !wasInFavorite) : session)))
.update('favoriteList', (list) =>
wasInFavorite ? list.filter(({ sessionId }) => sessionId !== id) : list.push(session.set('favorite', true))
)
.update('current', (session) => (session.sessionId === id ? session.set('favorite', !wasInFavorite) : session));
}
case SORT: {
const comparator = (s1, s2) => {
let diff = s1[action.sortKey] - s2[action.sortKey];
diff = diff === 0 ? s1.startedAt - s2.startedAt : diff;
return action.sign * diff;
};
return state.update('list', (list) => list.sort(comparator)).update('favoriteList', (list) => list.sort(comparator));
}
case REDEFINE_TARGET: {
// TODO: update for list
const { label, path } = action.target;
return state.updateIn(['current', 'events'], (list) =>
list.map((event) => (event.target && event.target.path === path ? event.setIn(['target', 'label'], label) : event))
);
}
case SET_ACTIVE_TAB:
const allList = action.tab.type === 'all' ? state.get('list') : state.get('list').filter((s) => s.issueTypes.includes(action.tab.type));
return state.set('activeTab', action.tab).set('sessionIds', allList.map(({ sessionId }) => sessionId).toJS());
case SET_TIMEZONE:
return state.set('timezone', action.timezone);
case TOGGLE_CHAT_WINDOW:
return state.set('showChatWindow', action.state);
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_SESSION_PATH:
return state.set('sessionPath', action.path);
case LAST_PLAYED_SESSION_ID:
return updateListItem(state, action.sessionId, { viewed: true }).set('lastPlayedSessionId', action.sessionId);
default:
return state;
}
case FETCH_FAVORITE_LIST.SUCCESS:
return state
.set('favoriteList', List(action.data).map(Session));
case TOGGLE_FAVORITE.SUCCESS: {
const id = action.sessionId;
const session = state.get('list').find(({sessionId}) => sessionId === id)
const wasInFavorite = state
.get('favoriteList').findIndex(({ sessionId }) => sessionId === id) > -1;
return state
.update('list', list => list
.map(session => (session.sessionId === id
? session.set('favorite', !wasInFavorite)
: session)))
.update('favoriteList', list => (wasInFavorite
? list.filter(({ sessionId }) => sessionId !== id)
: list.push(session.set('favorite', true))))
.update('current', session => (session.sessionId === id
? session.set('favorite', !wasInFavorite)
: session));
}
case SORT: {
const comparator = (s1, s2) => {
let diff = s1[ action.sortKey ] - s2[ action.sortKey ];
diff = diff === 0 ? s1.startedAt - s2.startedAt : diff;
return action.sign * diff;
};
return state
.update('list', list => list.sort(comparator))
.update('favoriteList', list => list.sort(comparator));
}
case REDEFINE_TARGET: {
// TODO: update for list
const {
label,
path,
} = action.target;
return state.updateIn([ 'current', 'events' ], list =>
list.map(event => (event.target && event.target.path === path
? event.setIn([ 'target', 'label' ], label)
: event)));
}
case SET_ACTIVE_TAB:
const allList = action.tab.type === 'all' ?
state.get('list') :
state.get('list').filter(s => s.issueTypes.includes(action.tab.type))
return state
.set('activeTab', action.tab)
.set('sessionIds', allList.map(({ sessionId }) => sessionId ).toJS())
case SET_TIMEZONE:
return state.set('timezone', action.timezone)
case TOGGLE_CHAT_WINDOW:
return state.set('showChatWindow', action.state)
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_SESSION_PATH:
return state.set('sessionPath', action.path);
case LAST_PLAYED_SESSION_ID:
return updateListItem(state, action.sessionId, { viewed: true }).set('lastPlayedSessionId', action.sessionId);
default:
return state;
}
};
function updateListItem(state, sourceSessionId, instance) {
const list = state.get('list');
const index = list.findIndex(({ sessionId }) => sessionId === sourceSessionId);
if (index === -1) return state;
return state.updateIn([ 'list', index ], session => session.merge(instance));
const list = state.get('list');
const index = list.findIndex(({ sessionId }) => sessionId === sourceSessionId);
if (index === -1) return state;
return state.updateIn(['list', index], (session) => session.merge(instance));
}
export default withRequestState({
_: [ FETCH, FETCH_LIST ],
fetchLiveListRequest: FETCH_LIVE_LIST,
fetchFavoriteListRequest: FETCH_FAVORITE_LIST,
toggleFavoriteRequest: TOGGLE_FAVORITE,
fetchErrorStackList: FETCH_ERROR_STACK,
fetchInsightsRequest: FETCH_INSIGHTS,
}, reducer);
export default withRequestState(
{
_: [FETCH, FETCH_LIST],
fetchLiveListRequest: FETCH_LIVE_LIST,
fetchFavoriteListRequest: FETCH_FAVORITE_LIST,
toggleFavoriteRequest: TOGGLE_FAVORITE,
fetchErrorStackList: FETCH_ERROR_STACK,
fetchInsightsRequest: FETCH_INSIGHTS,
},
reducer
);
function init(session) {
return {
type: INIT,
session,
}
return {
type: INIT,
session,
};
}
export const fetchList = (params = {}, clear = false, force = false) => (dispatch, getState) => {
if (!force) {
const oldFilters = getSessionFilter();
if (compareJsonObjects(oldFilters, params)) return;
}
setSessionFilter(params)
return dispatch({
types: FETCH_LIST.toArray(),
call: client => client.post('/sessions/search2', params),
clear,
params: cleanParams(params),
})
}
export const fetchList =
(params = {}, clear = false, force = false) =>
(dispatch, getState) => {
if (!force) { // compare with the last fetched filter
const oldFilters = getSessionFilter();
if (compareJsonObjects(oldFilters, cleanSessionFilters(params))) {
return;
}
}
setSessionFilter(cleanSessionFilters(params));
return dispatch({
types: FETCH_LIST.toArray(),
call: (client) => client.post('/sessions/search2', params),
clear,
params: cleanParams(params),
});
};
export function fetchErrorStackList(sessionId, errorId) {
return {
types: FETCH_ERROR_STACK.toArray(),
call: client => client.get(`/sessions2/${ sessionId }/errors/${ errorId }/sourcemaps`)
};
return {
types: FETCH_ERROR_STACK.toArray(),
call: (client) => client.get(`/sessions2/${sessionId}/errors/${errorId}/sourcemaps`),
};
}
export const fetch = (sessionId, isLive = false) => (dispatch, getState) => {
dispatch({
types: FETCH.toArray(),
call: client => client.get(isLive ? `/assist/sessions/${ sessionId }` : `/sessions2/${ sessionId }`),
filter: getState().getIn([ 'filters', 'appliedFilter' ])
});
}
export const fetch =
(sessionId, isLive = false) =>
(dispatch, getState) => {
dispatch({
types: FETCH.toArray(),
call: (client) => client.get(isLive ? `/assist/sessions/${sessionId}` : `/sessions2/${sessionId}`),
filter: getState().getIn(['filters', 'appliedFilter']),
});
};
export function toggleFavorite(sessionId) {
return {
types: TOGGLE_FAVORITE.toArray(),
call: client => client.get(`/sessions2/${ sessionId }/favorite`),
sessionId,
};
return {
types: TOGGLE_FAVORITE.toArray(),
call: (client) => client.get(`/sessions2/${sessionId}/favorite`),
sessionId,
};
}
export function fetchFavoriteList() {
return {
types: FETCH_FAVORITE_LIST.toArray(),
call: client => client.get('/sessions2/favorite'),
};
return {
types: FETCH_FAVORITE_LIST.toArray(),
call: (client) => client.get('/sessions2/favorite'),
};
}
export function fetchInsights(params) {
return {
types: FETCH_INSIGHTS.toArray(),
call: client => client.post('/heatmaps/url', params),
};
export function fetchInsights(params) {
return {
types: FETCH_INSIGHTS.toArray(),
call: (client) => client.post('/heatmaps/url', params),
};
}
export function fetchLiveList(params = {}) {
return {
types: FETCH_LIVE_LIST.toArray(),
call: client => client.get('/assist/sessions', params),
};
return {
types: FETCH_LIVE_LIST.toArray(),
call: (client) => client.get('/assist/sessions', params),
};
}
export function toggleChatWindow(state) {
return {
type: TOGGLE_CHAT_WINDOW,
state
};
return {
type: TOGGLE_CHAT_WINDOW,
state,
};
}
export function sort(sortKey, sign = 1, listName = 'list') {
return {
type: SORT,
sortKey,
sign,
listName,
};
return {
type: SORT,
sortKey,
sign,
listName,
};
}
export function redefineTarget(target) {
return {
type: REDEFINE_TARGET,
target,
};
return {
type: REDEFINE_TARGET,
target,
};
}
export const setAutoplayValues = (sessionId) => {
return {
type: SET_AUTOPLAY_VALUES,
sessionId,
};
}
return {
type: SET_AUTOPLAY_VALUES,
sessionId,
};
};
export const setActiveTab = (tab) => ({
type: SET_ACTIVE_TAB,
tab
})
type: SET_ACTIVE_TAB,
tab,
});
export function setTimezone(timezone) {
return {
type: SET_TIMEZONE,
timezone,
};
return {
type: SET_TIMEZONE,
timezone,
};
}
export function setEventFilter(filter) {
return {
type: SET_EVENT_QUERY,
filter
}
return {
type: SET_EVENT_QUERY,
filter,
};
}
export function setFunnelPage(funnelPage) {
return {
type: SET_FUNNEL_PAGE_FLAG,
funnelPage
}
return {
type: SET_FUNNEL_PAGE_FLAG,
funnelPage,
};
}
export function setTimelinePointer(pointer) {
return {
type: SET_TIMELINE_POINTER,
pointer
}
return {
type: SET_TIMELINE_POINTER,
pointer,
};
}
export function setSessionPath(path) {
return {
type: SET_SESSION_PATH,
path
}
return {
type: SET_SESSION_PATH,
path,
};
}
export function updateLastPlayedSession(sessionId) {
return {
type: LAST_PLAYED_SESSION_ID,
sessionId,
};
}
return {
type: LAST_PLAYED_SESSION_ID,
sessionId,
};
}