diff --git a/package-lock.json b/package-lock.json index fe210e1f3..319ccea61 100644 --- a/package-lock.json +++ b/package-lock.json @@ -41993,8 +41993,11 @@ }, "packages/chrome": { "name": "@redhat-cloud-services/chrome", - "version": "1.0.2", + "version": "1.0.3", "license": "Apache-2.0", + "dependencies": { + "lodash": "^4.17.21" + }, "devDependencies": { "@redhat-cloud-services/types": "^1.0.3", "@types/react": "^18.0.0", @@ -49307,7 +49310,8 @@ "requires": { "@redhat-cloud-services/types": "^1.0.3", "@types/react": "^18.0.0", - "glob": "10.3.3" + "glob": "10.3.3", + "lodash": "^4.17.21" } }, "@redhat-cloud-services/eslint-config-redhat-cloud-services": { diff --git a/packages/chrome/src/ChromeProvider/ChromeProvider.test.tsx b/packages/chrome/src/ChromeProvider/ChromeProvider.test.tsx index 12e0b8d5e..957e19fa7 100644 --- a/packages/chrome/src/ChromeProvider/ChromeProvider.test.tsx +++ b/packages/chrome/src/ChromeProvider/ChromeProvider.test.tsx @@ -31,7 +31,7 @@ describe('ChromeProvider', () => { expect(getSpy).toHaveBeenCalledWith('/api/chrome-service/v1/user'); }); - test('should post new data on title change', async () => { + test.skip('should post new data on title change', async () => { jest.useFakeTimers(); getSpy.mockResolvedValueOnce([]); postSpy.mockResolvedValue(['/', '/bar']); diff --git a/packages/chrome/src/ChromeProvider/ChromeProvider.tsx b/packages/chrome/src/ChromeProvider/ChromeProvider.tsx index 519f40692..acf9bbe77 100644 --- a/packages/chrome/src/ChromeProvider/ChromeProvider.tsx +++ b/packages/chrome/src/ChromeProvider/ChromeProvider.tsx @@ -20,6 +20,8 @@ const postDataDebounced = debounce(async (pathname: string, title: string, bundl // should help limit number of API calls }, 5000); +// FIXME: Use this hook once the issues with dead locking are resolved +// eslint-disable-next-line @typescript-eslint/no-unused-vars const useLastPageVisitedUploader = (providerState: ReturnType) => { const scalprum = useScalprum<{ initialized: boolean; api: { chrome: ChromeAPI } }>(); const { pathname } = useLocation(); @@ -73,6 +75,62 @@ const useLastPageVisitedUploader = (providerState: ReturnType) => { + const { pathname } = useLocation(); + const scalprum = useScalprum(); + const titleTarget = document.querySelector('title'); + useEffect(() => { + let titleObserver: MutationObserver | undefined; + let prevTitle: string | null; + const lastVisited = localStorage.getItem(LAST_VISITED_FLAG); + if (lastVisited) { + try { + const lastVisited: LastVisitedPage[] = JSON.parse(localStorage.getItem(LAST_VISITED_FLAG) ?? '[]'); + if (!Array.isArray(lastVisited)) { + localStorage.setItem(LAST_VISITED_FLAG, JSON.stringify([])); + providerState.setLastVisited([]); + } else { + providerState.setLastVisited(lastVisited); + } + } catch (error) { + console.error('Unable to parse last visited pages from localStorage!', error); + providerState.setLastVisited([]); + localStorage.setItem(LAST_VISITED_FLAG, JSON.stringify([])); + } + } + + if (titleTarget) { + titleObserver = new MutationObserver((mutations) => { + // grab text from the title element + const currentTitle = mutations[0]?.target.textContent; + // trigger only if the titles are different + if (typeof currentTitle === 'string' && currentTitle !== prevTitle) { + try { + prevTitle = currentTitle; + const newTitles = providerState.getState().lastVisitedPages.filter((item) => item.pathname !== pathname); + newTitles.unshift({ pathname, title: currentTitle, bundle: scalprum.api?.chrome.getBundleData().bundleTitle }); + providerState.setLastVisited(newTitles.slice(0, 10)); + + localStorage.setItem(LAST_VISITED_FLAG, JSON.stringify(newTitles.slice(0, 10))); + } catch (error) { + // catch sync errors + console.error('Unable to update last visited pages!', error); + } + } + }); + + titleObserver.observe(titleTarget, { + childList: true, + }); + } + return () => { + titleObserver?.disconnect(); + }; + }, [pathname]); +}; + const ChromeProvider: React.FC = ({ children }) => { const isMounted = useRef(false); const [initialRequest, setInitialRequest] = useState(false); @@ -81,7 +139,7 @@ const ChromeProvider: React.FC = ({ children }) => { providerState.current = chromeState(); } - useLastPageVisitedUploader(providerState.current); + useLastVisitedLocalStorage(providerState.current); useEffect(() => { isMounted.current = true;