diff --git a/packages/library-detection/src/components/libraryDetection/index.tsx b/packages/library-detection/src/components/libraryDetection/index.tsx index dcde91862..0ebc0e443 100644 --- a/packages/library-detection/src/components/libraryDetection/index.tsx +++ b/packages/library-detection/src/components/libraryDetection/index.tsx @@ -33,9 +33,10 @@ import type { LibraryData, AccordionProps } from '../../types'; const LibraryDetection = memo(function LibraryDetection() { useLibraryDetection(); - const { libraryMatches, showLoader, isCurrentTabLoading } = + const { libraryMatches, showLoader, isCurrentTabLoading, errorOccured } = useLibraryDetectionContext(({ state }) => ({ libraryMatches: state.libraryMatches, + errorOccured: state.errorOccured, showLoader: state.showLoader, isCurrentTabLoading: state.isCurrentTabLoading, })); @@ -57,7 +58,7 @@ const LibraryDetection = memo(function LibraryDetection() { ]; const result = - detectedLibraryNames.length > 0 ? ( + !errorOccured && detectedLibraryNames.length > 0 ? ( <> {LIBRARIES.map((library) => { const Component = library.component as React.FC; @@ -81,10 +82,15 @@ const LibraryDetection = memo(function LibraryDetection() { ); })} - ) : ( + ) : !errorOccured ? (

No libraries with known breakages found yet!

+ ) : ( +

+ A library detection error occurred. Please reopen the DevTool on a valid + URL. +

); return ( @@ -93,7 +99,7 @@ const LibraryDetection = memo(function LibraryDetection() { testId="library-detection" description="" > - {showLoader ? ( + {!errorOccured && showLoader ? ( <>

diff --git a/packages/library-detection/src/core/stateProvider/index.tsx b/packages/library-detection/src/core/stateProvider/index.tsx index 56875c04b..0141ac15f 100644 --- a/packages/library-detection/src/core/stateProvider/index.tsx +++ b/packages/library-detection/src/core/stateProvider/index.tsx @@ -42,6 +42,7 @@ export interface LibraryDetectionContext { loadedBefore: boolean; showLoader: boolean; tabId: number; + errorOccured: boolean; }; actions: { setLibraryMatches: React.Dispatch>; @@ -49,6 +50,7 @@ export interface LibraryDetectionContext { setIsInitialDataUpdated: React.Dispatch>; setLoadedBeforeState: React.Dispatch>; setShowLoader: React.Dispatch>; + setErrorOccured: React.Dispatch>; }; } @@ -62,6 +64,7 @@ const initialState: LibraryDetectionContext = { loadedBefore: false, showLoader: true, tabId: -1, + errorOccured: false, }, actions: { setLibraryMatches: noop, @@ -69,6 +72,7 @@ const initialState: LibraryDetectionContext = { setIsInitialDataUpdated: noop, setLoadedBeforeState: noop, setShowLoader: noop, + setErrorOccured: noop, }, }; @@ -83,6 +87,7 @@ export const LibraryDetectionProvider = ({ children }: PropsWithChildren) => { useState(false); // TODO: Use first/current tab loaded state instead. const [isInitialDataUpdated, setIsInitialDataUpdated] = useState(false); const [loadedBefore, setLoadedBeforeState] = useState(false); + const [errorOccured, setErrorOccured] = useState(false); const [showLoader, setShowLoader] = useState(true); const [tabId, setTabId] = useState(-1); @@ -90,6 +95,15 @@ export const LibraryDetectionProvider = ({ children }: PropsWithChildren) => { setTabId(chrome.devtools.inspectedWindow.tabId); }, []); + const onErrorOccuredListener = useCallback( + ({ frameId }: chrome.webNavigation.WebNavigationFramedCallbackDetails) => { + if (frameId === 0) { + setErrorOccured(true); + } + }, + [] + ); + // It is attached, next time the tab is updated or reloaded. const onTabUpdate = useCallback( (changingTabId: number, changeInfo: chrome.tabs.TabChangeInfo) => { @@ -105,23 +119,44 @@ export const LibraryDetectionProvider = ({ children }: PropsWithChildren) => { } } }, - [ - setIsCurrentTabLoading, - setLibraryMatches, - setShowLoader, - setLoadedBeforeState, - tabId, - ] + [tabId] + ); + + const onNavigatedListener = useCallback( + ({ frameId }: chrome.webNavigation.WebNavigationFramedCallbackDetails) => { + if (frameId === 0) { + setLibraryMatches(initialLibraryMatches); + setIsCurrentTabLoading(true); + setShowLoader(true); + setLoadedBeforeState(false); + setIsInitialDataUpdated(false); + } + }, + [] + ); + + const onCompleted = useCallback( + ({ frameId }: chrome.webNavigation.WebNavigationFramedCallbackDetails) => { + if (frameId === 0) { + setErrorOccured(false); + } + }, + [] ); useEffect(() => { chrome.tabs.onUpdated.removeListener(onTabUpdate); chrome.tabs.onUpdated.addListener(onTabUpdate); + chrome.webNavigation.onErrorOccurred.addListener(onErrorOccuredListener); + chrome.webNavigation.onBeforeNavigate.addListener(onNavigatedListener); + chrome.webNavigation.onCompleted.addListener(onCompleted); return () => { chrome.tabs.onUpdated.removeListener(onTabUpdate); + chrome.webNavigation.onBeforeNavigate.removeListener(onNavigatedListener); + chrome.webNavigation.onCompleted.removeListener(onCompleted); }; - }, [onTabUpdate]); + }, [onTabUpdate, onErrorOccuredListener, onNavigatedListener, onCompleted]); return ( { loadedBefore, showLoader, tabId, + errorOccured, }, actions: { setLibraryMatches, @@ -140,6 +176,7 @@ export const LibraryDetectionProvider = ({ children }: PropsWithChildren) => { setIsInitialDataUpdated, setLoadedBeforeState, setShowLoader, + setErrorOccured, }, }} >