diff --git a/frontend/app/PrivateRoutes.tsx b/frontend/app/PrivateRoutes.tsx index b9d2cb247..d256df7f8 100644 --- a/frontend/app/PrivateRoutes.tsx +++ b/frontend/app/PrivateRoutes.tsx @@ -1,14 +1,15 @@ import withSiteIdUpdater from 'HOCs/withSiteIdUpdater'; import React, { Suspense, lazy } from 'react'; import { Redirect, Route, Switch } from 'react-router-dom'; -import { observer } from 'mobx-react-lite' -import { useStore } from "./mstore"; +import { observer } from 'mobx-react-lite'; +import { useStore } from './mstore'; import { GLOBAL_HAS_NO_RECORDINGS } from 'App/constants/storageKeys'; import { OB_DEFAULT_TAB } from 'App/routes'; import { Loader } from 'UI'; import APIClient from './api_client'; import * as routes from './routes'; +import { debounce } from '@/utils'; const components: any = { SessionPure: lazy(() => import('Components/Session/Session')), @@ -31,7 +32,7 @@ const components: any = { SpotsListPure: lazy(() => import('Components/Spots/SpotsList')), SpotPure: lazy(() => import('Components/Spots/SpotPlayer')), ScopeSetup: lazy(() => import('Components/ScopeForm')), - HighlightsPure: lazy(() => import('Components/Highlights/HighlightsList')), + HighlightsPure: lazy(() => import('Components/Highlights/HighlightsList')) }; const enhancedComponents: any = { @@ -51,7 +52,7 @@ const enhancedComponents: any = { SpotsList: withSiteIdUpdater(components.SpotsListPure), Spot: components.SpotPure, ScopeSetup: components.ScopeSetup, - Highlights: withSiteIdUpdater(components.HighlightsPure), + Highlights: withSiteIdUpdater(components.HighlightsPure) }; const withSiteId = routes.withSiteId; @@ -97,9 +98,10 @@ const SPOT_PATH = routes.spot(); const SCOPE_SETUP = routes.scopeSetup(); const HIGHLIGHTS_PATH = routes.highlights(); +let debounceSearch: any = () => {}; function PrivateRoutes() { - const { projectsStore, userStore, integrationsStore } = useStore(); + const { projectsStore, userStore, integrationsStore, searchStore } = useStore(); const onboarding = userStore.onboarding; const scope = userStore.scopeState; const tenantId = userStore.account.tenantId; @@ -113,10 +115,20 @@ function PrivateRoutes() { React.useEffect(() => { if (siteId && integrationsStore.integrations.siteId !== siteId) { - integrationsStore.integrations.setSiteId(siteId) + integrationsStore.integrations.setSiteId(siteId); void integrationsStore.integrations.fetchIntegrations(siteId); } - }, [siteId]) + }, [siteId]); + + React.useEffect(() => { + debounceSearch = debounce(() => searchStore.fetchSessions(), 500); + }, []); + + React.useEffect(() => { + if (!searchStore.urlParsed) return; + debounceSearch(); + }, [searchStore.instance.filters, searchStore.instance.eventsOrder]); + return ( }> @@ -153,13 +165,13 @@ function PrivateRoutes() { case '/integrations/slack': client.post('integrations/slack/add', { code: location.search.split('=')[1], - state: tenantId, + state: tenantId }); break; case '/integrations/msteams': client.post('integrations/msteams/add', { code: location.search.split('=')[1], - state: tenantId, + state: tenantId }); break; } @@ -184,7 +196,7 @@ function PrivateRoutes() { withSiteId(DASHBOARD_PATH, siteIdList), withSiteId(DASHBOARD_SELECT_PATH, siteIdList), withSiteId(DASHBOARD_METRIC_CREATE_PATH, siteIdList), - withSiteId(DASHBOARD_METRIC_DETAILS_PATH, siteIdList), + withSiteId(DASHBOARD_METRIC_DETAILS_PATH, siteIdList) ]} component={enhancedComponents.Dashboard} /> @@ -245,7 +257,7 @@ function PrivateRoutes() { withSiteId(FFLAG_READ_PATH, siteIdList), withSiteId(FFLAG_CREATE_PATH, siteIdList), withSiteId(NOTES_PATH, siteIdList), - withSiteId(BOOKMARKS_PATH, siteIdList), + withSiteId(BOOKMARKS_PATH, siteIdList) ]} component={enhancedComponents.SessionsOverview} /> @@ -264,7 +276,7 @@ function PrivateRoutes() { {Object.entries(routes.redirects).map(([fr, to]) => ( ))} - + diff --git a/frontend/app/components/shared/SessionFilters/SessionFilters.tsx b/frontend/app/components/shared/SessionFilters/SessionFilters.tsx index 54a03b285..c6a918c0a 100644 --- a/frontend/app/components/shared/SessionFilters/SessionFilters.tsx +++ b/frontend/app/components/shared/SessionFilters/SessionFilters.tsx @@ -57,10 +57,6 @@ function SessionFilters() { } }); - useEffect(() => { - debounceFetch(); - }, [appliedFilter.filters]); - const onAddFilter = (filter: any) => { filter.autoOpen = true; searchStore.addFilter(filter); @@ -72,13 +68,13 @@ function SessionFilters() { const onFilterMove = (newFilters: any) => { searchStore.updateSearch({ ...appliedFilter, filters: newFilters}); - debounceFetch(); + // debounceFetch(); }; const onRemoveFilter = (filterIndex: any) => { searchStore.removeFilter(filterIndex); - debounceFetch(); + // debounceFetch(); }; const onChangeEventsOrder = (e: any, { value }: any) => { @@ -86,7 +82,7 @@ function SessionFilters() { eventsOrder: value, }); - debounceFetch(); + // debounceFetch(); }; return ( diff --git a/frontend/app/components/shared/SessionsTabOverview/components/LatestSessionsMessage/LatestSessionsMessage.tsx b/frontend/app/components/shared/SessionsTabOverview/components/LatestSessionsMessage/LatestSessionsMessage.tsx index 03b55e3b1..666ed39e7 100644 --- a/frontend/app/components/shared/SessionsTabOverview/components/LatestSessionsMessage/LatestSessionsMessage.tsx +++ b/frontend/app/components/shared/SessionsTabOverview/components/LatestSessionsMessage/LatestSessionsMessage.tsx @@ -6,9 +6,10 @@ import { observer } from 'mobx-react-lite'; function LatestSessionsMessage() { const { searchStore } = useStore(); - const count = searchStore.latestList.size; + const count = searchStore.latestSessionCount; const onShowNewSessions = () => { + searchStore.updateLatestSessionCount(0); void searchStore.updateCurrentPage(1, true); }; diff --git a/frontend/app/components/shared/SessionsTabOverview/components/SessionList/SessionList.tsx b/frontend/app/components/shared/SessionsTabOverview/components/SessionList/SessionList.tsx index e02cb9246..2d05133b2 100644 --- a/frontend/app/components/shared/SessionsTabOverview/components/SessionList/SessionList.tsx +++ b/frontend/app/components/shared/SessionsTabOverview/components/SessionList/SessionList.tsx @@ -47,10 +47,10 @@ function SessionList() { const hasNoRecordings = !activeSite || !activeSite.recorded; const metaList = customFieldStore.list; - // useEffect(() => { - // if (!searchStore.urlParsed) return; - // void searchStore.fetchSessions(true, isBookmark); - // }, [location.pathname]); + useEffect(() => { + if (!searchStore.urlParsed) return; + void searchStore.checkForLatestSessionCount(); + }, [location.pathname]); const NO_CONTENT = React.useMemo(() => { if (isBookmark && !isEnterprise) { @@ -110,7 +110,7 @@ function SessionList() { useEffect(() => { const id = setInterval(() => { if (!document.hidden) { - searchStore.checkForLatestSessions(); + void searchStore.checkForLatestSessionCount(); } }, AUTO_REFRESH_INTERVAL); return () => clearInterval(id); @@ -134,7 +134,7 @@ function SessionList() { sessionTimeOut = setTimeout(function () { if (!document.hidden) { - searchStore.checkForLatestSessions(); + void searchStore.checkForLatestSessionCount(); } }, 5000); }; diff --git a/frontend/app/mstore/searchStore.ts b/frontend/app/mstore/searchStore.ts index 1e0a00544..f6c1abd49 100644 --- a/frontend/app/mstore/searchStore.ts +++ b/frontend/app/mstore/searchStore.ts @@ -5,11 +5,11 @@ import { filtersMap, generateFilterOptions, liveFiltersMap, - mobileConditionalFiltersMap, + mobileConditionalFiltersMap } from 'Types/filter/newFilter'; import { List } from 'immutable'; import { makeAutoObservable, runInAction } from 'mobx'; -import { searchService } from 'App/services'; +import { searchService, sessionService } from 'App/services'; import Search from 'App/mstore/types/search'; import { checkFilterValue } from 'App/mstore/types/filter'; import FilterItem from 'App/mstore/types/filterItem'; @@ -28,18 +28,18 @@ export const checkValues = (key: any, value: any) => { }; export const filterMap = ({ - category, - value, - key, - operator, - sourceOperator, - source, - custom, - isEvent, - filters, - sort, - order, -}: any) => ({ + category, + value, + key, + operator, + sourceOperator, + source, + custom, + isEvent, + filters, + sort, + order + }: any) => ({ value: checkValues(key, value), custom, type: category === FilterCategory.METADATA ? FilterKey.METADATA : key, @@ -47,7 +47,7 @@ export const filterMap = ({ source: category === FilterCategory.METADATA ? key.replace(/^_/, '') : source, sourceOperator, isEvent, - filters: filters ? filters.map(filterMap) : [], + filters: filters ? filters.map(filterMap) : [] }); export const TAB_MAP: any = { @@ -55,7 +55,7 @@ export const TAB_MAP: any = { sessions: { name: 'Sessions', type: 'sessions' }, bookmarks: { name: 'Bookmarks', type: 'bookmarks' }, notes: { name: 'Notes', type: 'notes' }, - recommendations: { name: 'Recommendations', type: 'recommendations' }, + recommendations: { name: 'Recommendations', type: 'recommendations' } }; class SearchStore { @@ -72,6 +72,7 @@ class SearchStore { scrollY = 0; sessions = List(); total: number = 0; + latestSessionCount: number = 0; loadingFilterSearch = false; isSaving: boolean = false; activeTags: any[] = []; @@ -111,9 +112,9 @@ class SearchStore { this.edit({ filters: savedSearch.filter ? savedSearch.filter.filters.map((i: FilterItem) => - new FilterItem().fromJson(i) - ) - : [], + new FilterItem().fromJson(i) + ) + : [] }); this.currentPage = 1; } @@ -179,6 +180,10 @@ class SearchStore { void this.fetchSessions(force); } + updateLatestSessionCount(count: number = 0) { + this.latestSessionCount = count; + } + setActiveTab(tab: string) { runInAction(() => { this.activeTab = TAB_MAP[tab]; @@ -228,7 +233,7 @@ class SearchStore { rangeValue: instance.rangeValue, startDate: instance.startDate, endDate: instance.endDate, - filters: [], + filters: [] }) ); @@ -237,78 +242,67 @@ class SearchStore { void this.fetchSessions(true); } - checkForLatestSessionCount() { - const filter = this.instance.toSearch(); - if (this.latestRequestTime) { - const period = Period({ - rangeName: CUSTOM_RANGE, - start: this.latestRequestTime, - end: Date.now(), - }); - const newTimestamps: any = period.toJSON(); - filter.startDate = newTimestamps.startDate; - filter.endDate = newTimestamps.endDate; - } - // TODO - dedicated API endpoint to get the count of latest sessions, or show X+ sessions - delete filter.limit; - delete filter.page; - searchService.checkLatestSessions(filter).then((response: any) => { - console.log('response', response); - // runInAction(() => { - // this.latestList = List(response); - // }); - }); - } + async checkForLatestSessionCount(): Promise { + try { + const filter = this.instance.toSearch(); + + // Set time filter if we have the latest request time + if (this.latestRequestTime) { + const period = Period({ + rangeName: CUSTOM_RANGE, + start: this.latestRequestTime, + end: Date.now() + }); + const timeRange: any = period.toJSON(); + filter.startDate = timeRange.startDate; + filter.endDate = timeRange.endDate; + } + + // Only need the total count, not actual records + filter.limit = 1; + filter.page = 1; + + const response = await sessionService.getSessions(filter); - checkForLatestSessions() { - const filter = this.instance.toSearch(); - if (this.latestRequestTime) { - const period = Period({ - rangeName: CUSTOM_RANGE, - start: this.latestRequestTime, - end: Date.now(), - }); - const newTimestamps: any = period.toJSON(); - filter.startDate = newTimestamps.startDate; - filter.endDate = newTimestamps.endDate; - } - // TODO - dedicated API endpoint to get the count of latest sessions, or show X+ sessions - delete filter.limit; - delete filter.page; - searchService.checkLatestSessions(filter).then((response: any) => { runInAction(() => { - this.latestList = List(response); + if (response?.total && response.total > sessionStore.total) { + this.latestSessionCount = response.total - sessionStore.total; + } else { + this.latestSessionCount = 0; + } }); - }); + } catch (error) { + console.error('Failed to check for latest session count:', error); + } } addFilter(filter: any) { const index = filter.isEvent ? -1 : this.instance.filters.findIndex( - (i: FilterItem) => i.key === filter.key - ); + (i: FilterItem) => i.key === filter.key + ); filter.value = checkFilterValue(filter.value); filter.filters = filter.filters ? filter.filters.map((subFilter: any) => ({ - ...subFilter, - value: checkFilterValue(subFilter.value), - })) + ...subFilter, + value: checkFilterValue(subFilter.value) + })) : null; if (index > -1) { const oldFilter = new FilterItem(this.instance.filters[index]); const updatedFilter = { ...oldFilter, - value: oldFilter.value.concat(filter.value), + value: oldFilter.value.concat(filter.value) }; oldFilter.merge(updatedFilter); this.updateFilter(index, updatedFilter); } else { this.instance.filters.push(filter); this.instance = new Search({ - ...this.instance.toData(), + ...this.instance.toData() }); } @@ -346,7 +340,7 @@ class SearchStore { updateSearch = (search: Partial) => { this.instance = Object.assign(this.instance, search); - } + }; updateFilter = (index: number, search: Partial) => { const newFilters = this.instance.filters.map((_filter: any, i: any) => { @@ -359,7 +353,7 @@ class SearchStore { this.instance = new Search({ ...this.instance.toData(), - filters: newFilters, + filters: newFilters }); }; @@ -370,7 +364,7 @@ class SearchStore { this.instance = new Search({ ...this.instance.toData(), - filters: newFilters, + filters: newFilters }); }; @@ -393,7 +387,7 @@ class SearchStore { const tagFilter = filtersMap[FilterKey.ISSUE]; tagFilter.type = tagFilter.type.toLowerCase(); tagFilter.value = [ - issues_types.find((i: any) => i.type === this.activeTags[0])?.type, + issues_types.find((i: any) => i.type === this.activeTags[0])?.type ]; delete tagFilter.operatorOptions; delete tagFilter.options; @@ -413,7 +407,7 @@ class SearchStore { filter.filters.push({ type: FilterKey.DURATION, value, - operator: 'is', + operator: 'is' }); } } @@ -427,7 +421,7 @@ class SearchStore { page: this.currentPage, perPage: this.pageSize, tab: this.activeTab.type, - bookmarked: bookmarked ? true : undefined, + bookmarked: bookmarked ? true : undefined }, force ).finally(() => {