1
0
Fork 0
mirror of https://github.com/jellyfin/jellyfin-web synced 2025-03-30 19:56:21 +00:00

Merge pull request #4806 from thornbill/common-component-refactor

This commit is contained in:
Bill Thornton 2023-09-29 16:30:00 -04:00 committed by GitHub
commit 0d5b97455a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
22 changed files with 353 additions and 262 deletions

View file

@ -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)}
</Route>
{/* Redirects for old paths */}
{REDIRECTS.map(toRedirectRoute)}
</Route>
{/* Redirects for old paths */}
{REDIRECTS.map(toRedirectRoute)}
</Routes>
);
};

View file

@ -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 (
<ThemeProvider theme={theme}>
<Backdrop />
<div style={{ display: 'none' }}>
{/*
* 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.
*/}
<AppHeader />
</div>
<Box sx={{ display: 'flex' }}>
<ElevationScroll elevate={isDrawerOpen}>
<AppBar
position='fixed'
sx={{ zIndex: (muiTheme) => muiTheme.zIndex.drawer + 1 }}
>
<AppToolbar
isDrawerOpen={isDrawerOpen}
onDrawerButtonClick={onToggleDrawer}
/>
</AppBar>
</ElevationScroll>
<AppDrawer
open={isDrawerOpen}
onClose={onToggleDrawer}
onOpen={onToggleDrawer}
/>
<Box
component='main'
sx={{
width: '100%',
flexGrow: 1,
transition: theme.transitions.create('margin', {
easing: theme.transitions.easing.sharp,
duration: theme.transitions.duration.leavingScreen
}),
marginLeft: 0,
...(isDrawerAvailable && {
marginLeft: {
sm: `-${DRAWER_WIDTH}px`
}
}),
...(isDrawerActive && {
transition: theme.transitions.create('margin', {
easing: theme.transitions.easing.easeOut,
duration: theme.transitions.duration.enteringScreen
}),
marginLeft: 0
})
}}
<Box sx={{ display: 'flex' }}>
<ElevationScroll elevate={isDrawerOpen}>
<AppBar
position='fixed'
sx={{ zIndex: (muiTheme) => muiTheme.zIndex.drawer + 1 }}
>
<div className='mainAnimatedPages skinBody' />
<div className='skinBody'>
<Outlet />
</div>
</Box>
<AppToolbar
isDrawerOpen={isDrawerOpen}
onDrawerButtonClick={onToggleDrawer}
/>
</AppBar>
</ElevationScroll>
<AppDrawer
open={isDrawerOpen}
onClose={onToggleDrawer}
onOpen={onToggleDrawer}
/>
<Box
component='main'
sx={{
width: '100%',
flexGrow: 1,
transition: theme.transitions.create('margin', {
easing: theme.transitions.easing.sharp,
duration: theme.transitions.duration.leavingScreen
}),
marginLeft: 0,
...(isDrawerAvailable && {
marginLeft: {
sm: `-${DRAWER_WIDTH}px`
}
}),
...(isDrawerActive && {
transition: theme.transitions.create('margin', {
easing: theme.transitions.easing.easeOut,
duration: theme.transitions.duration.enteringScreen
}),
marginLeft: 0
})
}}
>
<AppBody>
<Outlet />
</AppBody>
</Box>
</ThemeProvider>
</Box>
);
};

View file

