|
| 1 | +/* eslint-disable max-len */ |
| 2 | +import React, { useState, useEffect, useRef } from 'react'; |
| 3 | +import { launchContentScript } from '../slices/mainSlice'; |
| 4 | +import Loader from '../components/Loader'; |
| 5 | +import ErrorMsg from '../components/ErrorMsg'; |
| 6 | +import { useDispatch, useSelector } from 'react-redux'; |
| 7 | +import { MainState, RootState, ErrorContainerProps } from '../FrontendTypes'; |
| 8 | +import { current } from '@reduxjs/toolkit'; |
| 9 | +/* |
| 10 | +This is the loading screen that a user may get when first initalizing the application. This page checks: |
| 11 | +
|
| 12 | + 1. if the content script has been launched on the current tab |
| 13 | + 2. if React Dev Tools has been installed |
| 14 | + 3. if target tab contains a compatible React app |
| 15 | +*/ |
| 16 | + |
| 17 | +function ErrorContainer(props: ErrorContainerProps): JSX.Element { |
| 18 | + const dispatch = useDispatch(); |
| 19 | + const { tabs, currentTitle, currentTab }: MainState = useSelector( |
| 20 | + (state: RootState) => state.main, |
| 21 | + ); |
| 22 | + const [loadingArray, setLoading] = useState([true, true, true]); // We create a local state "loadingArray" and set it to an array with three true elements. These will be used as hooks for error checking against a 'status' object that is declared later in a few lines. 'loadingArray' is used later in the return statement to display a spinning loader icon if it's true. If it's false, either a checkmark icon or an exclamation icon will be displayed to the user. |
| 23 | + const titleTracker = useRef(currentTitle); // useRef returns an object with a property 'initialValue' and a value of whatever was passed in. This allows us to reference a value that's not needed for rendering |
| 24 | + const timeout = useRef(null); |
| 25 | + const { port } = props; |
| 26 | + console.log( |
| 27 | + 'ErrorContainer state variables: tabs status: ', |
| 28 | + JSON.stringify(tabs[currentTab]?.status), |
| 29 | + 'currentTab: ', |
| 30 | + currentTab, |
| 31 | + 'currentTitle: ', |
| 32 | + currentTitle, |
| 33 | + ); |
| 34 | + // function that launches the main app |
| 35 | + function launch(): void { |
| 36 | + dispatch(launchContentScript(tabs[currentTab])); |
| 37 | + } |
| 38 | + |
| 39 | + let status = { |
| 40 | + // We create a status object that we may use later if tabs[currentTab] exists |
| 41 | + contentScriptLaunched: false, |
| 42 | + reactDevToolsInstalled: false, |
| 43 | + targetPageisaReactApp: false, |
| 44 | + }; |
| 45 | + |
| 46 | + if (tabs[currentTab]) { |
| 47 | + // If we do have a tabs[currentTab] object, we replace the status obj we declared above with the properties of the tabs[currentTab].status |
| 48 | + Object.assign(status, tabs[currentTab].status); |
| 49 | + } |
| 50 | + |
| 51 | + // hook that sets timer while waiting for a snapshot from the background script, resets if the tab changes/reloads |
| 52 | + useEffect(() => { |
| 53 | + if (tabs[currentTab]) |
| 54 | + console.log('ErrorContainer useEffect fired, ', JSON.stringify(tabs[currentTab]?.status)); |
| 55 | + // We declare a function |
| 56 | + function setLoadingArray(i: number, value: boolean) { |
| 57 | + // 'setLoadingArray' checks an element in our 'loadingArray' local state and compares it with passed in boolean argument. If they don't match, we update our local state replacing the selected element with the boolean argument |
| 58 | + if (loadingArray[i] !== value) { |
| 59 | + // this conditional helps us avoid unecessary state changes if the element and the value are already the same |
| 60 | + const loadingArrayClone = [...loadingArray]; |
| 61 | + loadingArrayClone[i] = value; |
| 62 | + setLoading(loadingArrayClone); |
| 63 | + } |
| 64 | + } |
| 65 | + |
| 66 | + if (titleTracker.current !== currentTitle) { |
| 67 | + // if the current tab changes/reloads, we reset loadingArray to it's default [true, true, true] |
| 68 | + titleTracker.current = currentTitle; |
| 69 | + setLoadingArray(0, true); |
| 70 | + setLoadingArray(1, true); |
| 71 | + setLoadingArray(2, true); |
| 72 | + |
| 73 | + if (timeout.current) { |
| 74 | + // if there is a current timeout set, we clear it |
| 75 | + clearTimeout(timeout.current); |
| 76 | + timeout.current = null; |
| 77 | + } |
| 78 | + } |
| 79 | + |
| 80 | + if (!status.contentScriptLaunched) { |
| 81 | + // if content script hasnt been launched/found, set a timer or immediately update 'loadingArray' state |
| 82 | + |
| 83 | + if (loadingArray[0] === true) { |
| 84 | + // if loadingArray[0] is true, then that means our timeout.current is still null so we now set it to a setTimeout function that will flip loadingArray[0] to false after 3 seconds |
| 85 | + timeout.current = setTimeout(() => { |
| 86 | + setLoadingArray(0, false); |
| 87 | + }, 3000); // increased from 1500 |
| 88 | + } |
| 89 | + } else { |
| 90 | + setLoadingArray(0, false); // if status.contentScriptLaunched is true, that means timeout.current !== null. This means that useEffect was triggered previously. |
| 91 | + } |
| 92 | + |
| 93 | + // The next two if statements are written in a way to allow the checking of 'content script hook', 'reactDevTools check', and 'target page is a react app' to be run in chronological order. |
| 94 | + if (loadingArray[0] === false && status.contentScriptLaunched === true) { |
| 95 | + timeout.current = setTimeout(() => { |
| 96 | + setLoadingArray(1, false); |
| 97 | + }, 3000); // increased from 1500 |
| 98 | + // let devTools; |
| 99 | + // while (!devTools) { |
| 100 | + // devTools = window.__REACT_DEVTOOLS_GLOBAL_HOOK__; |
| 101 | + // } |
| 102 | + // console.log('ErrorContainer react devtools: ', devTools); |
| 103 | + // // If React Devtools is not installed, object will be undefined. |
| 104 | + // if (!devTools) return; |
| 105 | + // // If React Devtools is installed, send a message to front end. |
| 106 | + |
| 107 | + // console.log('ErrorContainer react devtools check passed'); |
| 108 | + // status.reactDevToolsInstalled = true; |
| 109 | + |
| 110 | + // console.log('ErrorContainer attempting reinitalize'); |
| 111 | + // port.postMessage({ |
| 112 | + // action: 'reinitialize', |
| 113 | + // tabId: currentTab, |
| 114 | + // }); |
| 115 | + setLoadingArray(1, false); |
| 116 | + } |
| 117 | + if (loadingArray[1] === false && status.reactDevToolsInstalled === true) { |
| 118 | + setLoadingArray(2, false); |
| 119 | + } |
| 120 | + |
| 121 | + // Unload async function when Error Container is unmounted |
| 122 | + return () => { |
| 123 | + clearTimeout(timeout.current); |
| 124 | + }; |
| 125 | + }, [status, currentTitle, timeout, loadingArray]); // within our dependency array, we're keeping track of if the status, currentTitle/tab, timeout, or loadingArray changes and we re-run the useEffect hook if they do |
| 126 | + |
| 127 | + return ( |
| 128 | + <div className='error-container'> |
| 129 | + <img src='../assets/whiteBlackSquareLogo.png' alt='Reactime Logo' height='50px' /> |
| 130 | + |
| 131 | + <h2>Launching Reactime on tab: {currentTitle}</h2> |
| 132 | + |
| 133 | + <div className='loaderChecks'> |
| 134 | + <p>Checking if content script has been launched on current tab</p> |
| 135 | + <Loader loading={loadingArray[0]} result={status.contentScriptLaunched} /> |
| 136 | + |
| 137 | + <p>Checking if React Dev Tools has been installed</p> |
| 138 | + <Loader loading={loadingArray[1]} result={status.reactDevToolsInstalled} /> |
| 139 | + |
| 140 | + <p>Checking if target is a compatible React app</p> |
| 141 | + <Loader loading={loadingArray[2]} result={status.targetPageisaReactApp} /> |
| 142 | + </div> |
| 143 | + |
| 144 | + <br /> |
| 145 | + <div className='errorMsg'> |
| 146 | + <ErrorMsg loadingArray={loadingArray} status={status} launchContent={launch} /> |
| 147 | + </div> |
| 148 | + <br /> |
| 149 | + <a |
| 150 | + href='https://github.com/open-source-labs/reactime' |
| 151 | + target='_blank' |
| 152 | + rel='noopener noreferrer' |
| 153 | + > |
| 154 | + Please visit the Reactime Github for more info. |
| 155 | + </a> |
| 156 | + </div> |
| 157 | + ); |
| 158 | +} |
| 159 | + |
| 160 | +export default ErrorContainer; |
0 commit comments