From 44678a61c2b0cbd2b48775619ac080f340559481 Mon Sep 17 00:00:00 2001 From: Bill Thornton Date: Wed, 27 Sep 2023 02:07:40 -0400 Subject: [PATCH 1/2] Refactor app layouts and common components --- src/RootApp.tsx | 54 ++++--- src/apps/experimental/App.tsx | 10 +- src/apps/experimental/AppLayout.tsx | 117 +++++++-------- .../components/AppToolbar/index.tsx | 138 ++++-------------- .../components/drawers/AppDrawer.tsx | 28 +++- .../components/drawers/MainDrawerContent.tsx | 4 +- .../dashboard/AdvancedDrawerSection.tsx | 3 +- .../dashboard/DevicesDrawerSection.tsx | 3 +- .../drawers/dashboard/LiveTvDrawerSection.tsx | 3 +- .../drawers/dashboard/PluginDrawerSection.tsx | 3 +- .../drawers/dashboard/ServerDrawerSection.tsx | 3 +- src/apps/stable/App.tsx | 21 +-- src/components/AppBody.tsx | 24 +++ src/components/AppHeader.tsx | 18 ++- .../components/ElevationScroll.tsx | 0 .../drawers => components}/ListItemLink.tsx | 0 .../ResponsiveDrawer.tsx | 12 +- src/components/router/AsyncRoute.tsx | 40 +++-- src/components/toolbar/AppToolbar.tsx | 129 ++++++++++++++++ .../toolbar}/AppUserMenu.tsx | 0 .../toolbar}/UserMenuButton.tsx | 4 +- src/{apps/experimental => themes}/theme.ts | 1 + 22 files changed, 353 insertions(+), 262 deletions(-) create mode 100644 src/components/AppBody.tsx rename src/{apps/experimental => }/components/ElevationScroll.tsx (100%) rename src/{apps/experimental/components/drawers => components}/ListItemLink.tsx (100%) rename src/{apps/experimental/components/drawers => components}/ResponsiveDrawer.tsx (87%) create mode 100644 src/components/toolbar/AppToolbar.tsx rename src/{apps/experimental/components/AppToolbar/menus => components/toolbar}/AppUserMenu.tsx (100%) rename src/{apps/experimental/components/AppToolbar => components/toolbar}/UserMenuButton.tsx (96%) rename src/{apps/experimental => themes}/theme.ts (96%) diff --git a/src/RootApp.tsx b/src/RootApp.tsx index 62223f7236..5ee5ffab5c 100644 --- a/src/RootApp.tsx +++ b/src/RootApp.tsx @@ -1,37 +1,53 @@ import loadable from '@loadable/component'; +import { ThemeProvider } from '@mui/material/styles'; import { History } from '@remix-run/router'; import React from 'react'; import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; import { ReactQueryDevtools } from '@tanstack/react-query-devtools'; -import StableApp from './apps/stable/App'; -import { HistoryRouter } from './components/router/HistoryRouter'; -import { ApiProvider } from './hooks/useApi'; -import { WebConfigProvider } from './hooks/useWebConfig'; +import StableApp from 'apps/stable/App'; +import AppHeader from 'components/AppHeader'; +import Backdrop from 'components/Backdrop'; +import { HistoryRouter } from 'components/router/HistoryRouter'; +import { ApiProvider } from 'hooks/useApi'; +import { WebConfigProvider } from 'hooks/useWebConfig'; +import theme from 'themes/theme'; const ExperimentalApp = loadable(() => import('./apps/experimental/App')); const queryClient = new QueryClient(); -const RootApp = ({ history }: { history: History }) => { +const RootAppLayout = () => { const layoutMode = localStorage.getItem('layout'); + const isExperimentalLayout = layoutMode === 'experimental'; return ( - - - - - { - layoutMode === 'experimental' ? - : - - } - - - - - + <> + + + + { + isExperimentalLayout ? + : + + } + ); }; +const RootApp = ({ history }: { history: History }) => ( + + + + + + + + + + + + +); + export default RootApp; diff --git a/src/apps/experimental/App.tsx b/src/apps/experimental/App.tsx index 57ad22eae7..44c6d24b2d 100644 --- a/src/apps/experimental/App.tsx +++ b/src/apps/experimental/App.tsx @@ -1,16 +1,16 @@ import React from 'react'; import { Navigate, Route, Routes } from 'react-router-dom'; +import { REDIRECTS } from 'apps/stable/routes/_redirects'; import ConnectionRequired from 'components/ConnectionRequired'; import ServerContentPage from 'components/ServerContentPage'; import { toAsyncPageRoute } from 'components/router/AsyncRoute'; import { toViewManagerPageRoute } from 'components/router/LegacyRoute'; +import { toRedirectRoute } from 'components/router/Redirect'; import AppLayout from './AppLayout'; import { ASYNC_ADMIN_ROUTES, ASYNC_USER_ROUTES } from './routes/asyncRoutes'; import { LEGACY_ADMIN_ROUTES, LEGACY_PUBLIC_ROUTES, LEGACY_USER_ROUTES } from './routes/legacyRoutes'; -import { REDIRECTS } from 'apps/stable/routes/_redirects'; -import { toRedirectRoute } from 'components/router/Redirect'; const ExperimentalApp = () => { return ( @@ -38,10 +38,10 @@ const ExperimentalApp = () => { {LEGACY_PUBLIC_ROUTES.map(toViewManagerPageRoute)} - - {/* Redirects for old paths */} - {REDIRECTS.map(toRedirectRoute)} + + {/* Redirects for old paths */} + {REDIRECTS.map(toRedirectRoute)} ); }; diff --git a/src/apps/experimental/AppLayout.tsx b/src/apps/experimental/AppLayout.tsx index 71824e0d73..1f4860637c 100644 --- a/src/apps/experimental/AppLayout.tsx +++ b/src/apps/experimental/AppLayout.tsx @@ -1,18 +1,17 @@ import React, { useCallback, useEffect, useState } from 'react'; import AppBar from '@mui/material/AppBar'; import Box from '@mui/material/Box'; -import { ThemeProvider } from '@mui/material/styles'; +import { useTheme } from '@mui/material/styles'; import { Outlet, useLocation } from 'react-router-dom'; -import AppHeader from 'components/AppHeader'; -import Backdrop from 'components/Backdrop'; +import AppBody from 'components/AppBody'; +import ElevationScroll from 'components/ElevationScroll'; +import { DRAWER_WIDTH } from 'components/ResponsiveDrawer'; import { useApi } from 'hooks/useApi'; import { useLocalStorage } from 'hooks/useLocalStorage'; import AppToolbar from './components/AppToolbar'; -import AppDrawer, { DRAWER_WIDTH, isDrawerPath } from './components/drawers/AppDrawer'; -import ElevationScroll from './components/ElevationScroll'; -import theme from './theme'; +import AppDrawer, { isDrawerPath } from './components/drawers/AppDrawer'; import './AppOverrides.scss'; @@ -29,6 +28,7 @@ const AppLayout = () => { const [ isDrawerActive, setIsDrawerActive ] = useState(appSettings.isDrawerPinned); const { user } = useApi(); const location = useLocation(); + const theme = useTheme(); const isDrawerAvailable = isDrawerPath(location.pathname); const isDrawerOpen = isDrawerActive && isDrawerAvailable && Boolean(user); @@ -47,67 +47,54 @@ const AppLayout = () => { }, [ isDrawerActive, setIsDrawerActive ]); return ( - - - -
- {/* - * TODO: These components are not used, but views interact with them directly so the need to be - * present in the dom. We add them in a hidden element to prevent errors. - */} - -
- - - - muiTheme.zIndex.drawer + 1 }} - > - - - - - - - + + muiTheme.zIndex.drawer + 1 }} > -
-
- -
- + + + + + + + + + + - + ); }; diff --git a/src/apps/experimental/components/AppToolbar/index.tsx b/src/apps/experimental/components/AppToolbar/index.tsx index 0317ae92a3..ed447ae694 100644 --- a/src/apps/experimental/components/AppToolbar/index.tsx +++ b/src/apps/experimental/components/AppToolbar/index.tsx @@ -1,22 +1,14 @@ -import ArrowBack from '@mui/icons-material/ArrowBack'; -import MenuIcon from '@mui/icons-material/Menu'; import SearchIcon from '@mui/icons-material/Search'; -import Box from '@mui/material/Box'; import IconButton from '@mui/material/IconButton'; -import Toolbar from '@mui/material/Toolbar'; import Tooltip from '@mui/material/Tooltip'; -import Typography from '@mui/material/Typography'; import React, { FC } from 'react'; import { Link, useLocation } from 'react-router-dom'; -import appIcon from 'assets/img/icon-transparent.png'; -import { appRouter } from 'components/router/appRouter'; -import { useApi } from 'hooks/useApi'; +import AppToolbar from 'components/toolbar/AppToolbar'; import globalize from 'scripts/globalize'; import AppTabs from '../tabs/AppTabs'; import { isDrawerPath } from '../drawers/AppDrawer'; -import UserMenuButton from './UserMenuButton'; import RemotePlayButton from './RemotePlayButton'; import SyncPlayButton from './SyncPlayButton'; @@ -25,120 +17,40 @@ interface AppToolbarProps { onDrawerButtonClick: (event: React.MouseEvent) => void } -const onBackButtonClick = () => { - appRouter.back() - .catch(err => { - console.error('[AppToolbar] error calling appRouter.back', err); - }); -}; - -const AppToolbar: FC = ({ +const ExperimentalAppToolbar: FC = ({ isDrawerOpen, onDrawerButtonClick }) => { - const { user } = useApi(); - const isUserLoggedIn = Boolean(user); const location = useLocation(); - const isDrawerAvailable = isDrawerPath(location.pathname); - const isBackButtonAvailable = appRouter.canGoBack(); return ( - - {isUserLoggedIn && isDrawerAvailable && ( - - - - - - )} - - {isBackButtonAvailable && ( - - - - - - )} - - - - - Jellyfin - - - - - - {isUserLoggedIn && ( + - - - + + - - - - - - - - - - + + + + + - )} - + } + isDrawerAvailable={isDrawerAvailable} + isDrawerOpen={isDrawerOpen} + onDrawerButtonClick={onDrawerButtonClick} + > + + ); }; -export default AppToolbar; +export default ExperimentalAppToolbar; diff --git a/src/apps/experimental/components/drawers/AppDrawer.tsx b/src/apps/experimental/components/drawers/AppDrawer.tsx index ffd9ba119e..c414e6ba78 100644 --- a/src/apps/experimental/components/drawers/AppDrawer.tsx +++ b/src/apps/experimental/components/drawers/AppDrawer.tsx @@ -1,5 +1,7 @@ import React, { FC } from 'react'; -import { Route, Routes } from 'react-router-dom'; +import { Route, Routes, useLocation } from 'react-router-dom'; + +import ResponsiveDrawer, { ResponsiveDrawerProps } from 'components/ResponsiveDrawer'; import { ASYNC_ADMIN_ROUTES, ASYNC_USER_ROUTES } from '../../routes/asyncRoutes'; import { LEGACY_ADMIN_ROUTES, LEGACY_USER_ROUTES } from '../../routes/legacyRoutes'; @@ -10,7 +12,7 @@ import LiveTvDrawerSection from './dashboard/LiveTvDrawerSection'; import PluginDrawerSection from './dashboard/PluginDrawerSection'; import ServerDrawerSection from './dashboard/ServerDrawerSection'; import MainDrawerContent from './MainDrawerContent'; -import ResponsiveDrawer, { ResponsiveDrawerProps } from './ResponsiveDrawer'; +import { isTabPath } from '../tabs/tabRoutes'; export const DRAWER_WIDTH = 240; @@ -36,6 +38,20 @@ export const isDrawerPath = (path: string) => ( || ADMIN_DRAWER_ROUTES.some(route => route.path === path || `/${route.path}` === path) ); +const Drawer: FC = ({ children, ...props }) => { + const location = useLocation(); + const hasSecondaryToolBar = isTabPath(location.pathname); + + return ( + + {children} + + ); +}; + const AppDrawer: FC = ({ open = false, onClose, @@ -48,13 +64,13 @@ const AppDrawer: FC = ({ key={route.path} path={route.path} element={ - - + } /> )) @@ -65,7 +81,7 @@ const AppDrawer: FC = ({ key={route.path} path={route.path} element={ - = ({ - + } /> )) diff --git a/src/apps/experimental/components/drawers/MainDrawerContent.tsx b/src/apps/experimental/components/drawers/MainDrawerContent.tsx index f402189cf7..4d2a74b8a9 100644 --- a/src/apps/experimental/components/drawers/MainDrawerContent.tsx +++ b/src/apps/experimental/components/drawers/MainDrawerContent.tsx @@ -17,12 +17,12 @@ import ListSubheader from '@mui/material/ListSubheader'; import React, { useEffect, useState } from 'react'; import { useLocation } from 'react-router-dom'; +import ListItemLink from 'components/ListItemLink'; +import { appRouter } from 'components/router/appRouter'; import { useApi } from 'hooks/useApi'; import { useWebConfig } from 'hooks/useWebConfig'; import globalize from 'scripts/globalize'; -import { appRouter } from 'components/router/appRouter'; -import ListItemLink from './ListItemLink'; import LibraryIcon from '../LibraryIcon'; const MainDrawerContent = () => { diff --git a/src/apps/experimental/components/drawers/dashboard/AdvancedDrawerSection.tsx b/src/apps/experimental/components/drawers/dashboard/AdvancedDrawerSection.tsx index 7e0bb4e366..5a74c68632 100644 --- a/src/apps/experimental/components/drawers/dashboard/AdvancedDrawerSection.tsx +++ b/src/apps/experimental/components/drawers/dashboard/AdvancedDrawerSection.tsx @@ -15,10 +15,9 @@ import ListSubheader from '@mui/material/ListSubheader'; import React from 'react'; import { useLocation } from 'react-router-dom'; +import ListItemLink from 'components/ListItemLink'; import globalize from 'scripts/globalize'; -import ListItemLink from '../ListItemLink'; - const PLUGIN_PATHS = [ '/installedplugins.html', '/availableplugins.html', diff --git a/src/apps/experimental/components/drawers/dashboard/DevicesDrawerSection.tsx b/src/apps/experimental/components/drawers/dashboard/DevicesDrawerSection.tsx index cb3cbf33ce..fe3ec09217 100644 --- a/src/apps/experimental/components/drawers/dashboard/DevicesDrawerSection.tsx +++ b/src/apps/experimental/components/drawers/dashboard/DevicesDrawerSection.tsx @@ -8,10 +8,9 @@ import ListSubheader from '@mui/material/ListSubheader'; import React from 'react'; import { useLocation } from 'react-router-dom'; +import ListItemLink from 'components/ListItemLink'; import globalize from 'scripts/globalize'; -import ListItemLink from '../ListItemLink'; - const DLNA_PATHS = [ '/dlnasettings.html', '/dlnaprofiles.html' diff --git a/src/apps/experimental/components/drawers/dashboard/LiveTvDrawerSection.tsx b/src/apps/experimental/components/drawers/dashboard/LiveTvDrawerSection.tsx index 505973b82c..e3d20e154a 100644 --- a/src/apps/experimental/components/drawers/dashboard/LiveTvDrawerSection.tsx +++ b/src/apps/experimental/components/drawers/dashboard/LiveTvDrawerSection.tsx @@ -6,10 +6,9 @@ import ListItemText from '@mui/material/ListItemText'; import ListSubheader from '@mui/material/ListSubheader'; import React from 'react'; +import ListItemLink from 'components/ListItemLink'; import globalize from 'scripts/globalize'; -import ListItemLink from '../ListItemLink'; - const LiveTvDrawerSection = () => { return ( { const { api } = useApi(); const [ pagesInfo, setPagesInfo ] = useState([]); diff --git a/src/apps/experimental/components/drawers/dashboard/ServerDrawerSection.tsx b/src/apps/experimental/components/drawers/dashboard/ServerDrawerSection.tsx index 388c1feeeb..2ed6b73f86 100644 --- a/src/apps/experimental/components/drawers/dashboard/ServerDrawerSection.tsx +++ b/src/apps/experimental/components/drawers/dashboard/ServerDrawerSection.tsx @@ -8,10 +8,9 @@ import ListSubheader from '@mui/material/ListSubheader'; import React from 'react'; import { useLocation } from 'react-router-dom'; +import ListItemLink from 'components/ListItemLink'; import globalize from 'scripts/globalize'; -import ListItemLink from '../ListItemLink'; - const LIBRARY_PATHS = [ '/library.html', '/librarydisplay.html', diff --git a/src/apps/stable/App.tsx b/src/apps/stable/App.tsx index 3ad72bcb84..8285cbc9ec 100644 --- a/src/apps/stable/App.tsx +++ b/src/apps/stable/App.tsx @@ -1,8 +1,7 @@ import React from 'react'; import { Navigate, Outlet, Route, Routes } from 'react-router-dom'; -import AppHeader from 'components/AppHeader'; -import Backdrop from 'components/Backdrop'; +import AppBody from 'components/AppBody'; import ServerContentPage from 'components/ServerContentPage'; import ConnectionRequired from 'components/ConnectionRequired'; import { toAsyncPageRoute } from 'components/router/AsyncRoute'; @@ -14,15 +13,9 @@ import { REDIRECTS } from './routes/_redirects'; import { toRedirectRoute } from 'components/router/Redirect'; const Layout = () => ( - <> - - - -
-
- -
- + + + ); const StableApp = () => ( @@ -53,10 +46,10 @@ const StableApp = () => ( {/* Suppress warnings for unhandled routes */} - - {/* Redirects for old paths */} - {REDIRECTS.map(toRedirectRoute)} + + {/* Redirects for old paths */} + {REDIRECTS.map(toRedirectRoute)} ); diff --git a/src/components/AppBody.tsx b/src/components/AppBody.tsx new file mode 100644 index 0000000000..5d10bca2c9 --- /dev/null +++ b/src/components/AppBody.tsx @@ -0,0 +1,24 @@ +import React, { FC, useEffect } from 'react'; +import viewContainer from './viewContainer'; + +/** + * A simple component that includes the correct structure for ViewManager pages + * to exist alongside standard React pages. + */ +const AppBody: FC = ({ children }) => { + useEffect(() => () => { + // Reset view container state on unload + viewContainer.reset(); + }, []); + + return ( + <> +
+
+ {children} +
+ + ); +}; + +export default AppBody; diff --git a/src/components/AppHeader.tsx b/src/components/AppHeader.tsx index 941d36a940..1ce6a6a8c4 100644 --- a/src/components/AppHeader.tsx +++ b/src/components/AppHeader.tsx @@ -1,19 +1,29 @@ -import React, { useEffect } from 'react'; +import React, { FC, useEffect } from 'react'; -const AppHeader = () => { +interface AppHeaderParams { + isHidden?: boolean +} + +const AppHeader: FC = ({ + isHidden = false +}) => { useEffect(() => { // Initialize the UI components after first render import('../scripts/libraryMenu'); }, []); return ( - <> + /** + * NOTE: These components are not used with the new layouts, but legacy views interact with the elements + * directly so they need to be present in the DOM. We use display: none to hide them and prevent errors. + */ +
- +
); }; diff --git a/src/apps/experimental/components/ElevationScroll.tsx b/src/components/ElevationScroll.tsx similarity index 100% rename from src/apps/experimental/components/ElevationScroll.tsx rename to src/components/ElevationScroll.tsx diff --git a/src/apps/experimental/components/drawers/ListItemLink.tsx b/src/components/ListItemLink.tsx similarity index 100% rename from src/apps/experimental/components/drawers/ListItemLink.tsx rename to src/components/ListItemLink.tsx diff --git a/src/apps/experimental/components/drawers/ResponsiveDrawer.tsx b/src/components/ResponsiveDrawer.tsx similarity index 87% rename from src/apps/experimental/components/drawers/ResponsiveDrawer.tsx rename to src/components/ResponsiveDrawer.tsx index 1f7a554cf4..bc463cca2f 100644 --- a/src/apps/experimental/components/drawers/ResponsiveDrawer.tsx +++ b/src/components/ResponsiveDrawer.tsx @@ -5,14 +5,13 @@ import SwipeableDrawer from '@mui/material/SwipeableDrawer'; import Toolbar from '@mui/material/Toolbar'; import useMediaQuery from '@mui/material/useMediaQuery'; import React, { FC, useCallback } from 'react'; -import { useLocation } from 'react-router-dom'; import browser from 'scripts/browser'; -import { DRAWER_WIDTH } from './AppDrawer'; -import { isTabPath } from '../tabs/tabRoutes'; +export const DRAWER_WIDTH = 240; export interface ResponsiveDrawerProps { + hasSecondaryToolBar?: boolean open: boolean onClose: () => void onOpen: () => void @@ -20,18 +19,17 @@ export interface ResponsiveDrawerProps { const ResponsiveDrawer: FC = ({ children, + hasSecondaryToolBar = false, open = false, onClose, onOpen }) => { - const location = useLocation(); const isSmallScreen = useMediaQuery((theme: Theme) => theme.breakpoints.up('sm')); const isLargeScreen = useMediaQuery((theme: Theme) => theme.breakpoints.up('lg')); - const isTallToolbar = isTabPath(location.pathname) && !isLargeScreen; const getToolbarStyles = useCallback((theme: Theme) => ({ - marginBottom: isTallToolbar ? theme.spacing(6) : 0 - }), [ isTallToolbar ]); + marginBottom: (hasSecondaryToolBar && !isLargeScreen) ? theme.spacing(6) : 0 + }), [ hasSecondaryToolBar, isLargeScreen ]); return ( isSmallScreen ? ( /* DESKTOP DRAWER */ diff --git a/src/components/router/AsyncRoute.tsx b/src/components/router/AsyncRoute.tsx index 9ed49544fb..031e5700ea 100644 --- a/src/components/router/AsyncRoute.tsx +++ b/src/components/router/AsyncRoute.tsx @@ -1,4 +1,4 @@ -import loadable from '@loadable/component'; +import loadable, { LoadableComponent } from '@loadable/component'; import React from 'react'; import { Route } from 'react-router-dom'; @@ -10,13 +10,18 @@ export enum AsyncRouteType { export interface AsyncRoute { /** The URL path for this route. */ path: string - /** The relative path to the page component in the routes directory. */ - page: string - /** The route should use the page component from the experimental app. */ + /** + * The relative path to the page component in the routes directory. + * Will fallback to using the `path` value if not specified. + */ + page?: string + /** The page element to render. */ + element?: LoadableComponent + /** The page type used to load the correct page element. */ type?: AsyncRouteType } -interface AsyncPageProps { +export interface AsyncPageProps { /** The relative path to the page component in the routes directory. */ page: string } @@ -31,14 +36,19 @@ const StableAsyncPage = loadable( { cacheKey: (props: AsyncPageProps) => props.page } ); -export const toAsyncPageRoute = ({ path, page, type = AsyncRouteType.Stable }: AsyncRoute) => ( - { + const Element = element + || ( type === AsyncRouteType.Experimental ? - : - - )} - /> -); + ExperimentalAsyncPage : + StableAsyncPage + ); + + return ( + } + /> + ); +}; diff --git a/src/components/toolbar/AppToolbar.tsx b/src/components/toolbar/AppToolbar.tsx new file mode 100644 index 0000000000..8ed92eee16 --- /dev/null +++ b/src/components/toolbar/AppToolbar.tsx @@ -0,0 +1,129 @@ +import ArrowBack from '@mui/icons-material/ArrowBack'; +import MenuIcon from '@mui/icons-material/Menu'; +import Box from '@mui/material/Box'; +import IconButton from '@mui/material/IconButton'; +import Toolbar from '@mui/material/Toolbar'; +import Tooltip from '@mui/material/Tooltip'; +import Typography from '@mui/material/Typography'; +import React, { FC, ReactNode } from 'react'; +import { Link } from 'react-router-dom'; + +import appIcon from 'assets/img/icon-transparent.png'; +import { appRouter } from 'components/router/appRouter'; +import { useApi } from 'hooks/useApi'; +import globalize from 'scripts/globalize'; + +import UserMenuButton from './UserMenuButton'; + +interface AppToolbarProps { + buttons?: ReactNode + isDrawerAvailable: boolean + isDrawerOpen: boolean + onDrawerButtonClick: (event: React.MouseEvent) => void +} + +const onBackButtonClick = () => { + appRouter.back() + .catch(err => { + console.error('[AppToolbar] error calling appRouter.back', err); + }); +}; + +const AppToolbar: FC = ({ + buttons, + children, + isDrawerAvailable, + isDrawerOpen, + onDrawerButtonClick +}) => { + const { user } = useApi(); + const isUserLoggedIn = Boolean(user); + + const isBackButtonAvailable = appRouter.canGoBack(); + + return ( + + {isUserLoggedIn && isDrawerAvailable && ( + + + + + + )} + + {isBackButtonAvailable && ( + + + + + + )} + + + + + Jellyfin + + + + {children} + + {isUserLoggedIn && ( + <> + + {buttons} + + + + + + + )} + + ); +}; + +export default AppToolbar; diff --git a/src/apps/experimental/components/AppToolbar/menus/AppUserMenu.tsx b/src/components/toolbar/AppUserMenu.tsx similarity index 100% rename from src/apps/experimental/components/AppToolbar/menus/AppUserMenu.tsx rename to src/components/toolbar/AppUserMenu.tsx diff --git a/src/apps/experimental/components/AppToolbar/UserMenuButton.tsx b/src/components/toolbar/UserMenuButton.tsx similarity index 96% rename from src/apps/experimental/components/AppToolbar/UserMenuButton.tsx rename to src/components/toolbar/UserMenuButton.tsx index 89f18669e9..fa47ce85e6 100644 --- a/src/apps/experimental/components/AppToolbar/UserMenuButton.tsx +++ b/src/components/toolbar/UserMenuButton.tsx @@ -2,11 +2,11 @@ import IconButton from '@mui/material/IconButton'; import Tooltip from '@mui/material/Tooltip'; import React, { useCallback, useState } from 'react'; +import UserAvatar from 'components/UserAvatar'; import { useApi } from 'hooks/useApi'; import globalize from 'scripts/globalize'; -import AppUserMenu, { ID } from './menus/AppUserMenu'; -import UserAvatar from 'components/UserAvatar'; +import AppUserMenu, { ID } from './AppUserMenu'; const UserMenuButton = () => { const { user } = useApi(); diff --git a/src/apps/experimental/theme.ts b/src/themes/theme.ts similarity index 96% rename from src/apps/experimental/theme.ts rename to src/themes/theme.ts index d3f84366be..e223e24c51 100644 --- a/src/apps/experimental/theme.ts +++ b/src/themes/theme.ts @@ -1,5 +1,6 @@ import { createTheme } from '@mui/material/styles'; +/** The default Jellyfin app theme for mui */ const theme = createTheme({ palette: { mode: 'dark', From 459681a5128ea6f6c7137513f74eb60052776b74 Mon Sep 17 00:00:00 2001 From: Bill Thornton Date: Fri, 29 Sep 2023 01:07:47 -0400 Subject: [PATCH 2/2] Move stable app to loadable --- src/RootApp.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/RootApp.tsx b/src/RootApp.tsx index 5ee5ffab5c..0d66c15ad2 100644 --- a/src/RootApp.tsx +++ b/src/RootApp.tsx @@ -5,7 +5,6 @@ import React from 'react'; import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; import { ReactQueryDevtools } from '@tanstack/react-query-devtools'; -import StableApp from 'apps/stable/App'; import AppHeader from 'components/AppHeader'; import Backdrop from 'components/Backdrop'; import { HistoryRouter } from 'components/router/HistoryRouter'; @@ -14,6 +13,7 @@ import { WebConfigProvider } from 'hooks/useWebConfig'; import theme from 'themes/theme'; const ExperimentalApp = loadable(() => import('./apps/experimental/App')); +const StableApp = loadable(() => import('./apps/stable/App')); const queryClient = new QueryClient();