Move user settings menu to react (#6675)
* Move user settings menu to react * Add default href value to LinkButton * Use loading component while data is pending
This commit is contained in:
@@ -2,14 +2,15 @@ import { AsyncRoute } from 'components/router/AsyncRoute';
|
||||
import { AppType } from 'constants/appType';
|
||||
|
||||
export const ASYNC_USER_ROUTES: AsyncRoute[] = [
|
||||
{ path: 'home', page: 'home', type: AppType.Experimental },
|
||||
{ path: 'quickconnect', page: 'quickConnect' },
|
||||
{ path: 'search', page: 'search' },
|
||||
{ path: 'userprofile', page: 'user/userprofile' },
|
||||
{ path: 'movies', page: 'movies', type: AppType.Experimental },
|
||||
{ path: 'tv', page: 'shows', type: AppType.Experimental },
|
||||
{ path: 'music', page: 'music', type: AppType.Experimental },
|
||||
{ path: 'livetv', page: 'livetv', type: AppType.Experimental },
|
||||
{ path: 'home', type: AppType.Experimental },
|
||||
{ path: 'homevideos', type: AppType.Experimental },
|
||||
{ path: 'livetv', type: AppType.Experimental },
|
||||
{ path: 'movies', type: AppType.Experimental },
|
||||
{ path: 'music', type: AppType.Experimental },
|
||||
{ path: 'mypreferencesdisplay', page: 'user/display', type: AppType.Experimental },
|
||||
{ path: 'homevideos', page: 'homevideos', type: AppType.Experimental }
|
||||
{ path: 'mypreferencesmenu', page: 'user/settings' },
|
||||
{ path: 'quickconnect', page: 'quickConnect' },
|
||||
{ path: 'search' },
|
||||
{ path: 'tv', page: 'shows', type: AppType.Experimental },
|
||||
{ path: 'userprofile', page: 'user/userprofile' }
|
||||
];
|
||||
|
||||
@@ -19,12 +19,6 @@ export const LEGACY_USER_ROUTES: LegacyRoute[] = [
|
||||
controller: 'lyrics',
|
||||
view: 'lyrics.html'
|
||||
}
|
||||
}, {
|
||||
path: 'mypreferencesmenu',
|
||||
pageProps: {
|
||||
controller: 'user/menu/index',
|
||||
view: 'user/menu/index.html'
|
||||
}
|
||||
}, {
|
||||
path: 'mypreferencescontrols',
|
||||
pageProps: {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { AsyncRoute } from '../../../../components/router/AsyncRoute';
|
||||
|
||||
export const ASYNC_USER_ROUTES: AsyncRoute[] = [
|
||||
{ path: 'mypreferencesmenu', page: 'user/settings' },
|
||||
{ path: 'quickconnect', page: 'quickConnect' },
|
||||
{ path: 'search', page: 'search' },
|
||||
{ path: 'userprofile', page: 'user/userprofile' }
|
||||
|
||||
@@ -31,12 +31,6 @@ export const LEGACY_USER_ROUTES: LegacyRoute[] = [
|
||||
controller: 'music/musicrecommended',
|
||||
view: 'music/music.html'
|
||||
}
|
||||
}, {
|
||||
path: 'mypreferencesmenu',
|
||||
pageProps: {
|
||||
controller: 'user/menu/index',
|
||||
view: 'user/menu/index.html'
|
||||
}
|
||||
}, {
|
||||
path: 'mypreferencescontrols',
|
||||
pageProps: {
|
||||
|
||||
@@ -0,0 +1,361 @@
|
||||
import type { UserDto } from '@jellyfin/sdk/lib/generated-client/models/user-dto';
|
||||
import React, { useEffect, useMemo, useState, type FC } from 'react';
|
||||
import { useSearchParams } from 'react-router-dom';
|
||||
|
||||
import { appHost } from 'components/apphost';
|
||||
import layoutManager from 'components/layoutManager';
|
||||
import Loading from 'components/loading/LoadingComponent';
|
||||
import Page from 'components/Page';
|
||||
import LinkButton from 'elements/emby-button/LinkButton';
|
||||
import { useApi } from 'hooks/useApi';
|
||||
import { useQuickConnectEnabled } from 'hooks/useQuickConnect';
|
||||
import { useUsers } from 'hooks/useUsers';
|
||||
import globalize from 'lib/globalize';
|
||||
import browser from 'scripts/browser';
|
||||
import Dashboard from 'utils/dashboard';
|
||||
import shell from 'scripts/shell';
|
||||
|
||||
const UserSettingsPage: FC = () => {
|
||||
const { user: currentUser } = useApi();
|
||||
const [ searchParams ] = useSearchParams();
|
||||
const {
|
||||
data: isQuickConnectEnabled,
|
||||
isPending: isQuickConnectEnabledPending
|
||||
} = useQuickConnectEnabled();
|
||||
const { data: users } = useUsers();
|
||||
const [ user, setUser ] = useState<UserDto>();
|
||||
|
||||
const userId = useMemo(() => (
|
||||
searchParams.get('userId') || currentUser?.Id
|
||||
), [ currentUser, searchParams ]);
|
||||
const isLoggedInUser = useMemo(() => (
|
||||
userId && userId === currentUser?.Id
|
||||
), [ currentUser, userId ]);
|
||||
|
||||
useEffect(() => {
|
||||
if (userId) {
|
||||
if (userId === currentUser?.Id) setUser(currentUser);
|
||||
else setUser(users?.find(({ Id }) => userId === Id));
|
||||
}
|
||||
}, [ currentUser, userId, users ]);
|
||||
|
||||
if (!userId || !user || isQuickConnectEnabledPending) {
|
||||
return (
|
||||
<Loading />
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<Page
|
||||
id='myPreferencesMenuPage'
|
||||
className='libraryPage userPreferencesPage noSecondaryNavPage mainAnimatedPage'
|
||||
title={globalize.translate('Settings')}
|
||||
shouldAutoFocus
|
||||
>
|
||||
<div className='padded-left padded-right padded-bottom-page padded-top'>
|
||||
<div
|
||||
className='readOnlyContent'
|
||||
style={{
|
||||
margin: '0 auto'
|
||||
}}
|
||||
>
|
||||
<div className='verticalSection verticalSection-extrabottompadding'>
|
||||
<h2
|
||||
className='sectionTitle headerUsername'
|
||||
style={{
|
||||
paddingLeft: '0.25em'
|
||||
}}
|
||||
>
|
||||
{user.Name}
|
||||
</h2>
|
||||
|
||||
<LinkButton
|
||||
href={`#/userprofile?userId=${userId}`}
|
||||
className='lnkUserProfile listItem-border'
|
||||
style={{
|
||||
display: 'block',
|
||||
margin: 0,
|
||||
padding: 0
|
||||
}}
|
||||
>
|
||||
<div className='listItem'>
|
||||
<span className='material-icons listItemIcon listItemIcon-transparent person' aria-hidden='true' />
|
||||
<div className='listItemBody'>
|
||||
<div className='listItemBodyText'>
|
||||
{globalize.translate('Profile')}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</LinkButton>
|
||||
|
||||
{isQuickConnectEnabled && (
|
||||
<LinkButton
|
||||
href={`#/quickconnect?userId=${userId}`}
|
||||
className='lnkQuickConnectPreferences listItem-border'
|
||||
style={{
|
||||
display: 'block',
|
||||
margin: 0,
|
||||
padding: 0
|
||||
}}
|
||||
>
|
||||
<div className='listItem'>
|
||||
<span className='material-icons listItemIcon listItemIcon-transparent phonelink_lock' aria-hidden='true' />
|
||||
<div className='listItemBody'>
|
||||
<div className='listItemBodyText'>
|
||||
{globalize.translate('QuickConnect')}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</LinkButton>
|
||||
)}
|
||||
|
||||
<LinkButton
|
||||
href={`#/mypreferencesdisplay?userId=${userId}`}
|
||||
className='lnkDisplayPreferences listItem-border'
|
||||
style={{
|
||||
display: 'block',
|
||||
margin: 0,
|
||||
padding: 0
|
||||
}}
|
||||
>
|
||||
<div className='listItem'>
|
||||
<span className='material-icons listItemIcon listItemIcon-transparent tv' aria-hidden='true' />
|
||||
<div className='listItemBody'>
|
||||
<div className='listItemBodyText'>
|
||||
{globalize.translate('Display')}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</LinkButton>
|
||||
|
||||
<LinkButton
|
||||
href={`#/mypreferenceshome?userId=${userId}`}
|
||||
className='lnkHomePreferences listItem-border'
|
||||
style={{
|
||||
display: 'block',
|
||||
margin: 0,
|
||||
padding: 0
|
||||
}}
|
||||
>
|
||||
<div className='listItem'>
|
||||
<span className='material-icons listItemIcon listItemIcon-transparent home' aria-hidden='true' />
|
||||
<div className='listItemBody'>
|
||||
<div className='listItemBodyText'>
|
||||
{globalize.translate('Home')}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</LinkButton>
|
||||
|
||||
<LinkButton
|
||||
href={`#/mypreferencesplayback?userId=${userId}`}
|
||||
className='lnkPlaybackPreferences listItem-border'
|
||||
style={{
|
||||
display: 'block',
|
||||
margin: 0,
|
||||
padding: 0
|
||||
}}
|
||||
>
|
||||
<div className='listItem'>
|
||||
<span className='material-icons listItemIcon listItemIcon-transparent play_circle_filled' aria-hidden='true' />
|
||||
<div className='listItemBody'>
|
||||
<div className='listItemBodyText'>
|
||||
{globalize.translate('TitlePlayback')}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</LinkButton>
|
||||
|
||||
<LinkButton
|
||||
href={`#/mypreferencessubtitles?userId=${userId}`}
|
||||
className='lnkSubtitlePreferences listItem-border'
|
||||
style={{
|
||||
display: 'block',
|
||||
margin: 0,
|
||||
padding: 0
|
||||
}}
|
||||
>
|
||||
<div className='listItem'>
|
||||
<span className='material-icons listItemIcon listItemIcon-transparent closed_caption' aria-hidden='true' />
|
||||
<div className='listItemBody'>
|
||||
<div className='listItemBodyText'>
|
||||
{globalize.translate('Subtitles')}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</LinkButton>
|
||||
|
||||
{appHost.supports('clientsettings') && (
|
||||
<LinkButton
|
||||
onClick={shell.openClientSettings}
|
||||
className='clientSettings listItem-border'
|
||||
style={{
|
||||
display: 'block',
|
||||
margin: 0,
|
||||
padding: 0
|
||||
}}
|
||||
>
|
||||
<div className='listItem'>
|
||||
<span className='material-icons listItemIcon listItemIcon-transparent devices_other' aria-hidden='true' />
|
||||
<div className='listItemBody'>
|
||||
<div className='listItemBodyText'>
|
||||
{globalize.translate('ClientSettings')}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</LinkButton>
|
||||
)}
|
||||
|
||||
{isLoggedInUser && !browser.mobile && (
|
||||
<LinkButton
|
||||
href={`#/mypreferencescontrols?userId=${userId}`}
|
||||
className='lnkControlsPreferences listItem-border'
|
||||
style={{
|
||||
display: 'block',
|
||||
margin: 0,
|
||||
padding: 0
|
||||
}}
|
||||
>
|
||||
<div className='listItem'>
|
||||
<span className='material-icons listItemIcon listItemIcon-transparent keyboard' aria-hidden='true' />
|
||||
<div className='listItemBody'>
|
||||
<div className='listItemBodyText'>
|
||||
{globalize.translate('Controls')}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</LinkButton>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{isLoggedInUser && user.Policy?.IsAdministrator && !layoutManager.tv && (
|
||||
<div className='adminSection verticalSection verticalSection-extrabottompadding'>
|
||||
<h2
|
||||
className='sectionTitle headerUsername'
|
||||
style={{
|
||||
paddingLeft: '0.25em'
|
||||
}}
|
||||
>
|
||||
{globalize.translate('HeaderAdmin')}
|
||||
</h2>
|
||||
|
||||
<LinkButton
|
||||
href='#/dashboard'
|
||||
className='listItem-border'
|
||||
style={{
|
||||
display: 'block',
|
||||
margin: 0,
|
||||
padding: 0
|
||||
}}
|
||||
>
|
||||
<div className='listItem'>
|
||||
<span className='material-icons listItemIcon listItemIcon-transparent dashboard' aria-hidden='true' />
|
||||
<div className='listItemBody'>
|
||||
<div className='listItemBodyText'>
|
||||
{globalize.translate('TabDashboard')}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</LinkButton>
|
||||
|
||||
<LinkButton
|
||||
href='#/metadata'
|
||||
className='listItem-border'
|
||||
style={{
|
||||
display: 'block',
|
||||
margin: 0,
|
||||
padding: 0
|
||||
}}
|
||||
>
|
||||
<div className='listItem'>
|
||||
<span className='material-icons listItemIcon listItemIcon-transparent mode_edit' aria-hidden='true' />
|
||||
<div className='listItemBody'>
|
||||
<div className='listItemBodyText'>
|
||||
{globalize.translate('MetadataManager')}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</LinkButton>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{isLoggedInUser && (
|
||||
<div className='userSection verticalSection verticalSection-extrabottompadding'>
|
||||
<h2
|
||||
className='sectionTitle headerUsername'
|
||||
style={{
|
||||
paddingLeft: '0.25em'
|
||||
}}
|
||||
>
|
||||
{globalize.translate('HeaderUser')}
|
||||
</h2>
|
||||
|
||||
{appHost.supports('multiserver') && (
|
||||
<LinkButton
|
||||
onClick={Dashboard.selectServer}
|
||||
className='selectServer listItem-border'
|
||||
style={{
|
||||
display: 'block',
|
||||
margin: 0,
|
||||
padding: 0
|
||||
}}
|
||||
>
|
||||
<div className='listItem'>
|
||||
<span className='material-icons listItemIcon listItemIcon-transparent storage' aria-hidden='true' />
|
||||
<div className='listItemBody'>
|
||||
<div className='listItemBodyText'>
|
||||
{globalize.translate('SelectServer')}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</LinkButton>
|
||||
)}
|
||||
|
||||
<LinkButton
|
||||
onClick={Dashboard.logout}
|
||||
className='btnLogout listItem-border'
|
||||
style={{
|
||||
display: 'block',
|
||||
margin: 0,
|
||||
padding: 0
|
||||
}}
|
||||
>
|
||||
<div className='listItem'>
|
||||
<span className='material-icons listItemIcon listItemIcon-transparent exit_to_app' aria-hidden='true' />
|
||||
<div className='listItemBody'>
|
||||
<div className='listItemBodyText'>
|
||||
{globalize.translate('ButtonSignOut')}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</LinkButton>
|
||||
|
||||
{appHost.supports('exitmenu') && (
|
||||
<LinkButton
|
||||
onClick={appHost.exit}
|
||||
className='exitApp listItem-border'
|
||||
style={{
|
||||
display: 'block',
|
||||
margin: 0,
|
||||
padding: 0
|
||||
}}
|
||||
>
|
||||
<div className='listItem'>
|
||||
<span className='material-icons listItemIcon listItemIcon-transparent close' aria-hidden='true' />
|
||||
<div className='listItemBody'>
|
||||
<div className='listItemBodyText'>
|
||||
{globalize.translate('ButtonExitApp')}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</LinkButton>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</Page>
|
||||
);
|
||||
};
|
||||
|
||||
export default UserSettingsPage;
|
||||
Reference in New Issue
Block a user