From 753519c2d77da92f975302c93241a901b00f13d3 Mon Sep 17 00:00:00 2001 From: Shekar Siri Date: Thu, 30 Jun 2022 11:16:43 +0200 Subject: [PATCH] fix(ui) - assist active tab list, broken after with new api changes (pagination) --- .../components/SessionList/SessionList.tsx | 83 +-- frontend/app/duck/sessions.js | 553 +++++++++--------- 2 files changed, 315 insertions(+), 321 deletions(-) diff --git a/frontend/app/components/Assist/components/SessionList/SessionList.tsx b/frontend/app/components/Assist/components/SessionList/SessionList.tsx index 32c267588..79aa4af28 100644 --- a/frontend/app/components/Assist/components/SessionList/SessionList.tsx +++ b/frontend/app/components/Assist/components/SessionList/SessionList.tsx @@ -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 ( - - -
- { props.list.map(session => ( -
- {session.pageTitle && session.pageTitle !== '' && ( -
- - {session.pageTitle} + return ( + + +
+ {props.list.map((session: any) => ( +
+ {session.pageTitle && session.pageTitle !== '' && ( +
+ + {session.pageTitle} +
+ )} + +
+ ))}
- )} - -
- )) } -
- - - ); + + + ); } -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); \ No newline at end of file +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); diff --git a/frontend/app/duck/sessions.js b/frontend/app/duck/sessions.js index 633cf0942..0d40ae9e5 100644 --- a/frontend/app/duck/sessions.js +++ b/frontend/app/duck/sessions.js @@ -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, - }; -} \ No newline at end of file + return { + type: LAST_PLAYED_SESSION_ID, + sessionId, + }; +}