All files / client/src/app App.tsx

77.77% Statements 21/27
44.44% Branches 4/9
100% Functions 9/9
77.77% Lines 21/27

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117                                        356x   356x 714x   714x 357x 358x 358x     357x     357x 1x 1x             357x       357x   357x   357x               714x                                                           1x                 357x         1x 357x         357x              
import { ReactElement, useEffect, useState } from "react";
import { BrowserRouter } from "react-router";
import styled from "styled-components";
import { AppRoutes } from "client/app/AppRoutes";
import { Header } from "client/components/Header";
import { loadData } from "client/utils/loadData";
import { Loading } from "client/components/Loading";
import { getIconLibrary } from "client/utils/icons";
import { config } from "shared/config";
import { ErrorBar } from "client/components/ErrorBar";
import { MOBILE_MARGIN } from "client/globalStyle";
import { TestTime } from "client/test/test-components/TestTime";
import { TestGenerateSerial } from "client/test/test-components/TestGenerateSerial";
import { Announcement } from "client/components/Announcement";
import { AdminMessageBanner } from "client/components/AdminMessageBanner";
import { NotificationBar } from "client/views/event-log/NotificationBar";
import { resetNetworkError } from "client/views/admin/adminUtils";
import { HistoryProvider } from "client/app/HistoryContext";
 
const { loadedSettings, showTestValues, showAnnouncement, dataUpdateInterval } =
  config.client();
 
const App = (): ReactElement => {
  const [loading, setLoading] = useState<boolean>(true);
 
  useEffect(() => {
    const fetchData = async (): Promise<void> => {
      await loadData();
      setLoading(false);
    };
    // eslint-disable-next-line @typescript-eslint/no-floating-promises
    fetchData();
 
    // eslint-disable-next-line @typescript-eslint/no-misused-promises
    const updateTimer = setInterval(async () => {
      resetNetworkError();
      await fetchData();
    }, dataUpdateInterval * 1000);
 
    // The network error toast can appear right before the laptop sleeps
    // (Wi-Fi drops before JS is suspended), so clear it when connectivity is
    // lost and refresh immediately when it returns instead of leaving the
    // toast visible until the next poll cycle
    const handleOnline = async (): Promise<void> => {
      resetNetworkError();
      await fetchData();
    };
    addEventListener("offline", resetNetworkError);
    // eslint-disable-next-line @typescript-eslint/no-misused-promises
    addEventListener("online", handleOnline);
 
    return () => {
      clearInterval(updateTimer);
      removeEventListener("offline", resetNetworkError);
      // eslint-disable-next-line @typescript-eslint/no-misused-promises
      removeEventListener("online", handleOnline);
    };
  }, []);
 
  getIconLibrary();
 
  return (
    <>
      {loading && <Loading />}
 
      {!loading && (
        <BrowserRouter>
          <HistoryProvider>
            {loadedSettings !== "production" && showTestValues && (
              <TestValueContainer>
                <TestTime />
                <TestGenerateSerial />
              </TestValueContainer>
            )}
            <Header />
            <ErrorBar />
            <AdminMessageBanner />
            <NotificationBar />
            {showAnnouncement && <Announcement />}
            <AppContainer>
              <AppRoutes />
            </AppContainer>
          </HistoryProvider>
        </BrowserRouter>
      )}
    </>
  );
};
 
const TestValueContainer = styled.div`
  position: fixed;
  top: 0;
  left: 0;
  z-index: 1000;
 
  /* Don't block clicks on the header underneath; children opt back in */
  pointer-events: none;
 
  @media (max-width: ${(props) => props.theme.breakpointDesktop}) {
    display: none;
  }
`;
 
const AppContainer = styled.div`
  @media (max-width: ${(props) => props.theme.breakpointPhone}) {
    margin-left: ${MOBILE_MARGIN}px;
    margin-right: ${MOBILE_MARGIN}px;
  }
 
  @media (max-width: ${(props) => props.theme.breakpointDesktop}) {
    margin-left: ${MOBILE_MARGIN}px;
    margin-right: ${MOBILE_MARGIN}px;
  }
`;
 
export default App;