mirror of
				https://github.com/owncast/owncast.git
				synced 2025-11-01 02:44:31 +08:00 
			
		
		
		
	Use built-in Next layout support + lazy load
Instead of doing manual layout switching use the Nextjs nested layout support. Also add some additional lazy loading of components. This is to work on performance score re: #2167.
This commit is contained in:
		| @ -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<AppProps> = ({ Component, pageProps }) => ( | ||||
|   <> | ||||
|     <link rel="stylesheet" href="/styles/admin/main-layout.css" /> | ||||
|     <link rel="stylesheet" href="/styles/admin/form-textfields.css" /> | ||||
|     <link rel="stylesheet" href="/styles/admin/config-socialhandles.css" /> | ||||
|     <link rel="stylesheet" href="/styles/admin/config-storage.css" /> | ||||
|     <link rel="stylesheet" href="/styles/admin/config-edit-string-tags.css" /> | ||||
|     <link rel="stylesheet" href="/styles/admin/config-video-variants.css" /> | ||||
|     <link rel="stylesheet" href="/styles/admin/config-public-details.css" /> | ||||
|     <link rel="stylesheet" href="/styles/admin/home.css" /> | ||||
|     <link rel="stylesheet" href="/styles/admin/chat.css" /> | ||||
|     <link rel="stylesheet" href="/styles/admin/pages.css" /> | ||||
|     <link rel="stylesheet" href="/styles/admin/offline-notice.css" /> | ||||
|  | ||||
|     <ServerStatusProvider> | ||||
|       <AlertMessageProvider> | ||||
|         <MainLayout> | ||||
|           <Component {...pageProps} /> | ||||
|         </MainLayout> | ||||
|       </AlertMessageProvider> | ||||
|     </ServerStatusProvider> | ||||
|   </> | ||||
| ); | ||||
| @ -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: () => <div>Loading...</div>, | ||||
|     ssr: false, | ||||
|   }, | ||||
| ); | ||||
|  | ||||
| export const Main: FC = () => { | ||||
|   const clientConfig = useRecoilValue<ClientConfig>(clientConfigStateAtom); | ||||
|   const { name, title, customStyles } = clientConfig; | ||||
| @ -111,6 +125,7 @@ export const Main: FC = () => { | ||||
|       )} | ||||
|  | ||||
|       <ClientConfigStore /> | ||||
|       <PushNotificationServiceWorker /> | ||||
|       <TitleNotifier name={name} /> | ||||
|       <Theme /> | ||||
|       <Layout ref={layoutRef} style={{ minHeight: '100vh' }}> | ||||
|  | ||||
| @ -1,8 +0,0 @@ | ||||
| import { AppProps } from 'next/app'; | ||||
| import { FC } from 'react'; | ||||
|  | ||||
| export const SimpleLayout: FC<AppProps> = ({ Component, pageProps }) => ( | ||||
|   <div> | ||||
|     <Component {...pageProps} /> | ||||
|   </div> | ||||
| ); | ||||
| @ -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 | ||||
| @ -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; | ||||
| }; | ||||
| @ -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 <AdminLayout pageProps={pageProps} Component={Component} router={router} />; | ||||
|   } | ||||
|   return ( | ||||
|     <RecoilRoot> | ||||
|       <SimpleLayout pageProps={pageProps} Component={Component} router={router} /> | ||||
|     </RecoilRoot> | ||||
|   ); | ||||
| export type NextPageWithLayout<P = {}, IP = P> = NextPage<P, IP> & { | ||||
|   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( | ||||
|     <RecoilRoot> | ||||
|       <Component {...pageProps} /> | ||||
|     </RecoilRoot>, | ||||
|   ); | ||||
| } | ||||
|  | ||||
| @ -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 ( | ||||
|     <ul className="statistics-list"> | ||||
| @ -178,3 +182,27 @@ export default function Home() { | ||||
|     </div> | ||||
|   ); | ||||
| } | ||||
|  | ||||
| Home.getLayout = function getLayout(page: ReactElement) { | ||||
|   return ( | ||||
|     <> | ||||
|       <link rel="stylesheet" href="/styles/admin/main-layout.css" /> | ||||
|       <link rel="stylesheet" href="/styles/admin/form-textfields.css" /> | ||||
|       <link rel="stylesheet" href="/styles/admin/config-socialhandles.css" /> | ||||
|       <link rel="stylesheet" href="/styles/admin/config-storage.css" /> | ||||
|       <link rel="stylesheet" href="/styles/admin/config-edit-string-tags.css" /> | ||||
|       <link rel="stylesheet" href="/styles/admin/config-video-variants.css" /> | ||||
|       <link rel="stylesheet" href="/styles/admin/config-public-details.css" /> | ||||
|       <link rel="stylesheet" href="/styles/admin/home.css" /> | ||||
|       <link rel="stylesheet" href="/styles/admin/chat.css" /> | ||||
|       <link rel="stylesheet" href="/styles/admin/pages.css" /> | ||||
|       <link rel="stylesheet" href="/styles/admin/offline-notice.css" /> | ||||
|  | ||||
|       <ServerStatusProvider> | ||||
|         <AlertMessageProvider> | ||||
|           <MainLayout>{page}</MainLayout> | ||||
|         </AlertMessageProvider> | ||||
|       </ServerStatusProvider> | ||||
|     </> | ||||
|   ); | ||||
| }; | ||||
|  | ||||
| @ -1,5 +1,9 @@ | ||||
| import { ReactElement } from 'react'; | ||||
| import { Main } from '../components/layouts/Main'; | ||||
|  | ||||
| export default function Home() { | ||||
|   return <Main />; | ||||
| } | ||||
| Home.getLayout = function getLayout(page: ReactElement) { | ||||
|   return page; | ||||
| }; | ||||
|  | ||||
		Reference in New Issue
	
	Block a user
	 Gabe Kangas
					Gabe Kangas