@ -1,51 +0,0 @@
import IconButton from '@mui/material/IconButton';
import Tooltip from '@mui/material/Tooltip';
import React, { useCallback, useState } from 'react';
import { useApi } from 'hooks/useApi';
import globalize from 'scripts/globalize';
import AppUserMenu, { ID } from './menus/AppUserMenu';
import UserAvatar from 'components/UserAvatar';
const UserMenuButton = () => {
const { user } = useApi();
const [ userMenuAnchorEl, setUserMenuAnchorEl ] = useState<null | HTMLElement>(null);
const isUserMenuOpen = Boolean(userMenuAnchorEl);
const onUserButtonClick = useCallback((event) => {
setUserMenuAnchorEl(event.currentTarget);
}, [ setUserMenuAnchorEl ]);
const onUserMenuClose = useCallback(() => {
setUserMenuAnchorEl(null);
}, [ setUserMenuAnchorEl ]);
return (
<>
<Tooltip title={globalize.translate('UserMenu')}>
<IconButton
size='large'
edge='end'
aria-label={globalize.translate('UserMenu')}
aria-controls={ID}
aria-haspopup='true'
onClick={onUserButtonClick}
color='inherit'
sx={{ padding: 0 }}
>
<UserAvatar user={user} />
</IconButton>
</Tooltip>
<AppUserMenu
open={isUserMenuOpen}
anchorEl={userMenuAnchorEl}
onMenuClose={onUserMenuClose}
/>
</>
);
};
export default UserMenuButton;

View file

