mirror of
https://github.com/owncast/owncast.git
synced 2024-11-21 20:28:15 +03: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:
parent
15747f86dd
commit
c05a20a460
8 changed files with 108 additions and 83 deletions
|
@ -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 Head from 'next/head';
|
||||||
import { FC, useEffect, useRef } from 'react';
|
import { FC, useEffect, useRef } from 'react';
|
||||||
import { useLockBodyScroll } from 'react-use';
|
import { useLockBodyScroll } from 'react-use';
|
||||||
|
import dynamic from 'next/dynamic';
|
||||||
import {
|
import {
|
||||||
ClientConfigStore,
|
ClientConfigStore,
|
||||||
isChatAvailableSelector,
|
isChatAvailableSelector,
|
||||||
clientConfigStateAtom,
|
clientConfigStateAtom,
|
||||||
fatalErrorStateAtom,
|
fatalErrorStateAtom,
|
||||||
} from '../stores/ClientConfigStore';
|
} from '../stores/ClientConfigStore';
|
||||||
import { Content } from '../ui/Content/Content';
|
|
||||||
import { Header } from '../ui/Header/Header';
|
import { Header } from '../ui/Header/Header';
|
||||||
import { ClientConfig } from '../../interfaces/client-config.model';
|
import { ClientConfig } from '../../interfaces/client-config.model';
|
||||||
import { DisplayableError } from '../../types/displayable-error';
|
import { DisplayableError } from '../../types/displayable-error';
|
||||||
import { FatalErrorStateModal } from '../modals/FatalErrorStateModal/FatalErrorStateModal';
|
|
||||||
import setupNoLinkReferrer from '../../utils/no-link-referrer';
|
import setupNoLinkReferrer from '../../utils/no-link-referrer';
|
||||||
import { TitleNotifier } from '../TitleNotifier/TitleNotifier';
|
import { TitleNotifier } from '../TitleNotifier/TitleNotifier';
|
||||||
import { ServerRenderedHydration } from '../ServerRendered/ServerRenderedHydration';
|
import { ServerRenderedHydration } from '../ServerRendered/ServerRenderedHydration';
|
||||||
|
import { PushNotificationServiceWorker } from '../workers/PushNotificationServiceWorker/PushNotificationServiceWorker';
|
||||||
|
import { Content } from '../ui/Content/Content';
|
||||||
|
|
||||||
import { Theme } from '../theme/Theme';
|
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 = () => {
|
export const Main: FC = () => {
|
||||||
const clientConfig = useRecoilValue<ClientConfig>(clientConfigStateAtom);
|
const clientConfig = useRecoilValue<ClientConfig>(clientConfigStateAtom);
|
||||||
const { name, title, customStyles } = clientConfig;
|
const { name, title, customStyles } = clientConfig;
|
||||||
|
@ -111,6 +125,7 @@ export const Main: FC = () => {
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<ClientConfigStore />
|
<ClientConfigStore />
|
||||||
|
<PushNotificationServiceWorker />
|
||||||
<TitleNotifier name={name} />
|
<TitleNotifier name={name} />
|
||||||
<Theme />
|
<Theme />
|
||||||
<Layout ref={layoutRef} style={{ minHeight: '100vh' }}>
|
<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 '../components/video/VideoJS/VideoJS.scss';
|
||||||
|
|
||||||
import { AppProps } from 'next/app';
|
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 { RecoilRoot } from 'recoil';
|
||||||
import { useEffect } from 'react';
|
|
||||||
import { AdminLayout } from '../components/layouts/AdminLayout';
|
|
||||||
import { SimpleLayout } from '../components/layouts/SimpleLayout';
|
|
||||||
|
|
||||||
const App = ({ Component, pageProps }: AppProps) => {
|
export type NextPageWithLayout<P = {}, IP = P> = NextPage<P, IP> & {
|
||||||
useEffect(() => {
|
getLayout?: (page: ReactElement) => ReactNode;
|
||||||
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 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 { Skeleton, Card, Statistic, Row, Col } from 'antd';
|
||||||
import { UserOutlined, ClockCircleOutlined } from '@ant-design/icons';
|
import { UserOutlined, ClockCircleOutlined } from '@ant-design/icons';
|
||||||
import { formatDistanceToNow, formatRelative } from 'date-fns';
|
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 { LogTable } from '../../components/LogTable';
|
||||||
import { Offline } from '../../components/Offline';
|
import { Offline } from '../../components/Offline';
|
||||||
import { StreamHealthOverview } from '../../components/StreamHealthOverview';
|
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 { formatIPAddress, isEmptyObject } from '../../utils/format';
|
||||||
import { NewsFeed } from '../../components/NewsFeed';
|
import { NewsFeed } from '../../components/NewsFeed';
|
||||||
|
|
||||||
|
import AlertMessageProvider from '../../utils/alert-message-context';
|
||||||
|
import { MainLayout } from '../../components/MainLayout';
|
||||||
|
|
||||||
function streamDetailsFormatter(streamDetails) {
|
function streamDetailsFormatter(streamDetails) {
|
||||||
return (
|
return (
|
||||||
<ul className="statistics-list">
|
<ul className="statistics-list">
|
||||||
|
@ -178,3 +182,27 @@ export default function Home() {
|
||||||
</div>
|
</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';
|
import { Main } from '../components/layouts/Main';
|
||||||
|
|
||||||
export default function Home() {
|
export default function Home() {
|
||||||
return <Main />;
|
return <Main />;
|
||||||
}
|
}
|
||||||
|
Home.getLayout = function getLayout(page: ReactElement) {
|
||||||
|
return page;
|
||||||
|
};
|
||||||
|
|
Loading…
Reference in a new issue