diff --git a/web/components/layouts/AdminLayout.tsx b/web/components/layouts/AdminLayout.tsx deleted file mode 100644 index c6f44cb85..000000000 --- a/web/components/layouts/AdminLayout.tsx +++ /dev/null @@ -1,37 +0,0 @@ -/* eslint-disable @next/next/no-css-tags */ -import { AppProps } from 'next/app'; -import { FC } from 'react'; -import ServerStatusProvider from '../../utils/server-status-context'; -import AlertMessageProvider from '../../utils/alert-message-context'; -import { MainLayout } from '../MainLayout'; - -/* -NOTE: A bunch of compiled css is loaded here for the Admin UI. -These are old stylesheets that were converted from sass and should not be -edited or maintained. Instead we are using css modules everywhere. So if you -need to change a style rewrite the css file as a css module and import it -into the component that needs it, removing it from this global list. -*/ -export const AdminLayout: FC = ({ Component, pageProps }) => ( - <> - - - - - - - - - - - - - - - - - - - - -); diff --git a/web/components/layouts/Main.tsx b/web/components/layouts/Main.tsx index 753e9c7da..9db67742b 100644 --- a/web/components/layouts/Main.tsx +++ b/web/components/layouts/Main.tsx @@ -5,23 +5,37 @@ import { useRecoilValue } from 'recoil'; import Head from 'next/head'; import { FC, useEffect, useRef } from 'react'; import { useLockBodyScroll } from 'react-use'; +import dynamic from 'next/dynamic'; import { ClientConfigStore, isChatAvailableSelector, clientConfigStateAtom, fatalErrorStateAtom, } from '../stores/ClientConfigStore'; -import { Content } from '../ui/Content/Content'; import { Header } from '../ui/Header/Header'; import { ClientConfig } from '../../interfaces/client-config.model'; import { DisplayableError } from '../../types/displayable-error'; -import { FatalErrorStateModal } from '../modals/FatalErrorStateModal/FatalErrorStateModal'; import setupNoLinkReferrer from '../../utils/no-link-referrer'; import { TitleNotifier } from '../TitleNotifier/TitleNotifier'; import { ServerRenderedHydration } from '../ServerRendered/ServerRenderedHydration'; +import { PushNotificationServiceWorker } from '../workers/PushNotificationServiceWorker/PushNotificationServiceWorker'; +import { Content } from '../ui/Content/Content'; import { Theme } from '../theme/Theme'; +// Lazy loaded components + +const FatalErrorStateModal = dynamic( + () => + import('../modals/FatalErrorStateModal/FatalErrorStateModal').then( + mod => mod.FatalErrorStateModal, + ), + { + loading: () =>
Loading...
, + ssr: false, + }, +); + export const Main: FC = () => { const clientConfig = useRecoilValue(clientConfigStateAtom); const { name, title, customStyles } = clientConfig; @@ -111,6 +125,7 @@ export const Main: FC = () => { )} + diff --git a/web/components/layouts/SimpleLayout.tsx b/web/components/layouts/SimpleLayout.tsx deleted file mode 100644 index 615ce04d5..000000000 --- a/web/components/layouts/SimpleLayout.tsx +++ /dev/null @@ -1,8 +0,0 @@ -import { AppProps } from 'next/app'; -import { FC } from 'react'; - -export const SimpleLayout: FC = ({ Component, pageProps }) => ( -
- -
-); diff --git a/web/components/workers/PushNotificationServiceWorker/.editorconfig b/web/components/workers/PushNotificationServiceWorker/.editorconfig new file mode 100644 index 000000000..c1322dc7b --- /dev/null +++ b/web/components/workers/PushNotificationServiceWorker/.editorconfig @@ -0,0 +1,12 @@ +# EditorConfig is awesome: https://EditorConfig.org + +# top-most EditorConfig file +root = true + +[*] +indent_style = space +indent_size = 4 +end_of_line = lf +charset = utf-8 +trim_trailing_whitespace = false +insert_final_newline = false \ No newline at end of file diff --git a/web/components/workers/PushNotificationServiceWorker/PushNotificationServiceWorker.tsx b/web/components/workers/PushNotificationServiceWorker/PushNotificationServiceWorker.tsx new file mode 100644 index 000000000..d950b242c --- /dev/null +++ b/web/components/workers/PushNotificationServiceWorker/PushNotificationServiceWorker.tsx @@ -0,0 +1,27 @@ +/* eslint-disable react/no-danger */ +import { FC, useEffect } from 'react'; + +export const PushNotificationServiceWorker: FC = () => { + const add = () => { + navigator.serviceWorker.register('/serviceWorker.js').then( + registration => { + console.debug('Service Worker registration successful with scope: ', registration.scope); + }, + err => { + console.error('Service Worker registration failed: ', err); + }, + ); + }; + + useEffect(() => { + if ('serviceWorker' in navigator) { + window.addEventListener('load', add); + } + + return () => { + window.removeEventListener('load', add); + }; + }, []); + + return null; +}; diff --git a/web/pages/_app.tsx b/web/pages/_app.tsx index 14c0c06ce..689bce56d 100644 --- a/web/pages/_app.tsx +++ b/web/pages/_app.tsx @@ -11,41 +11,25 @@ import '../styles/ant-overrides.scss'; import '../components/video/VideoJS/VideoJS.scss'; import { AppProps } from 'next/app'; -import { Router, useRouter } from 'next/router'; - +import { ReactElement, ReactNode } from 'react'; +import { NextPage } from 'next'; import { RecoilRoot } from 'recoil'; -import { useEffect } from 'react'; -import { AdminLayout } from '../components/layouts/AdminLayout'; -import { SimpleLayout } from '../components/layouts/SimpleLayout'; -const App = ({ Component, pageProps }: AppProps) => { - useEffect(() => { - if ('serviceWorker' in navigator) { - window.addEventListener('load', () => { - navigator.serviceWorker.register('/serviceWorker.js').then( - registration => { - console.debug( - 'Service Worker registration successful with scope: ', - registration.scope, - ); - }, - err => { - console.error('Service Worker registration failed: ', err); - }, - ); - }); - } - }, []); - - const router = useRouter() as Router; - if (router.pathname.startsWith('/admin')) { - return ; - } - return ( - - - - ); +export type NextPageWithLayout

