mirror of
https://github.com/jellyfin/jellyfin-web
synced 2025-03-30 19:56:21 +00:00
Update mui based layout ui
This commit is contained in:
parent
bfbdffdff5
commit
4e7f0136f7
14 changed files with 163 additions and 194 deletions
|
@ -1,7 +1,8 @@
|
||||||
import AppBar from '@mui/material/AppBar';
|
import AppBar from '@mui/material/AppBar';
|
||||||
import Box from '@mui/material/Box';
|
import Box from '@mui/material/Box';
|
||||||
import { useTheme } from '@mui/material/styles';
|
import { type Theme } from '@mui/material/styles';
|
||||||
import React, { FC, useCallback, useEffect, useState } from 'react';
|
import useMediaQuery from '@mui/material/useMediaQuery';
|
||||||
|
import React, { FC, useCallback, useState } from 'react';
|
||||||
import { Outlet, useLocation } from 'react-router-dom';
|
import { Outlet, useLocation } from 'react-router-dom';
|
||||||
|
|
||||||
import AppBody from 'components/AppBody';
|
import AppBody from 'components/AppBody';
|
||||||
|
@ -9,7 +10,6 @@ import AppToolbar from 'components/toolbar/AppToolbar';
|
||||||
import ElevationScroll from 'components/ElevationScroll';
|
import ElevationScroll from 'components/ElevationScroll';
|
||||||
import { DRAWER_WIDTH } from 'components/ResponsiveDrawer';
|
import { DRAWER_WIDTH } from 'components/ResponsiveDrawer';
|
||||||
import { useApi } from 'hooks/useApi';
|
import { useApi } from 'hooks/useApi';
|
||||||
import { useLocalStorage } from 'hooks/useLocalStorage';
|
|
||||||
|
|
||||||
import AppDrawer from './components/drawer/AppDrawer';
|
import AppDrawer from './components/drawer/AppDrawer';
|
||||||
|
|
||||||
|
@ -19,45 +19,37 @@ interface AppLayoutProps {
|
||||||
drawerlessPaths: string[]
|
drawerlessPaths: string[]
|
||||||
}
|
}
|
||||||
|
|
||||||
interface DashboardAppSettings {
|
|
||||||
isDrawerPinned: boolean
|
|
||||||
}
|
|
||||||
|
|
||||||
const DEFAULT_APP_SETTINGS: DashboardAppSettings = {
|
|
||||||
isDrawerPinned: false
|
|
||||||
};
|
|
||||||
|
|
||||||
const AppLayout: FC<AppLayoutProps> = ({
|
const AppLayout: FC<AppLayoutProps> = ({
|
||||||
drawerlessPaths
|
drawerlessPaths
|
||||||
}) => {
|
}) => {
|
||||||
const [ appSettings, setAppSettings ] = useLocalStorage<DashboardAppSettings>('DashboardAppSettings', DEFAULT_APP_SETTINGS);
|
const [ isDrawerActive, setIsDrawerActive ] = useState(false);
|
||||||
const [ isDrawerActive, setIsDrawerActive ] = useState(appSettings.isDrawerPinned);
|
|
||||||
const location = useLocation();
|
const location = useLocation();
|
||||||
const theme = useTheme();
|
|
||||||
const { user } = useApi();
|
const { user } = useApi();
|
||||||
|
|
||||||
const isDrawerAvailable = !drawerlessPaths.some(path => location.pathname.startsWith(`/${path}`));
|
const isSmallScreen = useMediaQuery((t: Theme) => t.breakpoints.up('sm'));
|
||||||
|
const isDrawerAvailable = !isSmallScreen
|
||||||
|
&& !drawerlessPaths.some(path => location.pathname.startsWith(`/${path}`));
|
||||||
const isDrawerOpen = isDrawerActive && isDrawerAvailable && Boolean(user);
|
const isDrawerOpen = isDrawerActive && isDrawerAvailable && Boolean(user);
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (isDrawerActive !== appSettings.isDrawerPinned) {
|
|
||||||
setAppSettings({
|
|
||||||
...appSettings,
|
|
||||||
isDrawerPinned: isDrawerActive
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}, [ appSettings, isDrawerActive, setAppSettings ]);
|
|
||||||
|
|
||||||
const onToggleDrawer = useCallback(() => {
|
const onToggleDrawer = useCallback(() => {
|
||||||
setIsDrawerActive(!isDrawerActive);
|
setIsDrawerActive(!isDrawerActive);
|
||||||
}, [ isDrawerActive, setIsDrawerActive ]);
|
}, [ isDrawerActive, setIsDrawerActive ]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box sx={{ display: 'flex' }}>
|
<Box sx={{ display: 'flex' }}>
|
||||||
<ElevationScroll elevate={isDrawerOpen}>
|
<ElevationScroll elevate={false}>
|
||||||
<AppBar
|
<AppBar
|
||||||
position='fixed'
|
position='fixed'
|
||||||
sx={{ zIndex: (muiTheme) => muiTheme.zIndex.drawer + 1 }}
|
sx={{
|
||||||
|
width: {
|
||||||
|
xs: '100%',
|
||||||
|
sm: `calc(100% - ${DRAWER_WIDTH}px)`
|
||||||
|
},
|
||||||
|
ml: {
|
||||||
|
xs: 0,
|
||||||
|
sm: `${DRAWER_WIDTH}px`
|
||||||
|
}
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
<AppToolbar
|
<AppToolbar
|
||||||
isDrawerAvailable={isDrawerAvailable}
|
isDrawerAvailable={isDrawerAvailable}
|
||||||
|
@ -77,24 +69,7 @@ const AppLayout: FC<AppLayoutProps> = ({
|
||||||
component='main'
|
component='main'
|
||||||
sx={{
|
sx={{
|
||||||
width: '100%',
|
width: '100%',
|
||||||
flexGrow: 1,
|
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>
|
<AppBody>
|
||||||
|
|
|
@ -1,5 +1,8 @@
|
||||||
|
import ListItem from '@mui/material/ListItem';
|
||||||
|
import List from '@mui/material/List';
|
||||||
import React, { FC } from 'react';
|
import React, { FC } from 'react';
|
||||||
|
|
||||||
|
import DrawerHeaderLink from 'apps/experimental/components/drawers/DrawerHeaderLink';
|
||||||
import ResponsiveDrawer, { ResponsiveDrawerProps } from 'components/ResponsiveDrawer';
|
import ResponsiveDrawer, { ResponsiveDrawerProps } from 'components/ResponsiveDrawer';
|
||||||
|
|
||||||
import ServerDrawerSection from './sections/ServerDrawerSection';
|
import ServerDrawerSection from './sections/ServerDrawerSection';
|
||||||
|
@ -18,6 +21,11 @@ const AppDrawer: FC<ResponsiveDrawerProps> = ({
|
||||||
onClose={onClose}
|
onClose={onClose}
|
||||||
onOpen={onOpen}
|
onOpen={onOpen}
|
||||||
>
|
>
|
||||||
|
<List disablePadding>
|
||||||
|
<ListItem disablePadding>
|
||||||
|
<DrawerHeaderLink />
|
||||||
|
</ListItem>
|
||||||
|
</List>
|
||||||
<ServerDrawerSection />
|
<ServerDrawerSection />
|
||||||
<DevicesDrawerSection />
|
<DevicesDrawerSection />
|
||||||
<LiveTvDrawerSection />
|
<LiveTvDrawerSection />
|
||||||
|
|
|
@ -1,46 +1,28 @@
|
||||||
import React, { useCallback, useEffect, useState } from 'react';
|
import React, { useCallback, useState } from 'react';
|
||||||
import AppBar from '@mui/material/AppBar';
|
import AppBar from '@mui/material/AppBar';
|
||||||
import Box from '@mui/material/Box';
|
import Box from '@mui/material/Box';
|
||||||
import { useTheme } from '@mui/material/styles';
|
import { type Theme } from '@mui/material/styles';
|
||||||
|
import useMediaQuery from '@mui/material/useMediaQuery';
|
||||||
import { Outlet, useLocation } from 'react-router-dom';
|
import { Outlet, useLocation } from 'react-router-dom';
|
||||||
|
|
||||||
import AppBody from 'components/AppBody';
|
import AppBody from 'components/AppBody';
|
||||||
import ElevationScroll from 'components/ElevationScroll';
|
import ElevationScroll from 'components/ElevationScroll';
|
||||||
import { DRAWER_WIDTH } from 'components/ResponsiveDrawer';
|
import { DRAWER_WIDTH } from 'components/ResponsiveDrawer';
|
||||||
import { useApi } from 'hooks/useApi';
|
import { useApi } from 'hooks/useApi';
|
||||||
import { useLocalStorage } from 'hooks/useLocalStorage';
|
|
||||||
|
|
||||||
import AppToolbar from './components/AppToolbar';
|
import AppToolbar from './components/AppToolbar';
|
||||||
import AppDrawer, { isDrawerPath } from './components/drawers/AppDrawer';
|
import AppDrawer, { isDrawerPath } from './components/drawers/AppDrawer';
|
||||||
|
|
||||||
import './AppOverrides.scss';
|
import './AppOverrides.scss';
|
||||||
|
|
||||||
interface ExperimentalAppSettings {
|
|
||||||
isDrawerPinned: boolean
|
|
||||||
}
|
|
||||||
|
|
||||||
const DEFAULT_EXPERIMENTAL_APP_SETTINGS: ExperimentalAppSettings = {
|
|
||||||
isDrawerPinned: false
|
|
||||||
};
|
|
||||||
|
|
||||||
const AppLayout = () => {
|
const AppLayout = () => {
|
||||||
const [ appSettings, setAppSettings ] = useLocalStorage<ExperimentalAppSettings>('ExperimentalAppSettings', DEFAULT_EXPERIMENTAL_APP_SETTINGS);
|
const [ isDrawerActive, setIsDrawerActive ] = useState(false);
|
||||||
const [ isDrawerActive, setIsDrawerActive ] = useState(appSettings.isDrawerPinned);
|
|
||||||
const { user } = useApi();
|
const { user } = useApi();
|
||||||
const location = useLocation();
|
const location = useLocation();
|
||||||
const theme = useTheme();
|
|
||||||
|
|
||||||
const isDrawerAvailable = isDrawerPath(location.pathname);
|
const isSmallScreen = useMediaQuery((t: Theme) => t.breakpoints.up('sm'));
|
||||||
const isDrawerOpen = isDrawerActive && isDrawerAvailable && Boolean(user);
|
const isDrawerAvailable = isDrawerPath(location.pathname) && Boolean(user);
|
||||||
|
const isDrawerOpen = isDrawerActive && isDrawerAvailable;
|
||||||
useEffect(() => {
|
|
||||||
if (isDrawerActive !== appSettings.isDrawerPinned) {
|
|
||||||
setAppSettings({
|
|
||||||
...appSettings,
|
|
||||||
isDrawerPinned: isDrawerActive
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}, [ appSettings, isDrawerActive, setAppSettings ]);
|
|
||||||
|
|
||||||
const onToggleDrawer = useCallback(() => {
|
const onToggleDrawer = useCallback(() => {
|
||||||
setIsDrawerActive(!isDrawerActive);
|
setIsDrawerActive(!isDrawerActive);
|
||||||
|
@ -48,46 +30,43 @@ const AppLayout = () => {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box sx={{ display: 'flex' }}>
|
<Box sx={{ display: 'flex' }}>
|
||||||
<ElevationScroll elevate={isDrawerOpen}>
|
<ElevationScroll elevate={false}>
|
||||||
<AppBar
|
<AppBar
|
||||||
position='fixed'
|
position='fixed'
|
||||||
sx={{ zIndex: (muiTheme) => muiTheme.zIndex.drawer + 1 }}
|
sx={{
|
||||||
|
width: {
|
||||||
|
xs: '100%',
|
||||||
|
sm: isDrawerAvailable ? `calc(100% - ${DRAWER_WIDTH}px)` : '100%'
|
||||||
|
},
|
||||||
|
ml: {
|
||||||
|
xs: 0,
|
||||||
|
sm: isDrawerAvailable ? DRAWER_WIDTH : 0
|
||||||
|
}
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
<AppToolbar
|
<AppToolbar
|
||||||
|
isDrawerAvailable={!isSmallScreen && isDrawerAvailable}
|
||||||
isDrawerOpen={isDrawerOpen}
|
isDrawerOpen={isDrawerOpen}
|
||||||
onDrawerButtonClick={onToggleDrawer}
|
onDrawerButtonClick={onToggleDrawer}
|
||||||
/>
|
/>
|
||||||
</AppBar>
|
</AppBar>
|
||||||
</ElevationScroll>
|
</ElevationScroll>
|
||||||
|
|
||||||
|
{
|
||||||
|
user && (
|
||||||
<AppDrawer
|
<AppDrawer
|
||||||
open={isDrawerOpen}
|
open={isDrawerOpen}
|
||||||
onClose={onToggleDrawer}
|
onClose={onToggleDrawer}
|
||||||
onOpen={onToggleDrawer}
|
onOpen={onToggleDrawer}
|
||||||
/>
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
<Box
|
<Box
|
||||||
component='main'
|
component='main'
|
||||||
sx={{
|
sx={{
|
||||||
width: '100%',
|
width: '100%',
|
||||||
flexGrow: 1,
|
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>
|
<AppBody>
|
||||||
|
|
|
@ -20,7 +20,7 @@ $mui-bp-xl: 1536px;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fix the padding of some pages
|
// Fix the padding of some pages
|
||||||
.homePage.libraryPage, // Home page
|
.homePage.libraryPage.withTabs, // Home page
|
||||||
.libraryPage:not(.withTabs) { // Tabless library pages
|
.libraryPage:not(.withTabs) { // Tabless library pages
|
||||||
padding-top: 3.25rem !important;
|
padding-top: 3.25rem !important;
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,22 +8,22 @@ import AppToolbar from 'components/toolbar/AppToolbar';
|
||||||
import globalize from 'scripts/globalize';
|
import globalize from 'scripts/globalize';
|
||||||
|
|
||||||
import AppTabs from '../tabs/AppTabs';
|
import AppTabs from '../tabs/AppTabs';
|
||||||
import { isDrawerPath } from '../drawers/AppDrawer';
|
|
||||||
import RemotePlayButton from './RemotePlayButton';
|
import RemotePlayButton from './RemotePlayButton';
|
||||||
import SyncPlayButton from './SyncPlayButton';
|
import SyncPlayButton from './SyncPlayButton';
|
||||||
import { isTabPath } from '../tabs/tabRoutes';
|
import { isTabPath } from '../tabs/tabRoutes';
|
||||||
|
|
||||||
interface AppToolbarProps {
|
interface AppToolbarProps {
|
||||||
|
isDrawerAvailable: boolean
|
||||||
isDrawerOpen: boolean
|
isDrawerOpen: boolean
|
||||||
onDrawerButtonClick: (event: React.MouseEvent<HTMLElement>) => void
|
onDrawerButtonClick: (event: React.MouseEvent<HTMLElement>) => void
|
||||||
}
|
}
|
||||||
|
|
||||||
const ExperimentalAppToolbar: FC<AppToolbarProps> = ({
|
const ExperimentalAppToolbar: FC<AppToolbarProps> = ({
|
||||||
|
isDrawerAvailable,
|
||||||
isDrawerOpen,
|
isDrawerOpen,
|
||||||
onDrawerButtonClick
|
onDrawerButtonClick
|
||||||
}) => {
|
}) => {
|
||||||
const location = useLocation();
|
const location = useLocation();
|
||||||
const isDrawerAvailable = isDrawerPath(location.pathname);
|
|
||||||
const isTabsAvailable = isTabPath(location.pathname);
|
const isTabsAvailable = isTabPath(location.pathname);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|
|
@ -1,11 +1,9 @@
|
||||||
import React, { FC } from 'react';
|
import React, { FC } from 'react';
|
||||||
import { useLocation } from 'react-router-dom';
|
|
||||||
|
|
||||||
import ResponsiveDrawer, { ResponsiveDrawerProps } from 'components/ResponsiveDrawer';
|
import ResponsiveDrawer, { ResponsiveDrawerProps } from 'components/ResponsiveDrawer';
|
||||||
|
|
||||||
import { ASYNC_USER_ROUTES } from '../../routes/asyncRoutes';
|
import { ASYNC_USER_ROUTES } from '../../routes/asyncRoutes';
|
||||||
import { LEGACY_USER_ROUTES } from '../../routes/legacyRoutes';
|
import { LEGACY_USER_ROUTES } from '../../routes/legacyRoutes';
|
||||||
import { isTabPath } from '../tabs/tabRoutes';
|
|
||||||
|
|
||||||
import MainDrawerContent from './MainDrawerContent';
|
import MainDrawerContent from './MainDrawerContent';
|
||||||
|
|
||||||
|
@ -27,20 +25,14 @@ const AppDrawer: FC<ResponsiveDrawerProps> = ({
|
||||||
open = false,
|
open = false,
|
||||||
onClose,
|
onClose,
|
||||||
onOpen
|
onOpen
|
||||||
}) => {
|
}) => (
|
||||||
const location = useLocation();
|
|
||||||
const hasSecondaryToolBar = isTabPath(location.pathname);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<ResponsiveDrawer
|
<ResponsiveDrawer
|
||||||
hasSecondaryToolBar={hasSecondaryToolBar}
|
|
||||||
open={open}
|
open={open}
|
||||||
onClose={onClose}
|
onClose={onClose}
|
||||||
onOpen={onOpen}
|
onOpen={onOpen}
|
||||||
>
|
>
|
||||||
<MainDrawerContent />
|
<MainDrawerContent />
|
||||||
</ResponsiveDrawer>
|
</ResponsiveDrawer>
|
||||||
);
|
);
|
||||||
};
|
|
||||||
|
|
||||||
export default AppDrawer;
|
export default AppDrawer;
|
||||||
|
|
|
@ -0,0 +1,33 @@
|
||||||
|
import Box from '@mui/material/Box';
|
||||||
|
import ListItemIcon from '@mui/material/ListItemIcon';
|
||||||
|
import ListItemText from '@mui/material/ListItemText';
|
||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
import { useApi } from 'hooks/useApi';
|
||||||
|
import { useSystemInfo } from 'hooks/useSystemInfo';
|
||||||
|
import ListItemLink from 'components/ListItemLink';
|
||||||
|
|
||||||
|
import appIcon from 'assets/img/icon-transparent.png';
|
||||||
|
|
||||||
|
const DrawerHeaderLink = () => {
|
||||||
|
const { api } = useApi();
|
||||||
|
const { data: systemInfo } = useSystemInfo(api);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ListItemLink to='/'>
|
||||||
|
<ListItemIcon sx={{ minWidth: 56 }}>
|
||||||
|
<Box
|
||||||
|
component='img'
|
||||||
|
src={appIcon}
|
||||||
|
sx={{ height: '2.5rem' }}
|
||||||
|
/>
|
||||||
|
</ListItemIcon>
|
||||||
|
<ListItemText
|
||||||
|
primary={systemInfo?.ServerName || 'Jellyfin'}
|
||||||
|
primaryTypographyProps={{ variant: 'h6' }}
|
||||||
|
secondary={systemInfo?.Version}
|
||||||
|
/>
|
||||||
|
</ListItemLink>);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default DrawerHeaderLink;
|
|
@ -1,7 +1,5 @@
|
||||||
import type { BaseItemDto } from '@jellyfin/sdk/lib/generated-client/models/base-item-dto';
|
import type { BaseItemDto } from '@jellyfin/sdk/lib/generated-client/models/base-item-dto';
|
||||||
import type { SystemInfo } from '@jellyfin/sdk/lib/generated-client/models/system-info';
|
|
||||||
import { getUserViewsApi } from '@jellyfin/sdk/lib/utils/api/user-views-api';
|
import { getUserViewsApi } from '@jellyfin/sdk/lib/utils/api/user-views-api';
|
||||||
import { getSystemApi } from '@jellyfin/sdk/lib/utils/api/system-api';
|
|
||||||
import Dashboard from '@mui/icons-material/Dashboard';
|
import Dashboard from '@mui/icons-material/Dashboard';
|
||||||
import Edit from '@mui/icons-material/Edit';
|
import Edit from '@mui/icons-material/Edit';
|
||||||
import Favorite from '@mui/icons-material/Favorite';
|
import Favorite from '@mui/icons-material/Favorite';
|
||||||
|
@ -24,11 +22,11 @@ import { useWebConfig } from 'hooks/useWebConfig';
|
||||||
import globalize from 'scripts/globalize';
|
import globalize from 'scripts/globalize';
|
||||||
|
|
||||||
import LibraryIcon from '../LibraryIcon';
|
import LibraryIcon from '../LibraryIcon';
|
||||||
|
import DrawerHeaderLink from './DrawerHeaderLink';
|
||||||
|
|
||||||
const MainDrawerContent = () => {
|
const MainDrawerContent = () => {
|
||||||
const { api, user } = useApi();
|
const { api, user } = useApi();
|
||||||
const location = useLocation();
|
const location = useLocation();
|
||||||
const [ systemInfo, setSystemInfo ] = useState<SystemInfo>();
|
|
||||||
const [ userViews, setUserViews ] = useState<BaseItemDto[]>([]);
|
const [ userViews, setUserViews ] = useState<BaseItemDto[]>([]);
|
||||||
const webConfig = useWebConfig();
|
const webConfig = useWebConfig();
|
||||||
|
|
||||||
|
@ -45,15 +43,6 @@ const MainDrawerContent = () => {
|
||||||
console.warn('[MainDrawer] failed to fetch user views', err);
|
console.warn('[MainDrawer] failed to fetch user views', err);
|
||||||
setUserViews([]);
|
setUserViews([]);
|
||||||
});
|
});
|
||||||
|
|
||||||
getSystemApi(api)
|
|
||||||
.getSystemInfo()
|
|
||||||
.then(({ data }) => {
|
|
||||||
setSystemInfo(data);
|
|
||||||
})
|
|
||||||
.catch(err => {
|
|
||||||
console.warn('[MainDrawer] failed to fetch system info', err);
|
|
||||||
});
|
|
||||||
} else {
|
} else {
|
||||||
setUserViews([]);
|
setUserViews([]);
|
||||||
}
|
}
|
||||||
|
@ -62,7 +51,10 @@ const MainDrawerContent = () => {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{/* MAIN LINKS */}
|
{/* MAIN LINKS */}
|
||||||
<List>
|
<List sx={{ paddingTop: 0 }}>
|
||||||
|
<ListItem disablePadding>
|
||||||
|
<DrawerHeaderLink />
|
||||||
|
</ListItem>
|
||||||
<ListItem disablePadding>
|
<ListItem disablePadding>
|
||||||
<ListItemLink to='/home.html' selected={isHomeSelected}>
|
<ListItemLink to='/home.html' selected={isHomeSelected}>
|
||||||
<ListItemIcon>
|
<ListItemIcon>
|
||||||
|
@ -168,17 +160,6 @@ const MainDrawerContent = () => {
|
||||||
</List>
|
</List>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{/* FOOTER */}
|
|
||||||
<Divider style={{ marginTop: 'auto' }} />
|
|
||||||
<List>
|
|
||||||
<ListItem>
|
|
||||||
<ListItemText
|
|
||||||
primary={systemInfo?.ServerName ? systemInfo.ServerName : 'Jellyfin'}
|
|
||||||
secondary={systemInfo?.Version ? `v${systemInfo.Version}` : ''}
|
|
||||||
/>
|
|
||||||
</ListItem>
|
|
||||||
</List>
|
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,4 +1,6 @@
|
||||||
|
import Box from '@mui/material/Box';
|
||||||
import React, { useEffect } from 'react';
|
import React, { useEffect } from 'react';
|
||||||
|
import { DRAWER_WIDTH } from './ResponsiveDrawer';
|
||||||
|
|
||||||
const Backdrop = () => {
|
const Backdrop = () => {
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
@ -8,7 +10,14 @@ const Backdrop = () => {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div className='backdropContainer' />
|
<Box
|
||||||
|
className='backdropContainer'
|
||||||
|
sx={{
|
||||||
|
left: {
|
||||||
|
sm: DRAWER_WIDTH
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
/>
|
||||||
<div className='backgroundContainer' />
|
<div className='backgroundContainer' />
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
|
|
@ -1,17 +1,15 @@
|
||||||
import { Theme } from '@mui/material/styles';
|
import type { Theme } from '@mui/material/styles';
|
||||||
import Box from '@mui/material/Box';
|
import Box from '@mui/material/Box';
|
||||||
import Drawer from '@mui/material/Drawer';
|
import Drawer from '@mui/material/Drawer';
|
||||||
import SwipeableDrawer from '@mui/material/SwipeableDrawer';
|
import SwipeableDrawer from '@mui/material/SwipeableDrawer';
|
||||||
import Toolbar from '@mui/material/Toolbar';
|
|
||||||
import useMediaQuery from '@mui/material/useMediaQuery';
|
import useMediaQuery from '@mui/material/useMediaQuery';
|
||||||
import React, { FC, useCallback } from 'react';
|
import React, { FC } from 'react';
|
||||||
|
|
||||||
import browser from 'scripts/browser';
|
import browser from 'scripts/browser';
|
||||||
|
|
||||||
export const DRAWER_WIDTH = 240;
|
export const DRAWER_WIDTH = 240;
|
||||||
|
|
||||||
export interface ResponsiveDrawerProps {
|
export interface ResponsiveDrawerProps {
|
||||||
hasSecondaryToolBar?: boolean
|
|
||||||
open: boolean
|
open: boolean
|
||||||
onClose: () => void
|
onClose: () => void
|
||||||
onOpen: () => void
|
onOpen: () => void
|
||||||
|
@ -19,17 +17,11 @@ export interface ResponsiveDrawerProps {
|
||||||
|
|
||||||
const ResponsiveDrawer: FC<ResponsiveDrawerProps> = ({
|
const ResponsiveDrawer: FC<ResponsiveDrawerProps> = ({
|
||||||
children,
|
children,
|
||||||
hasSecondaryToolBar = false,
|
|
||||||
open = false,
|
open = false,
|
||||||
onClose,
|
onClose,
|
||||||
onOpen
|
onOpen
|
||||||
}) => {
|
}) => {
|
||||||
const isSmallScreen = useMediaQuery((theme: Theme) => theme.breakpoints.up('sm'));
|
const isSmallScreen = useMediaQuery((theme: Theme) => theme.breakpoints.up('sm'));
|
||||||
const isLargeScreen = useMediaQuery((theme: Theme) => theme.breakpoints.up('lg'));
|
|
||||||
|
|
||||||
const getToolbarStyles = useCallback((theme: Theme) => ({
|
|
||||||
marginBottom: (hasSecondaryToolBar && !isLargeScreen) ? theme.spacing(6) : 0
|
|
||||||
}), [ hasSecondaryToolBar, isLargeScreen ]);
|
|
||||||
|
|
||||||
return ( isSmallScreen ? (
|
return ( isSmallScreen ? (
|
||||||
/* DESKTOP DRAWER */
|
/* DESKTOP DRAWER */
|
||||||
|
@ -42,14 +34,9 @@ const ResponsiveDrawer: FC<ResponsiveDrawerProps> = ({
|
||||||
boxSizing: 'border-box'
|
boxSizing: 'border-box'
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
variant='persistent'
|
variant='permanent'
|
||||||
anchor='left'
|
anchor='left'
|
||||||
open={open}
|
|
||||||
>
|
>
|
||||||
<Toolbar
|
|
||||||
variant='dense'
|
|
||||||
sx={getToolbarStyles}
|
|
||||||
/>
|
|
||||||
{children}
|
{children}
|
||||||
</Drawer>
|
</Drawer>
|
||||||
) : (
|
) : (
|
||||||
|
@ -65,10 +52,6 @@ const ResponsiveDrawer: FC<ResponsiveDrawerProps> = ({
|
||||||
keepMounted: true // Better open performance on mobile.
|
keepMounted: true // Better open performance on mobile.
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Toolbar
|
|
||||||
variant='dense'
|
|
||||||
sx={getToolbarStyles}
|
|
||||||
/>
|
|
||||||
<Box
|
<Box
|
||||||
role='presentation'
|
role='presentation'
|
||||||
// Close the drawer when the content is clicked
|
// Close the drawer when the content is clicked
|
||||||
|
|
|
@ -4,11 +4,9 @@ import Box from '@mui/material/Box';
|
||||||
import IconButton from '@mui/material/IconButton';
|
import IconButton from '@mui/material/IconButton';
|
||||||
import Toolbar from '@mui/material/Toolbar';
|
import Toolbar from '@mui/material/Toolbar';
|
||||||
import Tooltip from '@mui/material/Tooltip';
|
import Tooltip from '@mui/material/Tooltip';
|
||||||
import Typography from '@mui/material/Typography';
|
|
||||||
import React, { FC, ReactNode } from 'react';
|
import React, { FC, ReactNode } from 'react';
|
||||||
import { Link, useLocation } from 'react-router-dom';
|
import { useLocation } from 'react-router-dom';
|
||||||
|
|
||||||
import appIcon from 'assets/img/icon-transparent.png';
|
|
||||||
import { appRouter } from 'components/router/appRouter';
|
import { appRouter } from 'components/router/appRouter';
|
||||||
import { useApi } from 'hooks/useApi';
|
import { useApi } from 'hooks/useApi';
|
||||||
import globalize from 'scripts/globalize';
|
import globalize from 'scripts/globalize';
|
||||||
|
@ -84,35 +82,6 @@ const AppToolbar: FC<AppToolbarProps> = ({
|
||||||
</Tooltip>
|
</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>
|
|
||||||
|
|
||||||
{children}
|
{children}
|
||||||
|
|
||||||
{isUserLoggedIn && isUserMenuAvailable && (
|
{isUserLoggedIn && isUserMenuAvailable && (
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
import { getQuickConnectApi } from '@jellyfin/sdk/lib/utils/api/quick-connect-api';
|
import { getQuickConnectApi } from '@jellyfin/sdk/lib/utils/api/quick-connect-api';
|
||||||
import { useQuery } from '@tanstack/react-query';
|
import { useQuery } from '@tanstack/react-query';
|
||||||
import { AxiosRequestConfig } from 'axios';
|
import type { AxiosRequestConfig } from 'axios';
|
||||||
|
|
||||||
import { JellyfinApiContext, useApi } from './useApi';
|
import { type JellyfinApiContext, useApi } from './useApi';
|
||||||
|
|
||||||
const fetchQuickConnectEnabled = async (
|
const fetchQuickConnectEnabled = async (
|
||||||
apiContext: JellyfinApiContext,
|
apiContext: JellyfinApiContext,
|
||||||
|
|
23
src/hooks/useSystemInfo.ts
Normal file
23
src/hooks/useSystemInfo.ts
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
import { useQuery } from '@tanstack/react-query';
|
||||||
|
import type { Api } from '@jellyfin/sdk';
|
||||||
|
import { getSystemApi } from '@jellyfin/sdk/lib/utils/api/system-api';
|
||||||
|
import type { AxiosRequestConfig } from 'axios';
|
||||||
|
|
||||||
|
const fetchSystemInfo = async (
|
||||||
|
api: Api | undefined,
|
||||||
|
options: AxiosRequestConfig
|
||||||
|
) => {
|
||||||
|
if (!api) throw new Error('No API instance available');
|
||||||
|
|
||||||
|
const response = await getSystemApi(api)
|
||||||
|
.getSystemInfo(options);
|
||||||
|
return response.data;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const useSystemInfo = (api: Api | undefined) => {
|
||||||
|
return useQuery({
|
||||||
|
queryKey: [ 'SystemInfo' ],
|
||||||
|
queryFn: ({ signal }) => fetchSystemInfo(api, { signal }),
|
||||||
|
enabled: !!api
|
||||||
|
});
|
||||||
|
};
|
|
@ -1,5 +1,7 @@
|
||||||
import { createTheme } from '@mui/material/styles';
|
import { createTheme } from '@mui/material/styles';
|
||||||
|
|
||||||
|
const LIST_ICON_WIDTH = 36;
|
||||||
|
|
||||||
/** The default Jellyfin app theme for mui */
|
/** The default Jellyfin app theme for mui */
|
||||||
const theme = createTheme({
|
const theme = createTheme({
|
||||||
palette: {
|
palette: {
|
||||||
|
@ -49,11 +51,26 @@ const theme = createTheme({
|
||||||
variant: 'filled'
|
variant: 'filled'
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
MuiListItemIcon: {
|
||||||
|
styleOverrides: {
|
||||||
|
root: {
|
||||||
|
minWidth: LIST_ICON_WIDTH
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
MuiListSubheader: {
|
MuiListSubheader: {
|
||||||
styleOverrides: {
|
styleOverrides: {
|
||||||
root: {
|
root: {
|
||||||
// NOTE: Added for drawer subheaders, but maybe it won't work in other cases?
|
// NOTE: Added for drawer subheaders, but maybe it won't work in other cases?
|
||||||
backgroundColor: 'inherit'
|
backgroundColor: 'inherit',
|
||||||
|
position: 'initial'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
MuiListItemText: {
|
||||||
|
styleOverrides: {
|
||||||
|
inset: {
|
||||||
|
paddingLeft: LIST_ICON_WIDTH
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue