From 2d97eb6834aea5ac3a505c941e9522a146ebcdd7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gabriel=20Mu=C3=B1oz?= Date: Wed, 30 Sep 2026 13:32:59 -0700 Subject: [PATCH] Fix back/forward browser navigation Restore time state whenever URL parameters change. Populate missing defaults with replaceIn, while user-applied time changes use pushIn, preserving navigable deep-URL history without mount-time entries. Fixes #150 --- src/gprofiler/frontend/src/App.jsx | 1 + .../components/comparison/ComparisonPage.jsx | 6 +- .../src/hooks/useTimePickQueryParams.js | 69 ++++++++++++------- .../src/states/selectors/SelectorsContext.jsx | 11 ++- 4 files changed, 55 insertions(+), 32 deletions(-) diff --git a/src/gprofiler/frontend/src/App.jsx b/src/gprofiler/frontend/src/App.jsx index 8c390742..ab45f786 100644 --- a/src/gprofiler/frontend/src/App.jsx +++ b/src/gprofiler/frontend/src/App.jsx @@ -28,6 +28,7 @@ import theme from './theme/theme'; const queryParamOptions = { objectToSearchString: (query) => transformSearchStringJsonSafe(objectToSearchString(query)), + updateType: 'replaceIn', }; function App() { diff --git a/src/gprofiler/frontend/src/components/comparison/ComparisonPage.jsx b/src/gprofiler/frontend/src/components/comparison/ComparisonPage.jsx index 8d4b44cb..7955a070 100644 --- a/src/gprofiler/frontend/src/components/comparison/ComparisonPage.jsx +++ b/src/gprofiler/frontend/src/components/comparison/ComparisonPage.jsx @@ -47,14 +47,14 @@ const ComparisonPage = memo(() => { customParam: 'cService', }); - const [compareTimeSelection, setCompareTimeSelection] = useState({ + const [compareTimeSelection, setCompareTimeSelectionState] = useState({ relativeTime: DEFAULT_INITIAL_TIME_RANGE_FILTER, }); const [absoluteCompareTime, setAbsoluteCompareTime] = useState({ cStartTime: '', cEndTime: '' }); - useTimePickQueryParams({ - setTimeSelection: setCompareTimeSelection, + const setCompareTimeSelection = useTimePickQueryParams({ + setTimeSelection: setCompareTimeSelectionState, timeSelection: compareTimeSelection, customQueryParams: { time: 'cTime', start: 'cStartTime', end: 'cEndTime' }, }); diff --git a/src/gprofiler/frontend/src/hooks/useTimePickQueryParams.js b/src/gprofiler/frontend/src/hooks/useTimePickQueryParams.js index c2cc1025..661cd571 100644 --- a/src/gprofiler/frontend/src/hooks/useTimePickQueryParams.js +++ b/src/gprofiler/frontend/src/hooks/useTimePickQueryParams.js @@ -46,28 +46,40 @@ const useTimePickQueryParams = ({ setTimeSelection, timeSelection, customQueryPa const startTime = queryParams[START_TIME_QUERY]; const endTime = queryParams[END_TIME_QUERY]; - const handleTimeChange = useCallback(() => { - if (timeSelection.relativeTime) { - setQueryParams({ - [TIME_QUERY]: MAP_TIME_TO_FILTER_QUERY[timeSelection.relativeTime], - [START_TIME_QUERY]: undefined, - [END_TIME_QUERY]: undefined, - }); - } else if (timeSelection.customTime) { - setQueryParams({ - [TIME_QUERY]: undefined, - [START_TIME_QUERY]: timeSelection.customTime?.startTime?.toISOString(), - [END_TIME_QUERY]: timeSelection.customTime?.endTime?.toISOString(), - }); - } - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [setQueryParams, timeSelection]); + const updateTimeQueryParams = useCallback( + (newTimeSelection, updateType) => { + if (newTimeSelection.relativeTime) { + setQueryParams( + { + [TIME_QUERY]: MAP_TIME_TO_FILTER_QUERY[newTimeSelection.relativeTime], + [START_TIME_QUERY]: undefined, + [END_TIME_QUERY]: undefined, + }, + updateType + ); + } else if (newTimeSelection.customTime) { + setQueryParams( + { + [TIME_QUERY]: undefined, + [START_TIME_QUERY]: newTimeSelection.customTime?.startTime?.toISOString(), + [END_TIME_QUERY]: newTimeSelection.customTime?.endTime?.toISOString(), + }, + updateType + ); + } + }, + [END_TIME_QUERY, START_TIME_QUERY, TIME_QUERY, setQueryParams] + ); - useEffect(() => { - if (location.pathname === PAGES.profiles.to || location.pathname === PAGES.comparison.to) { - handleTimeChange(); - } - }, [timeSelection, handleTimeChange, location.pathname]); + const setTimeSelectionFromUser = useCallback( + (newTimeSelection) => { + setTimeSelection(newTimeSelection); + if (location.pathname === PAGES.profiles.to || location.pathname === PAGES.comparison.to) { + updateTimeQueryParams(newTimeSelection, 'pushIn'); + } + }, + [location.pathname, setTimeSelection, updateTimeQueryParams] + ); useEffect(() => { if (time || (startTime && endTime)) { @@ -79,8 +91,19 @@ const useTimePickQueryParams = ({ setTimeSelection, timeSelection, customQueryPa setTimeSelection({ relativeTime: DEFAULT_INITIAL_TIME_RANGE_FILTER }); } } - // eslint-disable-next-line react-hooks/exhaustive-deps - }, []); + }, [endTime, setTimeSelection, startTime, time]); + + useEffect(() => { + if ( + (location.pathname === PAGES.profiles.to || location.pathname === PAGES.comparison.to) && + !time && + !(startTime && endTime) + ) { + updateTimeQueryParams(timeSelection, 'replaceIn'); + } + }, [endTime, location.pathname, startTime, time, timeSelection, updateTimeQueryParams]); + + return setTimeSelectionFromUser; }; export default useTimePickQueryParams; diff --git a/src/gprofiler/frontend/src/states/selectors/SelectorsContext.jsx b/src/gprofiler/frontend/src/states/selectors/SelectorsContext.jsx index b6f5ca84..89dfe1d6 100644 --- a/src/gprofiler/frontend/src/states/selectors/SelectorsContext.jsx +++ b/src/gprofiler/frontend/src/states/selectors/SelectorsContext.jsx @@ -37,10 +37,14 @@ export const SelectorsProvider = ({ children }) => { const [ignoreZeros, setIgnoreZeros] = useState(true); const [selectedServiceEnvType, setSelectedServiceEnvType] = useState(''); - const [timeSelection, setTimeSelection] = useState({ relativeTime: DEFAULT_INITIAL_TIME_RANGE_FILTER }); + const [timeSelection, setTimeSelectionState] = useState({ relativeTime: DEFAULT_INITIAL_TIME_RANGE_FILTER }); const [absoluteTimeSelection, setAbsoluteTimeSelection] = useState({ startTime: '', endTime: '' }); const [timeFetched, setTimeFetched] = useState(undefined); + const setTimeSelection = useTimePickQueryParams({ + setTimeSelection: setTimeSelectionState, + timeSelection, + }); const resetSelectedTimeRange = () => { setTimeSelection({ relativeTime: DEFAULT_INITIAL_TIME_RANGE_FILTER }); }; @@ -59,11 +63,6 @@ export const SelectorsProvider = ({ children }) => { } }, [selectedService, services]); - useTimePickQueryParams({ - setTimeSelection, - timeSelection, - }); - useGraphViewQueryParams({ viewMode, setViewMode }); useGraphTabQueryParams({ selectedGraphTab, setSelectedGraphTab });