= NextPage & { + getLayout?: (page: ReactElement) => ReactNode; }; -export default App; +type AppPropsWithLayout = AppProps & { + Component: NextPageWithLayout; +}; + +export default function App({ Component, pageProps }: AppPropsWithLayout) { + // Use the layout defined at the page level, if available + const getLayout = Component.getLayout ?? (page => page); + + return getLayout( + + + , + ); +} diff --git a/web/pages/admin/index.tsx b/web/pages/admin/index.tsx index 759af68f1..413f7402b 100644 --- a/web/pages/admin/index.tsx +++ b/web/pages/admin/index.tsx @@ -1,8 +1,9 @@ -import React, { useState, useEffect, useContext } from 'react'; +/* eslint-disable @next/next/no-css-tags */ +import React, { useState, useEffect, useContext, ReactElement } from 'react'; import { Skeleton, Card, Statistic, Row, Col } from 'antd'; import { UserOutlined, ClockCircleOutlined } from '@ant-design/icons'; import { formatDistanceToNow, formatRelative } from 'date-fns'; -import { ServerStatusContext } from '../../utils/server-status-context'; +import ServerStatusProvider, { ServerStatusContext } from '../../utils/server-status-context'; import { LogTable } from '../../components/LogTable'; import { Offline } from '../../components/Offline'; import { StreamHealthOverview } from '../../components/StreamHealthOverview'; @@ -11,6 +12,9 @@ import { LOGS_WARN, fetchData, FETCH_INTERVAL } from '../../utils/apis'; import { formatIPAddress, isEmptyObject } from '../../utils/format'; import { NewsFeed } from '../../components/NewsFeed'; +import AlertMessageProvider from '../../utils/alert-message-context'; +import { MainLayout } from '../../components/MainLayout'; + function streamDetailsFormatter(streamDetails) { return (

    @@ -178,3 +182,27 @@ export default function Home() { ); } + +Home.getLayout = function getLayout(page: ReactElement) { + return ( + <> + + + + + + + + + + + + + + + {page} + + + + ); +}; diff --git a/web/pages/index.tsx b/web/pages/index.tsx index 33a1e74c5..253c0ddcd 100644 --- a/web/pages/index.tsx +++ b/web/pages/index.tsx @@ -1,5 +1,9 @@ +import { ReactElement } from 'react'; import { Main } from '../components/layouts/Main'; export default function Home() { return
    ; } +Home.getLayout = function getLayout(page: ReactElement) { + return page; +};