@ -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<HTMLElement>) => void
}
const onBackButtonClick = () => {
appRouter.back()
.catch(err => {
console.error('[AppToolbar] error calling appRouter.back', err);
});
};
const AppToolbar: FC<AppToolbarProps> = ({
const ExperimentalAppToolbar: FC<AppToolbarProps> = ({
isDrawerOpen,
onDrawerButtonClick
}) => {
const { user } = useApi();
const isUserLoggedIn = Boolean(user);
const location = useLocation();
const isDrawerAvailable = isDrawerPath(location.pathname);
const isBackButtonAvailable = appRouter.canGoBack();
return (
<Toolbar
variant='dense'
sx={{
flexWrap: {
xs: 'wrap',
lg: 'nowrap'
}
}}
>
{isUserLoggedIn && isDrawerAvailable && (
<Tooltip title={globalize.translate(isDrawerOpen ? 'MenuClose' : 'MenuOpen')}>
<IconButton
size='large'
edge='start'
color='inherit'
aria-label={globalize.translate(isDrawerOpen ? 'MenuClose' : 'MenuOpen')}
onClick={onDrawerButtonClick}
>
<MenuIcon />
</IconButton>
</Tooltip>
)}
{isBackButtonAvailable && (
<Tooltip title={globalize.translate('ButtonBack')}>
<IconButton
size='large'
// Set the edge if the drawer button is not shown
edge={!(isUserLoggedIn && isDrawerAvailable) ? 'start' : undefined}
color='inherit'
aria-label={globalize.translate('ButtonBack')}
onClick={onBackButtonClick}
>
<ArrowBack />
</IconButton>
</Tooltip>
)}
<Box
component={Link}
to='/'
color='inherit'
aria-label={globalize.translate('Home')}
sx={{
ml: 2,
display: 'inline-flex',
textDecoration: 'none'
}}
>
<Box
component='img'
src={appIcon}
sx={{
height: '2rem',
marginInlineEnd: 1
}}
/>
<Typography
variant='h6'
noWrap
component='div'
sx={{ display: { xs: 'none', sm: 'inline-block' } }}
>
Jellyfin
</Typography>
</Box>
<AppTabs isDrawerOpen={isDrawerOpen} />
{isUserLoggedIn && (
<AppToolbar
buttons={
<>
<Box sx={{ display: 'flex', flexGrow: 1, justifyContent: 'flex-end' }}>
<SyncPlayButton />
<RemotePlayButton />
<SyncPlayButton />
<RemotePlayButton />
<Tooltip title={globalize.translate('Search')}>
<IconButton
size='large'
aria-label={globalize.translate('Search')}
color='inherit'
component={Link}
to='/search.html'
>
<SearchIcon />
</IconButton>
</Tooltip>
</Box>
<Box sx={{ flexGrow: 0 }}>
<UserMenuButton />
</Box>
<Tooltip title={globalize.translate('Search')}>
<IconButton
size='large'
aria-label={globalize.translate('Search')}
color='inherit'
component={Link}
to='/search.html'
>
<SearchIcon />
</IconButton>
</Tooltip>
</>
)}
</Toolbar>
}
isDrawerAvailable={isDrawerAvailable}
isDrawerOpen={isDrawerOpen}
onDrawerButtonClick={onDrawerButtonClick}
>
<AppTabs isDrawerOpen={isDrawerOpen} />
</AppToolbar>
);
};
export default AppToolbar;
export default ExperimentalAppToolbar;

View file

@ -1,196 +0,0 @@
import AccountCircle from '@mui/icons-material/AccountCircle';
import AppSettingsAlt from '@mui/icons-material/AppSettingsAlt';
import Close from '@mui/icons-material/Close';
import DashboardIcon from '@mui/icons-material/Dashboard';
import Edit from '@mui/icons-material/Edit';
import Logout from '@mui/icons-material/Logout';
import PhonelinkLock from '@mui/icons-material/PhonelinkLock';
import Settings from '@mui/icons-material/Settings';
import Storage from '@mui/icons-material/Storage';
import Divider from '@mui/material/Divider';
import ListItemIcon from '@mui/material/ListItemIcon';
import ListItemText from '@mui/material/ListItemText';
import Menu, { MenuProps } from '@mui/material/Menu';
import MenuItem from '@mui/material/MenuItem';
import React, { FC, useCallback } from 'react';
import { Link } from 'react-router-dom';
import { appHost } from 'components/apphost';
import { useApi } from 'hooks/useApi';
import globalize from 'scripts/globalize';
import Dashboard from 'utils/dashboard';
export const ID = 'app-user-menu';
interface AppUserMenuProps extends MenuProps {
onMenuClose: () => void
}
const AppUserMenu: FC<AppUserMenuProps> = ({
anchorEl,
open,
onMenuClose
}) => {
const { user } = useApi();
const onClientSettingsClick = useCallback(() => {
window.NativeShell?.openClientSettings();
onMenuClose();
}, [ onMenuClose ]);
const onExitAppClick = useCallback(() => {
appHost.exit();
onMenuClose();
}, [ onMenuClose ]);
const onLogoutClick = useCallback(() => {
Dashboard.logout();
onMenuClose();
}, [ onMenuClose ]);
const onSelectServerClick = useCallback(() => {
Dashboard.selectServer();
onMenuClose();
}, [ onMenuClose ]);
return (
<Menu
anchorEl={anchorEl}
anchorOrigin={{
vertical: 'bottom',
horizontal: 'right'
}}
transformOrigin={{
vertical: 'top',
horizontal: 'right'
}}
id={ID}
keepMounted
open={open}
onClose={onMenuClose}
>
<MenuItem
component={Link}
to={`/userprofile.html?userId=${user?.Id}`}
onClick={onMenuClose}
>
<ListItemIcon>
<AccountCircle />
</ListItemIcon>
<ListItemText>
{globalize.translate('Profile')}
</ListItemText>
</MenuItem>
<MenuItem
component={Link}
to='/mypreferencesmenu.html'
onClick={onMenuClose}
>
<ListItemIcon>
<Settings />
</ListItemIcon>
<ListItemText>
{globalize.translate('Settings')}
</ListItemText>
</MenuItem>
{appHost.supports('clientsettings') && ([
<Divider key='client-settings-divider' />,
<MenuItem
key='client-settings-button'
onClick={onClientSettingsClick}
>
<ListItemIcon>
<AppSettingsAlt />
</ListItemIcon>
<ListItemText>
{globalize.translate('ClientSettings')}
</ListItemText>
</MenuItem>
])}
{/* ADMIN LINKS */}
{user?.Policy?.IsAdministrator && ([
<Divider key='admin-links-divider' />,
<MenuItem
key='admin-dashboard-link'
component={Link}
to='/dashboard.html'
onClick={onMenuClose}
>
<ListItemIcon>
<DashboardIcon />
</ListItemIcon>
<ListItemText primary={globalize.translate('TabDashboard')} />
</MenuItem>,
<MenuItem
key='admin-metadata-link'
component={Link}
to='/edititemmetadata.html'
onClick={onMenuClose}
>
<ListItemIcon>
<Edit />
</ListItemIcon>
<ListItemText primary={globalize.translate('MetadataManager')} />
</MenuItem>
])}
<Divider />
<MenuItem
component={Link}
to='/quickconnect'
onClick={onMenuClose}
>
<ListItemIcon>
<PhonelinkLock />
</ListItemIcon>
<ListItemText>
{globalize.translate('QuickConnect')}
</ListItemText>
</MenuItem>
{appHost.supports('multiserver') && (
<MenuItem
onClick={onSelectServerClick}
>
<ListItemIcon>
<Storage />
</ListItemIcon>
<ListItemText>
{globalize.translate('SelectServer')}
</ListItemText>
</MenuItem>
)}
<MenuItem
onClick={onLogoutClick}
>
<ListItemIcon>
<Logout />
</ListItemIcon>
<ListItemText>
{globalize.translate('ButtonSignOut')}
</ListItemText>
</MenuItem>
{appHost.supports('exitmenu') && ([
<Divider key='exit-menu-divider' />,
<MenuItem
key='exit-menu-button'
onClick={onExitAppClick}
>
<ListItemIcon>
<Close />
</ListItemIcon>
<ListItemText>
{globalize.translate('ButtonExitApp')}
</ListItemText>
</MenuItem>
])}
</Menu>
);
};
export default AppUserMenu;

View file

@ -1,21 +0,0 @@
import useScrollTrigger from '@mui/material/useScrollTrigger';
import React, { ReactElement } from 'react';
/**
* Component that changes the elevation of a child component when scrolled.
*/
const ElevationScroll = ({ children, elevate = false }: { children: ReactElement, elevate?: boolean }) => {
const trigger = useScrollTrigger({
disableHysteresis: true,
threshold: 0
});
const isElevated = elevate || trigger;
return React.cloneElement(children, {
color: isElevated ? 'primary' : 'transparent',
elevation: isElevated ? 4 : 0
});
};
export default ElevationScroll;

View file

@ -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<ResponsiveDrawerProps> = ({ children, ...props }) => {
const location = useLocation();
const hasSecondaryToolBar = isTabPath(location.pathname);
return (
<ResponsiveDrawer
{...props}
hasSecondaryToolBar={hasSecondaryToolBar}
>
{children}
</ResponsiveDrawer>
);
};
const AppDrawer: FC<ResponsiveDrawerProps> = ({
open = false,
onClose,
@ -48,13 +64,13 @@ const AppDrawer: FC<ResponsiveDrawerProps> = ({
key={route.path}
path={route.path}
element={
<ResponsiveDrawer
<Drawer
open={open}
onClose={onClose}
onOpen={onOpen}
>
<MainDrawerContent />
</ResponsiveDrawer>
</Drawer>
}
/>
))
@ -65,7 +81,7 @@ const AppDrawer: FC<ResponsiveDrawerProps> = ({
key={route.path}
path={route.path}
element={
<ResponsiveDrawer
<Drawer
open={open}
onClose={onClose}
onOpen={onOpen}
@ -75,7 +91,7 @@ const AppDrawer: FC<ResponsiveDrawerProps> = ({
<LiveTvDrawerSection />
<AdvancedDrawerSection />
<PluginDrawerSection />
</ResponsiveDrawer>
</Drawer>
}
/>
))

View file

@ -1,45 +0,0 @@
import ListItemButton, { ListItemButtonBaseProps } from '@mui/material/ListItemButton';
import React, { FC } from 'react';
import { Link, useLocation, useSearchParams } from 'react-router-dom';
interface ListItemLinkProps extends ListItemButtonBaseProps {
to: string
}
const isMatchingParams = (routeParams: URLSearchParams, currentParams: URLSearchParams) => {
for (const param of routeParams) {
if (currentParams.get(param[0]) !== param[1]) {
return false;
}
}
return true;
};
const ListItemLink: FC<ListItemLinkProps> = ({
children,
to,
...params
}) => {
const location = useLocation();
const [ searchParams ] = useSearchParams();
const [ toPath, toParams ] = to.split('?');
// eslint-disable-next-line compat/compat
const toSearchParams = new URLSearchParams(`?${toParams}`);
const selected = location.pathname === toPath && (!toParams || isMatchingParams(toSearchParams, searchParams));
return (
<ListItemButton
component={Link}
to={to}
selected={selected}
{...params}
>
{children}
</ListItemButton>
);
};
export default ListItemLink;

View file

@ -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 = () => {

View file

@ -1,86 +0,0 @@
import { Theme } from '@mui/material/styles';
import Box from '@mui/material/Box';
import Drawer from '@mui/material/Drawer';
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 interface ResponsiveDrawerProps {
open: boolean
onClose: () => void
onOpen: () => void
}
const ResponsiveDrawer: FC<ResponsiveDrawerProps> = ({
children,
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 ]);
return ( isSmallScreen ? (
/* DESKTOP DRAWER */
<Drawer
sx={{
width: DRAWER_WIDTH,
flexShrink: 0,
'& .MuiDrawer-paper': {
width: DRAWER_WIDTH,
boxSizing: 'border-box'
}
}}
variant='persistent'
anchor='left'
open={open}
>
<Toolbar
variant='dense'
sx={getToolbarStyles}
/>
{children}
</Drawer>
) : (
/* MOBILE DRAWER */
<SwipeableDrawer
anchor='left'
open={open}
onClose={onClose}
onOpen={onOpen}
// Disable swipe to open on iOS since it interferes with back navigation
disableDiscovery={browser.iOS}
ModalProps={{
keepMounted: true // Better open performance on mobile.
}}
>
<Toolbar
variant='dense'
sx={getToolbarStyles}
/>
<Box
role='presentation'
// Close the drawer when the content is clicked
onClick={onClose}
onKeyDown={onClose}
>
{children}
</Box>
</SwipeableDrawer>
));
};
export default ResponsiveDrawer;

View file

@ -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',

View file

@ -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'

View file

@ -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 (
<List

View file

@ -8,12 +8,11 @@ import ListItemText from '@mui/material/ListItemText';
import ListSubheader from '@mui/material/ListSubheader';
import React, { useEffect, useState } from 'react';
import ListItemLink from 'components/ListItemLink';
import { useApi } from 'hooks/useApi';
import globalize from 'scripts/globalize';
import Dashboard from 'utils/dashboard';
import ListItemLink from '../ListItemLink';
const PluginDrawerSection = () => {
const { api } = useApi();
const [ pagesInfo, setPagesInfo ] = useState<ConfigurationPageInfo[]>([]);

View file

@ -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',

View file

@ -1,62 +0,0 @@
import { createTheme } from '@mui/material/styles';
const theme = createTheme({
palette: {
mode: 'dark',
primary: {
main: '#00a4dc'
},
secondary: {
main: '#aa5cc3'
},
background: {
default: '#101010',
paper: '#202020'
},
action: {
selectedOpacity: 0.2
}
},
typography: {
fontFamily: '"Noto Sans", sans-serif',
button: {
textTransform: 'none'
},
h1: {
fontSize: '1.8rem'
},
h2: {
fontSize: '1.5rem'
},
h3: {
fontSize: '1.17rem'
}
},
components: {
MuiButton: {
defaultProps: {
variant: 'contained'
}
},
MuiFormControl: {
defaultProps: {
variant: 'filled'
}
},
MuiTextField: {
defaultProps: {
variant: 'filled'
}
},
MuiListSubheader: {
styleOverrides: {
root: {
// NOTE: Added for drawer subheaders, but maybe it won't work in other cases?
backgroundColor: 'inherit'
}
}
}
}
});
export default theme;

View file

@ -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 = () => (
<>
<Backdrop />
<AppHeader />
<div className='mainAnimatedPages skinBody' />
<div className='skinBody'>
<Outlet />
</div>
</>
<AppBody>
<Outlet />
</AppBody>
);
const StableApp = () => (
@ -53,10 +46,10 @@ const StableApp = () => (
{/* Suppress warnings for unhandled routes */}
<Route path='*' element={null} />
{/* Redirects for old paths */}
{REDIRECTS.map(toRedirectRoute)}
</Route>
{/* Redirects for old paths */}
{REDIRECTS.map(toRedirectRoute)}
</Routes>
);