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

Merge pull request #4556 from thornbill/routes-cleanup

Cleanup routes
This commit is contained in:
Bill Thornton 2023-05-04 13:02:33 -04:00 committed by GitHub
commit d7d0d7305c
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
80 changed files with 228 additions and 2044 deletions

View file

@ -3,7 +3,7 @@ import { History } from '@remix-run/router';
import React from 'react';
import StableApp from './apps/stable/App';
import { HistoryRouter } from './components/HistoryRouter';
import { HistoryRouter } from './components/router/HistoryRouter';
import { ApiProvider } from './hooks/useApi';
const ExperimentalApp = loadable(() => import('./apps/experimental/App'));

View file

@ -1,8 +1,8 @@
import { AsyncRoute } from '../../../../components/router/AsyncRoute';
import { AsyncRoute, AsyncRouteType } from '../../../../components/router/AsyncRoute';
export const ASYNC_USER_ROUTES: AsyncRoute[] = [
{ path: 'search.html', page: 'search' },
{ path: 'userprofile.html', page: 'user/userprofile' },
{ path: 'home.html', page: 'home' },
{ path: 'movies.html', page: 'movies' }
{ path: 'home.html', page: 'home', type: AsyncRouteType.Experimental },
{ path: 'movies.html', page: 'movies', type: AsyncRouteType.Experimental }
];

View file

@ -1,15 +1,15 @@
import React, { FunctionComponent, useCallback, useEffect, useMemo, useRef } from 'react';
import { useSearchParams } from 'react-router-dom';
import globalize from '../scripts/globalize';
import LibraryMenu from '../scripts/libraryMenu';
import { clearBackdrop } from '../components/backdrop/backdrop';
import layoutManager from '../components/layoutManager';
import * as mainTabsManager from '../components/maintabsmanager';
import '../elements/emby-tabs/emby-tabs';
import '../elements/emby-button/emby-button';
import '../elements/emby-scroller/emby-scroller';
import Page from '../components/Page';
import globalize from '../../../scripts/globalize';
import LibraryMenu from '../../../scripts/libraryMenu';
import { clearBackdrop } from '../../../components/backdrop/backdrop';
import layoutManager from '../../../components/layoutManager';
import * as mainTabsManager from '../../../components/maintabsmanager';
import '../../../elements/emby-tabs/emby-tabs';
import '../../../elements/emby-button/emby-button';
import '../../../elements/emby-scroller/emby-scroller';
import Page from '../../../components/Page';
type OnResumeOptions = {
autoFocus?: boolean;
@ -65,7 +65,7 @@ const Home: FunctionComponent = () => {
depends = 'favorites';
}
return import(/* webpackChunkName: "[request]" */ `../controllers/${depends}`).then(({ default: controllerFactory }) => {
return import(/* webpackChunkName: "[request]" */ `../../../controllers/${depends}`).then(({ default: controllerFactory }) => {
let controller = tabControllers[index];
if (!controller) {

View file

@ -1,7 +1,7 @@
import React, { FC, useCallback } from 'react';
import ViewItemsContainer from '../../components/common/ViewItemsContainer';
import { LibraryViewProps } from '../../types/interface';
import ViewItemsContainer from '../../../../components/common/ViewItemsContainer';
import { LibraryViewProps } from '../../../../types/interface';
const CollectionsView: FC<LibraryViewProps> = ({ topParentId }) => {
const getBasekey = useCallback(() => {

View file

@ -1,7 +1,7 @@
import React, { FC, useCallback } from 'react';
import ViewItemsContainer from '../../components/common/ViewItemsContainer';
import { LibraryViewProps } from '../../types/interface';
import ViewItemsContainer from '../../../../components/common/ViewItemsContainer';
import { LibraryViewProps } from '../../../../types/interface';
const FavoritesView: FC<LibraryViewProps> = ({ topParentId }) => {
const getBasekey = useCallback(() => {

View file

@ -1,9 +1,9 @@
import type { BaseItemDtoQueryResult } from '@jellyfin/sdk/lib/generated-client';
import React, { FC, useCallback, useEffect, useState } from 'react';
import loading from '../../components/loading/loading';
import GenresItemsContainer from '../../components/common/GenresItemsContainer';
import { LibraryViewProps } from '../../types/interface';
import loading from '../../../../components/loading/loading';
import GenresItemsContainer from '../../../../components/common/GenresItemsContainer';
import { LibraryViewProps } from '../../../../types/interface';
const GenresView: FC<LibraryViewProps> = ({ topParentId }) => {
const [ itemsResult, setItemsResult ] = useState<BaseItemDtoQueryResult>({});

View file

@ -1,7 +1,7 @@
import React, { FC, useCallback } from 'react';
import ViewItemsContainer from '../../components/common/ViewItemsContainer';
import { LibraryViewProps } from '../../types/interface';
import ViewItemsContainer from '../../../../components/common/ViewItemsContainer';
import { LibraryViewProps } from '../../../../types/interface';
const MoviesView: FC<LibraryViewProps> = ({ topParentId }) => {
const getBasekey = useCallback(() => {

View file

@ -1,13 +1,13 @@
import type { BaseItemDto, BaseItemDtoQueryResult, RecommendationDto } from '@jellyfin/sdk/lib/generated-client';
import React, { FC, useCallback, useEffect, useRef, useState } from 'react';
import layoutManager from '../../components/layoutManager';
import loading from '../../components/loading/loading';
import dom from '../../scripts/dom';
import globalize from '../../scripts/globalize';
import RecommendationContainer from '../../components/common/RecommendationContainer';
import SectionContainer from '../../components/common/SectionContainer';
import { LibraryViewProps } from '../../types/interface';
import layoutManager from '../../../../components/layoutManager';
import loading from '../../../../components/loading/loading';
import dom from '../../../../scripts/dom';
import globalize from '../../../../scripts/globalize';
import RecommendationContainer from '../../../../components/common/RecommendationContainer';
import SectionContainer from '../../../../components/common/SectionContainer';
import { LibraryViewProps } from '../../../../types/interface';
const SuggestionsView: FC<LibraryViewProps> = ({ topParentId }) => {
const [ latestItems, setLatestItems ] = useState<BaseItemDto[]>([]);
@ -28,7 +28,7 @@ const SuggestionsView: FC<LibraryViewProps> = ({ topParentId }) => {
}, [enableScrollX]);
const autoFocus = useCallback((page) => {
import('../../components/autoFocuser').then(({ default: autoFocuser }) => {
import('../../../../components/autoFocuser').then(({ default: autoFocuser }) => {
autoFocuser.autoFocus(page);
});
}, []);

View file

@ -1,8 +1,8 @@
import React, { FC, useCallback } from 'react';
import ViewItemsContainer from '../../components/common/ViewItemsContainer';
import { LibraryViewProps } from '../../types/interface';
import ViewItemsContainer from '../../../../components/common/ViewItemsContainer';
import { LibraryViewProps } from '../../../../types/interface';
const TrailersView: FC<LibraryViewProps> = ({ topParentId }) => {
const getBasekey = useCallback(() => {

View file

@ -1,16 +1,16 @@
import '../../elements/emby-scroller/emby-scroller';
import '../../elements/emby-itemscontainer/emby-itemscontainer';
import '../../elements/emby-tabs/emby-tabs';
import '../../elements/emby-button/emby-button';
import '../../../../elements/emby-scroller/emby-scroller';
import '../../../../elements/emby-itemscontainer/emby-itemscontainer';
import '../../../../elements/emby-tabs/emby-tabs';
import '../../../../elements/emby-button/emby-button';
import React, { FC, useCallback, useEffect, useRef, useState } from 'react';
import { useSearchParams } from 'react-router-dom';
import * as mainTabsManager from '../../components/maintabsmanager';
import Page from '../../components/Page';
import globalize from '../../scripts/globalize';
import libraryMenu from '../../scripts/libraryMenu';
import * as userSettings from '../../scripts/settings/userSettings';
import * as mainTabsManager from '../../../../components/maintabsmanager';
import Page from '../../../../components/Page';
import globalize from '../../../../scripts/globalize';
import libraryMenu from '../../../../scripts/libraryMenu';
import * as userSettings from '../../../../scripts/settings/userSettings';
import CollectionsView from './CollectionsView';
import FavoritesView from './FavoritesView';
import GenresView from './GenresView';

View file

@ -5,7 +5,7 @@ import ConnectionRequired from '../../../components/ConnectionRequired';
import ServerContentPage from '../../../components/ServerContentPage';
import { toAsyncPageRoute } from '../../../components/router/AsyncRoute';
import { toViewManagerPageRoute } from '../../../components/router/LegacyRoute';
import { ASYNC_USER_ROUTES } from './asyncRoutes';
import { ASYNC_ADMIN_ROUTES, ASYNC_USER_ROUTES } from './asyncRoutes';
import { LEGACY_ADMIN_ROUTES, LEGACY_PUBLIC_ROUTES, LEGACY_USER_ROUTES } from './legacyRoutes';
export const AppRoutes = () => (
@ -19,6 +19,7 @@ export const AppRoutes = () => (
{/* Admin routes */}
<Route path='/' element={<ConnectionRequired isAdminRequired />}>
{ASYNC_ADMIN_ROUTES.map(toAsyncPageRoute)}
{LEGACY_ADMIN_ROUTES.map(toViewManagerPageRoute)}
<Route path='configurationpage' element={

View file

@ -0,0 +1,10 @@
import { AsyncRoute } from '../../../../components/router/AsyncRoute';
export const ASYNC_ADMIN_ROUTES: AsyncRoute[] = [
{ path: 'usernew.html', page: 'user/usernew' },
{ path: 'userprofiles.html', page: 'user/userprofiles' },
{ path: 'useredit.html', page: 'user/useredit' },
{ path: 'userlibraryaccess.html', page: 'user/userlibraryaccess' },
{ path: 'userparentalcontrol.html', page: 'user/userparentalcontrol' },
{ path: 'userpassword.html', page: 'user/userpassword' }
];

View file

@ -1 +1,2 @@
export * from './admin';
export * from './user';

View file

@ -1,5 +1,6 @@
import { AsyncRoute } from '../../../../components/router/AsyncRoute';
export const ASYNC_USER_ROUTES: AsyncRoute[] = [
{ path: 'search.html', page: 'search' }
{ path: 'search.html', page: 'search' },
{ path: 'userprofile.html', page: 'user/userprofile' }
];

View file

@ -193,41 +193,5 @@ export const LEGACY_ADMIN_ROUTES: LegacyRoute[] = [
view: 'dashboard/streaming.html',
controller: 'dashboard/streaming'
}
}, {
path: 'usernew.html',
pageProps: {
view: 'dashboard/users/usernew.html',
controller: 'dashboard/users/usernew'
}
}, {
path: 'userprofiles.html',
pageProps: {
view: 'dashboard/users/userprofiles.html',
controller: 'dashboard/users/userprofilespage'
}
}, {
path: 'useredit.html',
pageProps: {
view: 'dashboard/users/useredit.html',
controller: 'dashboard/users/useredit'
}
}, {
path: 'userlibraryaccess.html',
pageProps: {
view: 'dashboard/users/userlibraryaccess.html',
controller: 'dashboard/users/userlibraryaccess'
}
}, {
path: 'userparentalcontrol.html',
pageProps: {
view: 'dashboard/users/userparentalcontrol.html',
controller: 'dashboard/users/userparentalcontrol'
}
}, {
path: 'userpassword.html',
pageProps: {
view: 'dashboard/users/userpassword.html',
controller: 'dashboard/users/userpasswordpage'
}
}
];

View file

@ -92,12 +92,6 @@ export const LEGACY_USER_ROUTES: LegacyRoute[] = [
isNowPlayingBarEnabled: false,
isThemeMediaSupported: true
}
}, {
path: 'userprofile.html',
pageProps: {
controller: 'user/profile/index',
view: 'user/profile/index.html'
}
}, {
path: 'home.html',
pageProps: {

View file

@ -1,12 +1,12 @@
import React, { FunctionComponent, useState } from 'react';
import { useSearchParams } from 'react-router-dom';
import Page from '../components/Page';
import SearchFields from '../components/search/SearchFields';
import SearchResults from '../components/search/SearchResults';
import SearchSuggestions from '../components/search/SearchSuggestions';
import LiveTVSearchResults from '../components/search/LiveTVSearchResults';
import globalize from '../scripts/globalize';
import Page from '../../../components/Page';
import SearchFields from '../../../components/search/SearchFields';
import SearchResults from '../../../components/search/SearchResults';
import SearchSuggestions from '../../../components/search/SearchSuggestions';
import LiveTVSearchResults from '../../../components/search/LiveTVSearchResults';
import globalize from '../../../scripts/globalize';
const Search: FunctionComponent = () => {
const [ query, setQuery ] = useState<string>();

View file

@ -1,20 +1,21 @@
import type { SyncPlayUserAccessType, UserDto } from '@jellyfin/sdk/lib/generated-client';
import React, { FunctionComponent, useCallback, useEffect, useState, useRef } from 'react';
import Dashboard from '../../utils/dashboard';
import globalize from '../../scripts/globalize';
import LibraryMenu from '../../scripts/libraryMenu';
import ButtonElement from '../../elements/ButtonElement';
import CheckBoxElement from '../../elements/CheckBoxElement';
import InputElement from '../../elements/InputElement';
import LinkEditUserPreferences from '../../components/dashboard/users/LinkEditUserPreferences';
import SectionTitleContainer from '../../elements/SectionTitleContainer';
import SectionTabs from '../../components/dashboard/users/SectionTabs';
import loading from '../../components/loading/loading';
import toast from '../../components/toast/toast';
import { getParameterByName } from '../../utils/url';
import escapeHTML from 'escape-html';
import SelectElement from '../../elements/SelectElement';
import Page from '../../components/Page';
import Dashboard from '../../../../utils/dashboard';
import globalize from '../../../../scripts/globalize';
import LibraryMenu from '../../../../scripts/libraryMenu';
import ButtonElement from '../../../../elements/ButtonElement';
import CheckBoxElement from '../../../../elements/CheckBoxElement';
import InputElement from '../../../../elements/InputElement';
import LinkEditUserPreferences from '../../../../components/dashboard/users/LinkEditUserPreferences';
import SectionTitleContainer from '../../../../elements/SectionTitleContainer';
import SectionTabs from '../../../../components/dashboard/users/SectionTabs';
import loading from '../../../../components/loading/loading';
import toast from '../../../../components/toast/toast';
import { getParameterByName } from '../../../../utils/url';
import SelectElement from '../../../../elements/SelectElement';
import Page from '../../../../components/Page';
type ResetProvider = AuthProvider & {
checkedAttribute: string

View file

@ -1,17 +1,17 @@
import type { UserDto } from '@jellyfin/sdk/lib/generated-client';
import React, { FunctionComponent, useCallback, useEffect, useState, useRef } from 'react';
import loading from '../../components/loading/loading';
import libraryMenu from '../../scripts/libraryMenu';
import globalize from '../../scripts/globalize';
import toast from '../../components/toast/toast';
import SectionTabs from '../../components/dashboard/users/SectionTabs';
import ButtonElement from '../../elements/ButtonElement';
import { getParameterByName } from '../../utils/url';
import SectionTitleContainer from '../../elements/SectionTitleContainer';
import AccessContainer from '../../components/dashboard/users/AccessContainer';
import CheckBoxElement from '../../elements/CheckBoxElement';
import Page from '../../components/Page';
import loading from '../../../../components/loading/loading';
import libraryMenu from '../../../../scripts/libraryMenu';
import globalize from '../../../../scripts/globalize';
import toast from '../../../../components/toast/toast';
import SectionTabs from '../../../../components/dashboard/users/SectionTabs';
import ButtonElement from '../../../../elements/ButtonElement';
import { getParameterByName } from '../../../../utils/url';
import SectionTitleContainer from '../../../../elements/SectionTitleContainer';
import AccessContainer from '../../../../components/dashboard/users/AccessContainer';
import CheckBoxElement from '../../../../elements/CheckBoxElement';
import Page from '../../../../components/Page';
type ItemsArr = {
Name?: string;

View file

@ -1,15 +1,15 @@
import React, { FunctionComponent, useCallback, useEffect, useState, useRef } from 'react';
import Dashboard from '../../utils/dashboard';
import globalize from '../../scripts/globalize';
import loading from '../../components/loading/loading';
import toast from '../../components/toast/toast';
import SectionTitleContainer from '../../elements/SectionTitleContainer';
import InputElement from '../../elements/InputElement';
import ButtonElement from '../../elements/ButtonElement';
import AccessContainer from '../../components/dashboard/users/AccessContainer';
import CheckBoxElement from '../../elements/CheckBoxElement';
import Page from '../../components/Page';
import Dashboard from '../../../../utils/dashboard';
import globalize from '../../../../scripts/globalize';
import loading from '../../../../components/loading/loading';
import toast from '../../../../components/toast/toast';
import SectionTitleContainer from '../../../../elements/SectionTitleContainer';
import InputElement from '../../../../elements/InputElement';
import ButtonElement from '../../../../elements/ButtonElement';
import AccessContainer from '../../../../components/dashboard/users/AccessContainer';
import CheckBoxElement from '../../../../elements/CheckBoxElement';
import Page from '../../../../components/Page';
type userInput = {
Name?: string;

View file

@ -1,20 +1,21 @@
import type { AccessSchedule, ParentalRating, UserDto } from '@jellyfin/sdk/lib/generated-client';
import { DynamicDayOfWeek } from '@jellyfin/sdk/lib/generated-client/models/dynamic-day-of-week';
import React, { FunctionComponent, useCallback, useEffect, useState, useRef } from 'react';
import globalize from '../../scripts/globalize';
import LibraryMenu from '../../scripts/libraryMenu';
import AccessScheduleList from '../../components/dashboard/users/AccessScheduleList';
import BlockedTagList from '../../components/dashboard/users/BlockedTagList';
import ButtonElement from '../../elements/ButtonElement';
import SectionTitleContainer from '../../elements/SectionTitleContainer';
import SectionTabs from '../../components/dashboard/users/SectionTabs';
import loading from '../../components/loading/loading';
import toast from '../../components/toast/toast';
import { getParameterByName } from '../../utils/url';
import CheckBoxElement from '../../elements/CheckBoxElement';
import escapeHTML from 'escape-html';
import SelectElement from '../../elements/SelectElement';
import Page from '../../components/Page';
import globalize from '../../../../scripts/globalize';
import LibraryMenu from '../../../../scripts/libraryMenu';
import AccessScheduleList from '../../../../components/dashboard/users/AccessScheduleList';
import BlockedTagList from '../../../../components/dashboard/users/BlockedTagList';
import ButtonElement from '../../../../elements/ButtonElement';
import SectionTitleContainer from '../../../../elements/SectionTitleContainer';
import SectionTabs from '../../../../components/dashboard/users/SectionTabs';
import loading from '../../../../components/loading/loading';
import toast from '../../../../components/toast/toast';
import { getParameterByName } from '../../../../utils/url';
import CheckBoxElement from '../../../../elements/CheckBoxElement';
import SelectElement from '../../../../elements/SelectElement';
import Page from '../../../../components/Page';
type UnratedItem = {
name: string;
@ -235,7 +236,7 @@ const UserParentalControl: FunctionComponent = () => {
const showSchedulePopup = (schedule: AccessSchedule, index: number) => {
schedule = schedule || {};
import('../../components/accessSchedule/accessSchedule').then(({ default: accessschedule }) => {
import('../../../../components/accessSchedule/accessSchedule').then(({ default: accessschedule }) => {
accessschedule.show({
schedule: schedule
}).then(function (updatedSchedule) {
@ -268,7 +269,7 @@ const UserParentalControl: FunctionComponent = () => {
};
const showBlockedTagPopup = () => {
import('../../components/prompt/prompt').then(({ default: prompt }) => {
import('../../../../components/prompt/prompt').then(({ default: prompt }) => {
prompt({
label: globalize.translate('LabelTag')
}).then(function (value) {

View file

@ -1,10 +1,11 @@
import React, { FunctionComponent, useCallback, useEffect, useState } from 'react';
import SectionTabs from '../../components/dashboard/users/SectionTabs';
import UserPasswordForm from '../../components/dashboard/users/UserPasswordForm';
import { getParameterByName } from '../../utils/url';
import SectionTitleContainer from '../../elements/SectionTitleContainer';
import Page from '../../components/Page';
import loading from '../../components/loading/loading';
import SectionTabs from '../../../../components/dashboard/users/SectionTabs';
import UserPasswordForm from '../../../../components/dashboard/users/UserPasswordForm';
import { getParameterByName } from '../../../../utils/url';
import SectionTitleContainer from '../../../../elements/SectionTitleContainer';
import Page from '../../../../components/Page';
import loading from '../../../../components/loading/loading';
const UserPassword: FunctionComponent = () => {
const userId = getParameterByName('userId');

View file

@ -2,17 +2,17 @@ import type { UserDto } from '@jellyfin/sdk/lib/generated-client';
import { ImageType } from '@jellyfin/sdk/lib/generated-client/models/image-type';
import React, { FunctionComponent, useEffect, useState, useRef, useCallback } from 'react';
import Dashboard from '../../utils/dashboard';
import globalize from '../../scripts/globalize';
import LibraryMenu from '../../scripts/libraryMenu';
import { appHost } from '../../components/apphost';
import confirm from '../../components/confirm/confirm';
import ButtonElement from '../../elements/ButtonElement';
import UserPasswordForm from '../../components/dashboard/users/UserPasswordForm';
import loading from '../../components/loading/loading';
import toast from '../../components/toast/toast';
import { getParameterByName } from '../../utils/url';
import Page from '../../components/Page';
import Dashboard from '../../../../utils/dashboard';
import globalize from '../../../../scripts/globalize';
import LibraryMenu from '../../../../scripts/libraryMenu';
import { appHost } from '../../../../components/apphost';
import confirm from '../../../../components/confirm/confirm';
import ButtonElement from '../../../../elements/ButtonElement';
import UserPasswordForm from '../../../../components/dashboard/users/UserPasswordForm';
import loading from '../../../../components/loading/loading';
import toast from '../../../../components/toast/toast';
import { getParameterByName } from '../../../../utils/url';
import Page from '../../../../components/Page';
const UserProfile: FunctionComponent = () => {
const userId = getParameterByName('userId');

View file

@ -1,18 +1,19 @@
import type { UserDto } from '@jellyfin/sdk/lib/generated-client';
import React, { FunctionComponent, useEffect, useState, useRef } from 'react';
import Dashboard from '../../utils/dashboard';
import globalize from '../../scripts/globalize';
import loading from '../../components/loading/loading';
import dom from '../../scripts/dom';
import confirm from '../../components/confirm/confirm';
import UserCardBox from '../../components/dashboard/users/UserCardBox';
import SectionTitleContainer from '../../elements/SectionTitleContainer';
import '../../elements/emby-button/emby-button';
import '../../elements/emby-button/paper-icon-button-light';
import '../../components/cardbuilder/card.scss';
import '../../components/indicators/indicators.scss';
import '../../styles/flexstyles.scss';
import Page from '../../components/Page';
import Dashboard from '../../../../utils/dashboard';
import globalize from '../../../../scripts/globalize';
import loading from '../../../../components/loading/loading';
import dom from '../../../../scripts/dom';
import confirm from '../../../../components/confirm/confirm';
import UserCardBox from '../../../../components/dashboard/users/UserCardBox';
import SectionTitleContainer from '../../../../elements/SectionTitleContainer';
import '../../../../elements/emby-button/emby-button';
import '../../../../elements/emby-button/paper-icon-button-light';
import '../../../../components/cardbuilder/card.scss';
import '../../../../components/indicators/indicators.scss';
import '../../../../styles/flexstyles.scss';
import Page from '../../../../components/Page';
type MenuEntry = {
name?: string;
@ -75,7 +76,7 @@ const UserProfiles: FunctionComponent = () => {
icon: 'delete'
});
import('../../components/actionSheet/actionSheet').then(({ default: actionsheet }) => {
import('../../../../components/actionSheet/actionSheet').then(({ default: actionsheet }) => {
actionsheet.show({
items: menuItems,
positionTo: card,

View file

@ -1,17 +0,0 @@
import loadable from '@loadable/component';
interface AsyncPageProps {
/** The relative path to the page component in the routes directory. */
page: string
}
/**
* Page component that uses the loadable component library to load pages defined in the routes directory asynchronously
* with code splitting.
*/
const AsyncPage = loadable(
(props: AsyncPageProps) => import(/* webpackChunkName: "[request]" */ `../routes/${props.page}`),
{ cacheKey: (props: AsyncPageProps) => props.page }
);
export default AsyncPage;

View file

@ -3,7 +3,7 @@ import { Outlet, useLocation, useNavigate } from 'react-router-dom';
import type { ConnectResponse } from 'jellyfin-apiclient';
import alert from './alert';
import { appRouter } from './appRouter';
import { appRouter } from './router/appRouter';
import Loading from './loading/LoadingComponent';
import ServerConnections from './ServerConnections';
import globalize from '../scripts/globalize';

View file

@ -1,4 +1,4 @@
import { appRouter } from './appRouter';
import { appRouter } from './router/appRouter';
import browser from '../scripts/browser';
import dialog from './dialog/dialog';
import globalize from '../scripts/globalize';

View file

@ -22,7 +22,7 @@ import './card.scss';
import '../../elements/emby-button/paper-icon-button-light';
import '../guide/programs.scss';
import ServerConnections from '../ServerConnections';
import { appRouter } from '../appRouter';
import { appRouter } from '../router/appRouter';
const enableFocusTransform = !browser.slow && !browser.edge;

View file

@ -3,7 +3,7 @@ import dom from '../../scripts/dom';
import dialogHelper from '../dialogHelper/dialogHelper';
import loading from '../loading/loading';
import layoutManager from '../layoutManager';
import { appRouter } from '../appRouter';
import { appRouter } from '../router/appRouter';
import globalize from '../../scripts/globalize';
import '../../elements/emby-button/emby-button';
import '../../elements/emby-button/paper-icon-button-light';

View file

@ -5,7 +5,7 @@ import type { BaseItemDtoQueryResult } from '@jellyfin/sdk/lib/generated-client'
import escapeHTML from 'escape-html';
import React, { FC, useCallback, useEffect, useRef } from 'react';
import { appRouter } from '../appRouter';
import { appRouter } from '../router/appRouter';
import cardBuilder from '../cardbuilder/cardBuilder';
import layoutManager from '../layoutManager';
import lazyLoader from '../lazyLoader/lazyLoaderIntersectionObserver';

View file

@ -1,4 +1,4 @@
import { appRouter } from '../appRouter';
import { appRouter } from '../router/appRouter';
import browser from '../../scripts/browser';
import dialog from '../dialog/dialog';
import globalize from '../../scripts/globalize';

View file

@ -1,4 +1,4 @@
import { history } from '../appRouter';
import { history } from '../router/appRouter';
import focusManager from '../focusManager';
import browser from '../../scripts/browser';
import layoutManager from '../layoutManager';

View file

@ -1,5 +1,5 @@
import dom from '../scripts/dom';
import { appRouter } from './appRouter';
import { appRouter } from './router/appRouter';
import Dashboard from '../utils/dashboard';
import ServerConnections from './ServerConnections';

View file

@ -3,7 +3,7 @@ import cardBuilder from '../cardbuilder/cardBuilder';
import layoutManager from '../layoutManager';
import imageLoader from '../images/imageLoader';
import globalize from '../../scripts/globalize';
import { appRouter } from '../appRouter';
import { appRouter } from '../router/appRouter';
import imageHelper from '../../scripts/imagehelper';
import '../../elements/emby-button/paper-icon-button-light';
import '../../elements/emby-itemscontainer/emby-itemscontainer';

View file

@ -4,7 +4,7 @@ import dom from '../scripts/dom';
import globalize from '../scripts/globalize';
import actionsheet from './actionSheet/actionSheet';
import { appHost } from './apphost';
import { appRouter } from './appRouter';
import { appRouter } from './router/appRouter';
import itemHelper from './itemHelper';
import { playbackManager } from './playback/playbackmanager';
import ServerConnections from './ServerConnections';

View file

@ -1,7 +1,7 @@
import escapeHtml from 'escape-html';
import datetime from '../../scripts/datetime';
import globalize from '../../scripts/globalize';
import { appRouter } from '../appRouter';
import { appRouter } from '../router/appRouter';
import itemHelper from '../itemHelper';
import indicators from '../indicators/indicators';
import 'material-design-icons-iconfont';

View file

@ -20,7 +20,7 @@ import '../../styles/flexstyles.scss';
import './style.scss';
import ServerConnections from '../ServerConnections';
import toast from '../toast/toast';
import { appRouter } from '../appRouter';
import { appRouter } from '../router/appRouter';
import template from './metadataEditor.template.html';
let currentContext;

View file

@ -15,7 +15,7 @@ import appFooter from '../appFooter/appFooter';
import itemShortcuts from '../shortcuts';
import './nowPlayingBar.scss';
import '../../elements/emby-slider/emby-slider';
import { appRouter } from '../appRouter';
import { appRouter } from '../router/appRouter';
let currentPlayer;
let currentPlayerSupportedCommands = [];

View file

@ -4,7 +4,7 @@ import browser from '../../scripts/browser';
import loading from '../loading/loading';
import { playbackManager } from '../playback/playbackmanager';
import { pluginManager } from '../pluginManager';
import { appRouter } from '../appRouter';
import { appRouter } from '../router/appRouter';
import globalize from '../../scripts/globalize';
import { appHost } from '../apphost';
import { enable, isEnabled, supported } from '../../scripts/autocast';

View file

@ -6,7 +6,7 @@ import layoutManager from '../layoutManager';
import { playbackManager } from '../playback/playbackmanager';
import { pluginManager } from '../pluginManager';
import * as userSettings from '../../scripts/settings/userSettings';
import { appRouter } from '../appRouter';
import { appRouter } from '../router/appRouter';
import globalize from '../../scripts/globalize';
import { PluginType } from '../../types/plugin.ts';

View file

@ -4,7 +4,7 @@ import loading from './loading/loading';
import appSettings from '../scripts/settings/appSettings';
import { playbackManager } from './playback/playbackmanager';
import { appHost } from '../components/apphost';
import { appRouter } from '../components/appRouter';
import { appRouter } from './router/appRouter';
import * as inputManager from '../scripts/inputManager';
import toast from '../components/toast/toast';
import confirm from '../components/confirm/confirm';

View file

@ -18,7 +18,7 @@ import './remotecontrol.scss';
import '../../elements/emby-ratingbutton/emby-ratingbutton';
import ServerConnections from '../ServerConnections';
import toast from '../toast/toast';
import { appRouter } from '../appRouter';
import { appRouter } from '../router/appRouter';
let showMuteButton = true;
let showVolumeSlider = true;

View file

@ -1,19 +1,44 @@
import loadable from '@loadable/component';
import React from 'react';
import { Route } from 'react-router-dom';
import AsyncPage from '../AsyncPage';
export enum AsyncRouteType {
Stable,
Experimental
}
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. */
type?: AsyncRouteType
}
export const toAsyncPageRoute = (route: AsyncRoute) => (
interface AsyncPageProps {
/** The relative path to the page component in the routes directory. */
page: string
}
const ExperimentalAsyncPage = loadable(
(props: { page: string }) => import(/* webpackChunkName: "[request]" */ `../../apps/experimental/routes/${props.page}`),
{ cacheKey: (props: AsyncPageProps) => props.page }
);
const StableAsyncPage = loadable(
(props: { page: string }) => import(/* webpackChunkName: "[request]" */ `../../apps/stable/routes/${props.page}`),
{ cacheKey: (props: AsyncPageProps) => props.page }
);
export const toAsyncPageRoute = ({ path, page, type = AsyncRouteType.Stable }: AsyncRoute) => (
<Route
key={route.path}
path={route.path}
element={<AsyncPage page={route.page} />}
key={path}
path={path}
element={(
type === AsyncRouteType.Experimental ?
<ExperimentalAsyncPage page={page} /> :
<StableAsyncPage page={page} />
)}
/>
);

View file

@ -1,15 +1,15 @@
import { Action, createHashHistory } from 'history';
import { appHost } from './apphost';
import { clearBackdrop, setBackdropTransparency } from './backdrop/backdrop';
import globalize from '../scripts/globalize';
import Events from '../utils/events.ts';
import itemHelper from './itemHelper';
import loading from './loading/loading';
import viewManager from './viewManager/viewManager';
import ServerConnections from './ServerConnections';
import alert from './alert';
import { ConnectionState } from '../utils/jellyfin-apiclient/ConnectionState.ts';
import { appHost } from '../apphost';
import { clearBackdrop, setBackdropTransparency } from '../backdrop/backdrop';
import globalize from '../../scripts/globalize';
import Events from '../../utils/events.ts';
import itemHelper from '../itemHelper';
import loading from '../loading/loading';
import viewManager from '../viewManager/viewManager';
import ServerConnections from '../ServerConnections';
import alert from '../alert';
import { ConnectionState } from '../../utils/jellyfin-apiclient/ConnectionState.ts';
export const history = createHashHistory();
@ -247,7 +247,7 @@ class AppRouter {
url = apiClient.getUrl(`/web${url}`);
promise = apiClient.get(url);
} else {
promise = import(/* webpackChunkName: "[request]" */ `../controllers/${url}`);
promise = import(/* webpackChunkName: "[request]" */ `../../controllers/${url}`);
}
promise.then((html) => {
@ -267,7 +267,7 @@ class AppRouter {
};
if (route.controller) {
import(/* webpackChunkName: "[request]" */ '../controllers/' + route.controller).then(onInitComplete);
import(/* webpackChunkName: "[request]" */ '../../controllers/' + route.controller).then(onInitComplete);
} else {
onInitComplete();
}

View file

@ -5,7 +5,7 @@ import { ItemSortBy } from '@jellyfin/sdk/lib/models/api/item-sort-by';
import escapeHtml from 'escape-html';
import React, { FunctionComponent, useEffect, useState } from 'react';
import { appRouter } from '../appRouter';
import { appRouter } from '../router/appRouter';
import { useApi } from '../../hooks/useApi';
import globalize from '../../scripts/globalize';

View file

@ -5,7 +5,7 @@
import { playbackManager } from './playback/playbackmanager';
import inputManager from '../scripts/inputManager';
import { appRouter } from './appRouter';
import { appRouter } from './router/appRouter';
import globalize from '../scripts/globalize';
import dom from '../scripts/dom';
import recordingHelper from './recordingcreator/recordinghelper';

View file

@ -1,198 +0,0 @@
<div id="editUserPage" data-role="page" class="page type-interior">
<div>
<div class="content-primary">
<div class="verticalSection">
<div class="sectionTitleContainer flex align-items-center">
<h2 class="sectionTitle username"></h2>
<a is="emby-linkbutton" rel="noopener noreferrer" class="raised button-alt headerHelpButton" target="_blank" href="https://jellyfin.org/docs/general/server/users/">${Help}</a>
</div>
</div>
<div data-role="controlgroup" data-type="horizontal" class="localnav" id="userProfileNavigation" data-mini="true">
<a href="#" is="emby-linkbutton" data-role="button" class="ui-btn-active">${Profile}</a>
<a href="#" is="emby-linkbutton" data-role="button" onclick="Dashboard.navigate('userlibraryaccess.html', true);">${TabAccess}</a>
<a href="#" is="emby-linkbutton" data-role="button" onclick="Dashboard.navigate('userparentalcontrol.html', true);">${TabParentalControl}</a>
<a href="#" is="emby-linkbutton" data-role="button" onclick="Dashboard.navigate('userpassword.html', true);">${HeaderPassword}</a>
</div>
<p class="lnkEditUserPreferencesContainer">
<a class="lnkEditUserPreferences button-link" href="#" is="emby-linkbutton">${ButtonEditOtherUserPreferences}</a>
</p>
<form class="editUserProfileForm">
<div class="disabledUserBanner" style="display: none;">
<div class="btn btnDarkAccent btnStatic">
<div>
${HeaderThisUserIsCurrentlyDisabled}
</div>
<div style="margin-top: 5px;">
${MessageReenableUser}
</div>
</div>
</div>
<div id="fldUserName" class="inputContainer">
<input is="emby-input" id="txtUserName" required type="text" label="${LabelName}" />
</div>
<div class="selectContainer fldSelectLoginProvider hide">
<select class="selectLoginProvider" is="emby-select" label="${LabelAuthProvider}"></select>
<div class="fieldDescription">${AuthProviderHelp}</div>
</div>
<div class="selectContainer fldSelectPasswordResetProvider hide">
<select class="selectPasswordResetProvider" is="emby-select" label="${LabelPasswordResetProvider}"></select>
<div class="fieldDescription">${PasswordResetProviderHelp}</div>
</div>
<div class="checkboxContainer checkboxContainer-withDescription fldRemoteAccess hide">
<label>
<input type="checkbox" is="emby-checkbox" id="chkRemoteAccess" />
<span>${AllowRemoteAccess}</span>
</label>
<div class="fieldDescription checkboxFieldDescription">${AllowRemoteAccessHelp}</div>
</div>
<label class="checkboxContainer">
<input type="checkbox" is="emby-checkbox" id="chkIsAdmin" />
<span>${OptionAllowUserToManageServer}</span>
</label>
<label class="checkboxContainer">
<input type="checkbox" is="emby-checkbox" id="chkEnableCollectionManagement" />
<span>${AllowCollectionManagement}</span>
</label>
<div id="featureAccessFields" class="verticalSection">
<h2 class="paperListLabel">${HeaderFeatureAccess}</h2>
<div class="checkboxList paperList" style="padding:.5em 1em;">
<label>
<input type="checkbox" is="emby-checkbox" id="chkEnableLiveTvAccess" />
<span>${OptionAllowBrowsingLiveTv}</span>
</label>
<label>
<input type="checkbox" is="emby-checkbox" id="chkManageLiveTv" />
<span>${OptionAllowManageLiveTv}</span>
</label>
</div>
</div>
<div class="verticalSection">
<h2 class="paperListLabel">${HeaderPlayback}</h2>
<div class="checkboxList paperList" style="padding:.5em 1em;">
<label>
<input type="checkbox" is="emby-checkbox" id="chkEnableMediaPlayback" />
<span>${OptionAllowMediaPlayback}</span>
</label>
<label>
<input type="checkbox" is="emby-checkbox" id="chkEnableAudioPlaybackTranscoding" />
<span>${OptionAllowAudioPlaybackTranscoding}</span>
</label>
<label>
<input type="checkbox" is="emby-checkbox" id="chkEnableVideoPlaybackTranscoding" />
<span>${OptionAllowVideoPlaybackTranscoding}</span>
</label>
<label>
<input type="checkbox" is="emby-checkbox" id="chkEnableVideoPlaybackRemuxing" />
<span>${OptionAllowVideoPlaybackRemuxing}</span>
</label>
<label>
<input type="checkbox" is="emby-checkbox" id="chkForceRemoteSourceTranscoding" />
<span>${OptionForceRemoteSourceTranscoding}</span>
</label>
</div>
<div class="fieldDescription">${OptionAllowMediaPlaybackTranscodingHelp}</div>
</div>
<br />
<div class="verticalSection">
<div class="inputContainer">
<input is="emby-input" type="number" id="txtRemoteClientBitrateLimit" inputmode="decimal" pattern="[0-9]*(\.[0-9]+)?" min="0" step=".25" label="${LabelRemoteClientBitrateLimit}" />
<div class="fieldDescription">${LabelRemoteClientBitrateLimitHelp}</div>
<div class="fieldDescription">${LabelUserRemoteClientBitrateLimitHelp}</div>
</div>
</div>
<div class="verticalSection">
<div class="selectContainer fldSelectSyncPlayAccess">
<select class="selectSyncPlayAccess" is="emby-select" id="selectSyncPlayAccess" label="${LabelSyncPlayAccess}">
<option value="CreateAndJoinGroups">${LabelSyncPlayAccessCreateAndJoinGroups}</option>
<option value="JoinGroups">${LabelSyncPlayAccessJoinGroups}</option>
<option value="None">${LabelSyncPlayAccessNone}</option>
</select>
<div class="fieldDescription">${SyncPlayAccessHelp}</div>
</div>
</div>
<div class="verticalSection">
<h2 class="checkboxListLabel" style="margin-bottom:1em;">${HeaderAllowMediaDeletionFrom}</h2>
<div class="checkboxList paperList checkboxList-paperList">
<label class="checkboxContainer">
<input type="checkbox" is="emby-checkbox" id="chkEnableDeleteAllFolders" />
<span>${AllLibraries}</span>
</label>
<div class="deleteAccess">
</div>
</div>
</div>
<div class="verticalSection">
<h2 class="checkboxListLabel">${HeaderRemoteControl}</h2>
<div class="checkboxList paperList" style="padding:.5em 1em;">
<label>
<input type="checkbox" is="emby-checkbox" id="chkEnableRemoteControlOtherUsers" />
<span>${OptionAllowRemoteControlOthers}</span>
</label>
<label>
<input type="checkbox" is="emby-checkbox" id="chkRemoteControlSharedDevices" />
<span>${OptionAllowRemoteSharedDevices}</span>
</label>
</div>
<div class="fieldDescription">${OptionAllowRemoteSharedDevicesHelp}</div>
</div>
<h2 class="checkboxListLabel">${Other}</h2>
<div class="checkboxContainer checkboxContainer-withDescription">
<label>
<input type="checkbox" is="emby-checkbox" id="chkEnableDownloading" />
<span>${OptionAllowContentDownload}</span>
</label>
<div class="fieldDescription checkboxFieldDescription">${OptionAllowContentDownloadHelp}</div>
</div>
<div class="checkboxContainer checkboxContainer-withDescription" id="fldIsEnabled">
<label>
<input type="checkbox" is="emby-checkbox" id="chkDisabled" />
<span>${OptionDisableUser}</span>
</label>
<div class="fieldDescription checkboxFieldDescription">${OptionDisableUserHelp}</div>
</div>
<div class="checkboxContainer checkboxContainer-withDescription" id="fldIsHidden">
<label>
<input type="checkbox" is="emby-checkbox" id="chkIsHidden" />
<span>${OptionHideUser}</span>
</label>
<div class="fieldDescription checkboxFieldDescription">${OptionHideUserFromLoginHelp}</div>
</div>
<br/>
<div class=verticalSection>
<div class="inputContainer" id="fldLoginAttemptsBeforeLockout">
<input is="emby-input" type="number" id="txtLoginAttemptsBeforeLockout" min="-1" step="1" label="${LabelUserLoginAttemptsBeforeLockout}"/>
<div class="fieldDescription">${OptionLoginAttemptsBeforeLockout}</div>
<div class="fieldDescription">${OptionLoginAttemptsBeforeLockoutHelp}</div>
</div>
</div>
<br />
<div class=verticalSection>
<div class="inputContainer" id="fldMaxActiveSessions">
<input is="emby-input" type="number" id="txtMaxActiveSessions" min="0" step="1" label="${LabelUserMaxActiveSessions}"/>
<div class="fieldDescription">${OptionMaxActiveSessions}</div>
<div class="fieldDescription">${OptionMaxActiveSessionsHelp}</div>
</div>
</div>
<br />
<div>
<button is="emby-button" type="submit" class="raised button-submit block">
<span>${Save}</span>
</button>
<button is="emby-button" type="button" class="raised button-cancel block btnCancel" onclick="history.back();">
<span>${ButtonCancel}</span>
</button>
</div>
</form>
</div>
</div>
</div>

View file

@ -1,196 +0,0 @@
import 'jquery';
import loading from '../../../components/loading/loading';
import libraryMenu from '../../../scripts/libraryMenu';
import globalize from '../../../scripts/globalize';
import Dashboard from '../../../utils/dashboard';
import toast from '../../../components/toast/toast';
import { getParameterByName } from '../../../utils/url.ts';
function loadDeleteFolders(page, user, mediaFolders) {
ApiClient.getJSON(ApiClient.getUrl('Channels', {
SupportsMediaDeletion: true
})).then(function (channelsResult) {
let isChecked;
let checkedAttribute;
let html = '';
for (const folder of mediaFolders) {
isChecked = user.Policy.EnableContentDeletion || user.Policy.EnableContentDeletionFromFolders.indexOf(folder.Id) != -1;
checkedAttribute = isChecked ? ' checked="checked"' : '';
html += '<label><input type="checkbox" is="emby-checkbox" class="chkFolder" data-id="' + folder.Id + '" ' + checkedAttribute + '><span>' + folder.Name + '</span></label>';
}
for (const folder of channelsResult.Items) {
isChecked = user.Policy.EnableContentDeletion || user.Policy.EnableContentDeletionFromFolders.indexOf(folder.Id) != -1;
checkedAttribute = isChecked ? ' checked="checked"' : '';
html += '<label><input type="checkbox" is="emby-checkbox" class="chkFolder" data-id="' + folder.Id + '" ' + checkedAttribute + '><span>' + folder.Name + '</span></label>';
}
$('.deleteAccess', page).html(html).trigger('create');
$('#chkEnableDeleteAllFolders', page).prop('checked', user.Policy.EnableContentDeletion);
});
}
function loadAuthProviders(page, user, providers) {
if (providers.length > 1) {
page.querySelector('.fldSelectLoginProvider').classList.remove('hide');
} else {
page.querySelector('.fldSelectLoginProvider').classList.add('hide');
}
const currentProviderId = user.Policy.AuthenticationProviderId;
page.querySelector('.selectLoginProvider').innerHTML = providers.map(function (provider) {
const selected = provider.Id === currentProviderId || providers.length < 2 ? ' selected' : '';
return '<option value="' + provider.Id + '"' + selected + '>' + provider.Name + '</option>';
});
}
function loadPasswordResetProviders(page, user, providers) {
if (providers.length > 1) {
page.querySelector('.fldSelectPasswordResetProvider').classList.remove('hide');
} else {
page.querySelector('.fldSelectPasswordResetProvider').classList.add('hide');
}
const currentProviderId = user.Policy.PasswordResetProviderId;
page.querySelector('.selectPasswordResetProvider').innerHTML = providers.map(function (provider) {
const selected = provider.Id === currentProviderId || providers.length < 2 ? ' selected' : '';
return '<option value="' + provider.Id + '"' + selected + '>' + provider.Name + '</option>';
});
}
function loadUser(page, user) {
ApiClient.getJSON(ApiClient.getUrl('Auth/Providers')).then(function (providers) {
loadAuthProviders(page, user, providers);
});
ApiClient.getJSON(ApiClient.getUrl('Auth/PasswordResetProviders')).then(function (providers) {
loadPasswordResetProviders(page, user, providers);
});
ApiClient.getJSON(ApiClient.getUrl('Library/MediaFolders', {
IsHidden: false
})).then(function (folders) {
loadDeleteFolders(page, user, folders.Items);
});
if (user.Policy.IsDisabled) {
$('.disabledUserBanner', page).show();
} else {
$('.disabledUserBanner', page).hide();
}
$('#txtUserName', page).prop('disabled', '').removeAttr('disabled');
$('#fldConnectInfo', page).show();
$('.lnkEditUserPreferences', page).attr('href', 'mypreferencesmenu.html?userId=' + user.Id);
libraryMenu.setTitle(user.Name);
page.querySelector('.username').innerHTML = user.Name;
$('#txtUserName', page).val(user.Name);
$('#chkIsAdmin', page).prop('checked', user.Policy.IsAdministrator);
$('#chkDisabled', page).prop('checked', user.Policy.IsDisabled);
$('#chkIsHidden', page).prop('checked', user.Policy.IsHidden);
$('#chkEnableCollectionManagement', page).prop('checked', user.Policy.chkEnableCollectionManagement);
$('#chkRemoteControlSharedDevices', page).prop('checked', user.Policy.EnableSharedDeviceControl);
$('#chkEnableRemoteControlOtherUsers', page).prop('checked', user.Policy.EnableRemoteControlOfOtherUsers);
$('#chkEnableDownloading', page).prop('checked', user.Policy.EnableContentDownloading);
$('#chkManageLiveTv', page).prop('checked', user.Policy.EnableLiveTvManagement);
$('#chkEnableLiveTvAccess', page).prop('checked', user.Policy.EnableLiveTvAccess);
$('#chkEnableMediaPlayback', page).prop('checked', user.Policy.EnableMediaPlayback);
$('#chkEnableAudioPlaybackTranscoding', page).prop('checked', user.Policy.EnableAudioPlaybackTranscoding);
$('#chkEnableVideoPlaybackTranscoding', page).prop('checked', user.Policy.EnableVideoPlaybackTranscoding);
$('#chkEnableVideoPlaybackRemuxing', page).prop('checked', user.Policy.EnablePlaybackRemuxing);
$('#chkForceRemoteSourceTranscoding', page).prop('checked', user.Policy.ForceRemoteSourceTranscoding);
$('#chkRemoteAccess', page).prop('checked', user.Policy.EnableRemoteAccess == null || user.Policy.EnableRemoteAccess);
$('#txtRemoteClientBitrateLimit', page).val(user.Policy.RemoteClientBitrateLimit / 1e6 || '');
$('#txtLoginAttemptsBeforeLockout', page).val(user.Policy.LoginAttemptsBeforeLockout || '0');
$('#txtMaxActiveSessions', page).val(user.Policy.MaxActiveSessions || '0');
if (ApiClient.isMinServerVersion('10.6.0')) {
$('#selectSyncPlayAccess').val(user.Policy.SyncPlayAccess);
}
loading.hide();
}
function onSaveComplete() {
Dashboard.navigate('userprofiles.html');
loading.hide();
toast(globalize.translate('SettingsSaved'));
}
function saveUser(user, page) {
user.Name = $('#txtUserName', page).val();
user.Policy.IsAdministrator = $('#chkIsAdmin', page).is(':checked');
user.Policy.IsHidden = $('#chkIsHidden', page).is(':checked');
user.Policy.IsDisabled = $('#chkDisabled', page).is(':checked');
user.Policy.EnableRemoteControlOfOtherUsers = $('#chkEnableRemoteControlOtherUsers', page).is(':checked');
user.Policy.EnableLiveTvManagement = $('#chkManageLiveTv', page).is(':checked');
user.Policy.EnableLiveTvAccess = $('#chkEnableLiveTvAccess', page).is(':checked');
user.Policy.EnableSharedDeviceControl = $('#chkRemoteControlSharedDevices', page).is(':checked');
user.Policy.EnableMediaPlayback = $('#chkEnableMediaPlayback', page).is(':checked');
user.Policy.EnableAudioPlaybackTranscoding = $('#chkEnableAudioPlaybackTranscoding', page).is(':checked');
user.Policy.EnableVideoPlaybackTranscoding = $('#chkEnableVideoPlaybackTranscoding', page).is(':checked');
user.Policy.EnablePlaybackRemuxing = $('#chkEnableVideoPlaybackRemuxing', page).is(':checked');
user.Policy.EnableCollectionManagement = $('#chkEnableCollectionManagement', page).is(':checked');
user.Policy.ForceRemoteSourceTranscoding = $('#chkForceRemoteSourceTranscoding', page).is(':checked');
user.Policy.EnableContentDownloading = $('#chkEnableDownloading', page).is(':checked');
user.Policy.EnableRemoteAccess = $('#chkRemoteAccess', page).is(':checked');
user.Policy.RemoteClientBitrateLimit = parseInt(1e6 * parseFloat($('#txtRemoteClientBitrateLimit', page).val() || '0'), 10);
user.Policy.LoginAttemptsBeforeLockout = parseInt($('#txtLoginAttemptsBeforeLockout', page).val() || '0', 10);
user.Policy.MaxActiveSessions = parseInt($('#txtMaxActiveSessions', page).val() || '0', 10);
user.Policy.AuthenticationProviderId = page.querySelector('.selectLoginProvider').value;
user.Policy.PasswordResetProviderId = page.querySelector('.selectPasswordResetProvider').value;
user.Policy.EnableContentDeletion = $('#chkEnableDeleteAllFolders', page).is(':checked');
user.Policy.EnableContentDeletionFromFolders = user.Policy.EnableContentDeletion ? [] : $('.chkFolder', page).get().filter(function (c) {
return c.checked;
}).map(function (c) {
return c.getAttribute('data-id');
});
if (ApiClient.isMinServerVersion('10.6.0')) {
user.Policy.SyncPlayAccess = page.querySelector('#selectSyncPlayAccess').value;
}
ApiClient.updateUser(user).then(function () {
ApiClient.updateUserPolicy(user.Id, user.Policy).then(function () {
onSaveComplete();
});
});
}
function onSubmit() {
const page = $(this).parents('.page')[0];
loading.show();
getUser().then(function (result) {
saveUser(result, page);
});
return false;
}
function getUser() {
const userId = getParameterByName('userId');
return ApiClient.getUser(userId);
}
function loadData(page) {
loading.show();
getUser().then(function (user) {
loadUser(page, user);
});
}
$(document).on('pageinit', '#editUserPage', function () {
$('.editUserProfileForm').off('submit', onSubmit).on('submit', onSubmit);
const page = this;
$('#chkEnableDeleteAllFolders', this).on('change', function () {
if (this.checked) {
$('.deleteAccess', page).hide();
} else {
$('.deleteAccess', page).show();
}
});
ApiClient.getServerConfiguration().then(function (config) {
if (config.EnableRemoteAccess) {
page.querySelector('.fldRemoteAccess').classList.remove('hide');
} else {
page.querySelector('.fldRemoteAccess').classList.add('hide');
}
});
}).on('pagebeforeshow', '#editUserPage', function () {
loadData(this);
});

View file

@ -1,68 +0,0 @@
<div id="userLibraryAccessPage" data-role="page" class="page type-interior">
<div>
<div class="content-primary">
<div class="verticalSection">
<div class="sectionTitleContainer flex align-items-center">
<h2 class="sectionTitle username"></h2>
<a is="emby-linkbutton" rel="noopener noreferrer" class="raised button-alt headerHelpButton" target="_blank" href="https://jellyfin.org/docs/general/server/users/">${Help}</a>
</div>
</div>
<div data-role="controlgroup" data-type="horizontal" class="localnav" data-mini="true">
<a is="emby-linkbutton" href="#" data-role="button" onclick="Dashboard.navigate('useredit.html', true);">${Profile}</a>
<a is="emby-linkbutton" href="#" data-role="button" onclick="Dashboard.navigate('userlibraryaccess.html', true);" class="ui-btn-active">${TabAccess}</a>
<a is="emby-linkbutton" href="#" data-role="button" onclick="Dashboard.navigate('userparentalcontrol.html', true);">${TabParentalControl}</a>
<a is="emby-linkbutton" href="#" data-role="button" onclick="Dashboard.navigate('userpassword.html', true);">${HeaderPassword}</a>
</div>
<form class="userLibraryAccessForm">
<div class="folderAccessContainer">
<h2>${HeaderLibraryAccess}</h2>
<label class="checkboxContainer">
<input type="checkbox" is="emby-checkbox" id="chkEnableAllFolders" />
<span>${OptionEnableAccessToAllLibraries}</span>
</label>
<div class="folderAccessListContainer">
<div class="folderAccess">
</div>
<div class="fieldDescription">${LibraryAccessHelp}</div>
</div>
</div>
<div class="channelAccessContainer" style="display:none;">
<h2>${HeaderChannelAccess}</h2>
<label class="checkboxContainer">
<input type="checkbox" is="emby-checkbox" id="chkEnableAllChannels" />
<span>${OptionEnableAccessToAllChannels}</span>
</label>
<div class="channelAccessListContainer">
<div class="channelAccess">
</div>
<div class="fieldDescription">${ChannelAccessHelp}</div>
</div>
</div>
<br />
<div class="deviceAccessContainer hide">
<h2>${HeaderDeviceAccess}</h2>
<label class="checkboxContainer">
<input type="checkbox" is="emby-checkbox" id="chkEnableAllDevices" />
<span>${OptionEnableAccessFromAllDevices}</span>
</label>
<div class="deviceAccessListContainer">
<div class="deviceAccess">
</div>
<div class="fieldDescription">${DeviceAccessHelp}</div>
</div>
<br />
</div>
<br />
<div>
<button is="emby-button" type="submit" class="raised button-submit block">
<span>${Save}</span>
</button>
</div>
</form>
</div>
</div>
</div>

View file

@ -1,184 +0,0 @@
import 'jquery';
import loading from '../../../components/loading/loading';
import libraryMenu from '../../../scripts/libraryMenu';
import globalize from '../../../scripts/globalize';
import Dashboard from '../../../utils/dashboard';
import toast from '../../../components/toast/toast';
import { getParameterByName } from '../../../utils/url.ts';
function triggerChange(select) {
const evt = document.createEvent('HTMLEvents');
evt.initEvent('change', false, true);
select.dispatchEvent(evt);
}
function loadMediaFolders(page, user, mediaFolders) {
let html = '';
html += '<h3 class="checkboxListLabel">' + globalize.translate('HeaderLibraries') + '</h3>';
html += '<div class="checkboxList paperList checkboxList-paperList">';
for (let i = 0, length = mediaFolders.length; i < length; i++) {
const folder = mediaFolders[i];
const isChecked = user.Policy.EnableAllFolders || user.Policy.EnabledFolders.indexOf(folder.Id) != -1;
const checkedAttribute = isChecked ? ' checked="checked"' : '';
html += '<label><input type="checkbox" is="emby-checkbox" class="chkFolder" data-id="' + folder.Id + '" ' + checkedAttribute + '><span>' + folder.Name + '</span></label>';
}
html += '</div>';
page.querySelector('.folderAccess').innerHTML = html;
const chkEnableAllFolders = page.querySelector('#chkEnableAllFolders');
chkEnableAllFolders.checked = user.Policy.EnableAllFolders;
triggerChange(chkEnableAllFolders);
}
function loadChannels(page, user, channels) {
let html = '';
html += '<h3 class="checkboxListLabel">' + globalize.translate('Channels') + '</h3>';
html += '<div class="checkboxList paperList checkboxList-paperList">';
for (let i = 0, length = channels.length; i < length; i++) {
const folder = channels[i];
const isChecked = user.Policy.EnableAllChannels || user.Policy.EnabledChannels.indexOf(folder.Id) != -1;
const checkedAttribute = isChecked ? ' checked="checked"' : '';
html += '<label><input type="checkbox" is="emby-checkbox" class="chkChannel" data-id="' + folder.Id + '" ' + checkedAttribute + '><span>' + folder.Name + '</span></label>';
}
html += '</div>';
$('.channelAccess', page).show().html(html);
if (channels.length) {
$('.channelAccessContainer', page).show();
} else {
$('.channelAccessContainer', page).hide();
}
const chkEnableAllChannels = page.querySelector('#chkEnableAllChannels');
chkEnableAllChannels.checked = user.Policy.EnableAllChannels;
triggerChange(chkEnableAllChannels);
}
function loadDevices(page, user, devices) {
let html = '';
html += '<h3 class="checkboxListLabel">' + globalize.translate('HeaderDevices') + '</h3>';
html += '<div class="checkboxList paperList checkboxList-paperList">';
for (let i = 0, length = devices.length; i < length; i++) {
const device = devices[i];
const checkedAttribute = user.Policy.EnableAllDevices || user.Policy.EnabledDevices.indexOf(device.Id) != -1 ? ' checked="checked"' : '';
html += '<label><input type="checkbox" is="emby-checkbox" class="chkDevice" data-id="' + device.Id + '" ' + checkedAttribute + '><span>' + device.Name + ' - ' + device.AppName + '</span></label>';
}
html += '</div>';
$('.deviceAccess', page).show().html(html);
const chkEnableAllDevices = page.querySelector('#chkEnableAllDevices');
chkEnableAllDevices.checked = user.Policy.EnableAllDevices;
triggerChange(chkEnableAllDevices);
if (user.Policy.IsAdministrator) {
page.querySelector('.deviceAccessContainer').classList.add('hide');
} else {
page.querySelector('.deviceAccessContainer').classList.remove('hide');
}
}
function loadUser(page, user, loggedInUser, mediaFolders, channels, devices) {
page.querySelector('.username').innerHTML = user.Name;
libraryMenu.setTitle(user.Name);
loadChannels(page, user, channels);
loadMediaFolders(page, user, mediaFolders);
loadDevices(page, user, devices);
loading.hide();
}
function onSaveComplete() {
loading.hide();
toast(globalize.translate('SettingsSaved'));
}
function saveUser(user, page) {
user.Policy.EnableAllFolders = $('#chkEnableAllFolders', page).is(':checked');
user.Policy.EnabledFolders = user.Policy.EnableAllFolders ? [] : $('.chkFolder', page).get().filter(function (c) {
return c.checked;
}).map(function (c) {
return c.getAttribute('data-id');
});
user.Policy.EnableAllChannels = $('#chkEnableAllChannels', page).is(':checked');
user.Policy.EnabledChannels = user.Policy.EnableAllChannels ? [] : $('.chkChannel', page).get().filter(function (c) {
return c.checked;
}).map(function (c) {
return c.getAttribute('data-id');
});
user.Policy.EnableAllDevices = $('#chkEnableAllDevices', page).is(':checked');
user.Policy.EnabledDevices = user.Policy.EnableAllDevices ? [] : $('.chkDevice', page).get().filter(function (c) {
return c.checked;
}).map(function (c) {
return c.getAttribute('data-id');
});
user.Policy.BlockedChannels = null;
user.Policy.BlockedMediaFolders = null;
ApiClient.updateUserPolicy(user.Id, user.Policy).then(function () {
onSaveComplete();
});
}
function onSubmit() {
const page = $(this).parents('.page');
loading.show();
const userId = getParameterByName('userId');
ApiClient.getUser(userId).then(function (result) {
saveUser(result, page);
});
return false;
}
$(document).on('pageinit', '#userLibraryAccessPage', function () {
const page = this;
$('#chkEnableAllDevices', page).on('change', function () {
if (this.checked) {
$('.deviceAccessListContainer', page).hide();
} else {
$('.deviceAccessListContainer', page).show();
}
});
$('#chkEnableAllChannels', page).on('change', function () {
if (this.checked) {
$('.channelAccessListContainer', page).hide();
} else {
$('.channelAccessListContainer', page).show();
}
});
page.querySelector('#chkEnableAllFolders').addEventListener('change', function () {
if (this.checked) {
page.querySelector('.folderAccessListContainer').classList.add('hide');
} else {
page.querySelector('.folderAccessListContainer').classList.remove('hide');
}
});
$('.userLibraryAccessForm').off('submit', onSubmit).on('submit', onSubmit);
}).on('pageshow', '#userLibraryAccessPage', function () {
const page = this;
loading.show();
let promise1;
const userId = getParameterByName('userId');
if (userId) {
promise1 = ApiClient.getUser(userId);
} else {
const deferred = $.Deferred();
deferred.resolveWith(null, [{
Configuration: {}
}]);
promise1 = deferred.promise();
}
const promise2 = Dashboard.getCurrentUser();
const promise4 = ApiClient.getJSON(ApiClient.getUrl('Library/MediaFolders', {
IsHidden: false
}));
const promise5 = ApiClient.getJSON(ApiClient.getUrl('Channels'));
const promise6 = ApiClient.getJSON(ApiClient.getUrl('Devices'));
Promise.all([promise1, promise2, promise4, promise5, promise6]).then(function (responses) {
loadUser(page, responses[0], responses[1], responses[2].Items, responses[3].Items, responses[4].Items);
});
});

View file

@ -1,62 +0,0 @@
<div id="newUserPage" data-role="page" class="page type-interior">
<div>
<div class="content-primary">
<form class="newUserProfileForm">
<div class="verticalSection">
<div class="sectionTitleContainer flex align-items-center">
<h2 class="sectionTitle">${ButtonAddUser}</h2>
<a is="emby-linkbutton" rel="noopener noreferrer" class="raised button-alt headerHelpButton" target="_blank" href="https://jellyfin.org/docs/general/server/users/">${Help}</a>
</div>
<div class="inputContainer">
<input is="emby-input" id="txtUsername" required type="text" label="${LabelName}" />
</div>
<div class="inputContainer">
<input is="emby-input" id="txtPassword" type="password" label="${LabelPassword}" />
</div>
</div>
<div class="folderAccessContainer verticalSection">
<h2 class="sectionTitle">${HeaderLibraryAccess}</h2>
<div class="checkboxContainer checkboxContainer-withDescription">
<label>
<input type="checkbox" is="emby-checkbox" id="chkEnableAllFolders" />
<span>${OptionEnableAccessToAllLibraries}</span>
</label>
<div class="fieldDescription checkboxFieldDescription">${LibraryAccessHelp}</div>
</div>
<div class="folderAccessListContainer">
<div class="folderAccess">
</div>
</div>
</div>
<div class="channelAccessContainer verticalSection verticalSection-extrabottompadding" style="display:none;">
<h2 class="sectionTitle">${HeaderChannelAccess}</h2>
<div class="checkboxContainer checkboxContainer-withDescription">
<label>
<input type="checkbox" is="emby-checkbox" id="chkEnableAllChannels" />
<span>${OptionEnableAccessToAllChannels}</span>
</label>
<div class="fieldDescription checkboxFieldDescription">${ChannelAccessHelp}</div>
</div>
<div class="channelAccessListContainer">
<div class="channelAccess">
</div>
</div>
</div>
<div>
<button is="emby-button" type="submit" class="raised button-submit block">
<span>${Save}</span>
</button>
<button is="emby-button" type="button" class="raised button-cancel block btnCancel" onclick="history.back();">
<span>${ButtonCancel}</span>
</button>
</div>
</form>
</div>
</div>
</div>

View file

@ -1,128 +0,0 @@
import 'jquery';
import loading from '../../../components/loading/loading';
import globalize from '../../../scripts/globalize';
import '../../../elements/emby-checkbox/emby-checkbox';
import Dashboard from '../../../utils/dashboard';
import toast from '../../../components/toast/toast';
function loadMediaFolders(page, mediaFolders) {
let html = '';
html += '<h3 class="checkboxListLabel">' + globalize.translate('HeaderLibraries') + '</h3>';
html += '<div class="checkboxList paperList" style="padding:.5em 1em;">';
for (let i = 0; i < mediaFolders.length; i++) {
const folder = mediaFolders[i];
html += '<label><input type="checkbox" is="emby-checkbox" class="chkFolder" data-id="' + folder.Id + '"/><span>' + folder.Name + '</span></label>';
}
html += '</div>';
$('.folderAccess', page).html(html).trigger('create');
$('#chkEnableAllFolders', page).prop('checked', false);
}
function loadChannels(page, channels) {
let html = '';
html += '<h3 class="checkboxListLabel">' + globalize.translate('Channels') + '</h3>';
html += '<div class="checkboxList paperList" style="padding:.5em 1em;">';
for (let i = 0; i < channels.length; i++) {
const folder = channels[i];
html += '<label><input type="checkbox" is="emby-checkbox" class="chkChannel" data-id="' + folder.Id + '"/><span>' + folder.Name + '</span></label>';
}
html += '</div>';
$('.channelAccess', page).show().html(html).trigger('create');
if (channels.length) {
$('.channelAccessContainer', page).show();
} else {
$('.channelAccessContainer', page).hide();
}
$('#chkEnableAllChannels', page).prop('checked', false);
}
function loadUser(page) {
$('#txtUsername', page).val('');
$('#txtPassword', page).val('');
loading.show();
const promiseFolders = ApiClient.getJSON(ApiClient.getUrl('Library/MediaFolders', {
IsHidden: false
}));
const promiseChannels = ApiClient.getJSON(ApiClient.getUrl('Channels'));
Promise.all([promiseFolders, promiseChannels]).then(function (responses) {
loadMediaFolders(page, responses[0].Items);
loadChannels(page, responses[1].Items);
loading.hide();
});
}
function saveUser(page) {
const _user = {
Name: $('#txtUsername', page).val(),
Password: $('#txtPassword', page).val()
};
ApiClient.createUser(_user).then(function (user) {
user.Policy.EnableAllFolders = $('#chkEnableAllFolders', page).is(':checked');
user.Policy.EnabledFolders = [];
if (!user.Policy.EnableAllFolders) {
user.Policy.EnabledFolders = $('.chkFolder', page).get().filter(function (i) {
return i.checked;
}).map(function (i) {
return i.getAttribute('data-id');
});
}
user.Policy.EnableAllChannels = $('#chkEnableAllChannels', page).is(':checked');
user.Policy.EnabledChannels = [];
if (!user.Policy.EnableAllChannels) {
user.Policy.EnabledChannels = $('.chkChannel', page).get().filter(function (i) {
return i.checked;
}).map(function (i) {
return i.getAttribute('data-id');
});
}
ApiClient.updateUserPolicy(user.Id, user.Policy).then(function () {
Dashboard.navigate('useredit.html?userId=' + user.Id);
});
}, function () {
toast(globalize.translate('ErrorDefault'));
loading.hide();
});
}
function onSubmit() {
const page = $(this).parents('.page')[0];
loading.show();
saveUser(page);
return false;
}
function loadData(page) {
loadUser(page);
}
$(document).on('pageinit', '#newUserPage', function () {
const page = this;
$('#chkEnableAllChannels', page).on('change', function () {
if (this.checked) {
$('.channelAccessListContainer', page).hide();
} else {
$('.channelAccessListContainer', page).show();
}
});
$('#chkEnableAllFolders', page).on('change', function () {
if (this.checked) {
$('.folderAccessListContainer', page).hide();
} else {
$('.folderAccessListContainer', page).show();
}
});
$('.newUserProfileForm').off('submit', onSubmit).on('submit', onSubmit);
}).on('pageshow', '#newUserPage', function () {
loadData(this);
});

View file

@ -1,60 +0,0 @@
<div id="userParentalControlPage" data-role="page" class="page type-interior">
<div>
<div class="content-primary">
<div class="verticalSection">
<div class="sectionTitleContainer flex align-items-center">
<h2 class="sectionTitle username"></h2>
<a is="emby-linkbutton" rel="noopener noreferrer" class="raised button-alt headerHelpButton" target="_blank" href="https://jellyfin.org/docs/general/server/users/">${Help}</a>
</div>
</div>
<div data-role="controlgroup" data-type="horizontal" class="localnav" data-mini="true">
<a is="emby-linkbutton" href="#" data-role="button" onclick="Dashboard.navigate('useredit.html', true);">${Profile}</a>
<a is="emby-linkbutton" href="#" data-role="button" onclick="Dashboard.navigate('userlibraryaccess.html', true);">${TabAccess}</a>
<a is="emby-linkbutton" href="#" data-role="button" onclick="Dashboard.navigate('userparentalcontrol.html', true);" class="ui-btn-active">${TabParentalControl}</a>
<a is="emby-linkbutton" href="#" data-role="button" onclick="Dashboard.navigate('userpassword.html', true);">${HeaderPassword}</a>
</div>
<form class="userParentalControlForm">
<div class="selectContainer">
<select is="emby-select" id="selectMaxParentalRating" label="${LabelMaxParentalRating}"></select>
<div class="fieldDescription">${MaxParentalRatingHelp}</div>
</div>
<div>
<div class="blockUnratedItems"></div>
</div>
<br />
<div class="verticalSection" style="margin-bottom:2em;">
<div class="detailSectionHeader sectionTitleContainer">
<h2 class="sectionTitle">${LabelBlockContentWithTags}</h2>
<button is="emby-button" type="button" class="fab btnAddBlockedTag submit" style="margin-left:1em;" title="${Add}">
<span class="material-icons add"></span>
</button>
</div>
<div class="blockedTags" style="margin-top:.5em;"></div>
</div>
<div class="accessScheduleSection verticalSection" style="margin-bottom:2em;">
<div class="sectionTitleContainer">
<h2 class="sectionTitle">${HeaderAccessSchedule}</h2>
<button is="emby-button" type="button" class="fab btnAddSchedule submit" style="margin-left:1em;" title="${Add}">
<span class="material-icons add"></span>
</button>
</div>
<p>${HeaderAccessScheduleHelp}</p>
<div class="accessScheduleList paperList"></div>
</div>
<div>
<button is="emby-button" type="submit" class="raised button-submit block">
<span>${Save}</span>
</button>
</div>
</form>
</div>
</div>
</div>

View file

@ -1,278 +0,0 @@
import 'jquery';
import datetime from '../../../scripts/datetime';
import loading from '../../../components/loading/loading';
import libraryMenu from '../../../scripts/libraryMenu';
import globalize from '../../../scripts/globalize';
import '../../../components/listview/listview.scss';
import '../../../elements/emby-button/paper-icon-button-light';
import toast from '../../../components/toast/toast';
import { getParameterByName } from '../../../utils/url.ts';
function populateRatings(allParentalRatings, page) {
let html = '';
html += "<option value=''></option>";
let rating;
const ratings = [];
for (let i = 0, length = allParentalRatings.length; i < length; i++) {
rating = allParentalRatings[i];
if (ratings.length) {
const lastRating = ratings[ratings.length - 1];
if (lastRating.Value === rating.Value) {
lastRating.Name += '/' + rating.Name;
continue;
}
}
ratings.push({
Name: rating.Name,
Value: rating.Value
});
}
for (let i = 0, length = ratings.length; i < length; i++) {
rating = ratings[i];
html += "<option value='" + rating.Value + "'>" + rating.Name + '</option>';
}
$('#selectMaxParentalRating', page).html(html);
}
function loadUnratedItems(page, user) {
const items = [{
name: globalize.translate('Books'),
value: 'Book'
}, {
name: globalize.translate('Channels'),
value: 'ChannelContent'
}, {
name: globalize.translate('LiveTV'),
value: 'LiveTvChannel'
}, {
name: globalize.translate('Movies'),
value: 'Movie'
}, {
name: globalize.translate('Music'),
value: 'Music'
}, {
name: globalize.translate('Trailers'),
value: 'Trailer'
}, {
name: globalize.translate('Shows'),
value: 'Series'
}];
let html = '';
html += '<h3 class="checkboxListLabel">' + globalize.translate('HeaderBlockItemsWithNoRating') + '</h3>';
html += '<div class="checkboxList paperList checkboxList-paperList">';
for (let i = 0, length = items.length; i < length; i++) {
const item = items[i];
const checkedAttribute = user.Policy.BlockUnratedItems.indexOf(item.value) != -1 ? ' checked="checked"' : '';
html += '<label><input type="checkbox" is="emby-checkbox" class="chkUnratedItem" data-itemtype="' + item.value + '" type="checkbox"' + checkedAttribute + '><span>' + item.name + '</span></label>';
}
html += '</div>';
$('.blockUnratedItems', page).html(html).trigger('create');
}
function loadUser(page, user, allParentalRatings) {
page.querySelector('.username').innerHTML = user.Name;
libraryMenu.setTitle(user.Name);
loadUnratedItems(page, user);
loadBlockedTags(page, user.Policy.BlockedTags);
populateRatings(allParentalRatings, page);
let ratingValue = '';
if (user.Policy.MaxParentalRating) {
for (let i = 0, length = allParentalRatings.length; i < length; i++) {
const rating = allParentalRatings[i];
if (user.Policy.MaxParentalRating >= rating.Value) {
ratingValue = rating.Value;
}
}
}
$('#selectMaxParentalRating', page).val(ratingValue);
if (user.Policy.IsAdministrator) {
$('.accessScheduleSection', page).hide();
} else {
$('.accessScheduleSection', page).show();
}
renderAccessSchedule(page, user.Policy.AccessSchedules || []);
loading.hide();
}
function loadBlockedTags(page, tags) {
let html = tags.map(function (h) {
let li = '<div class="listItem">';
li += '<div class="listItemBody">';
li += '<h3 class="listItemBodyText">';
li += h;
li += '</h3>';
li += '</div>';
li += '<button type="button" is="paper-icon-button-light" class="blockedTag btnDeleteTag listItemButton" data-tag="' + h + '"><span class="material-icons delete"></span></button>';
li += '</div>';
return li;
}).join('');
if (html) {
html = '<div class="paperList">' + html + '</div>';
}
const blockedTags = page.querySelector('.blockedTags');
blockedTags.innerHTML = html;
for (const btnDeleteTag of blockedTags.querySelectorAll('.btnDeleteTag')) {
btnDeleteTag.addEventListener('click', function () {
const tag = this.getAttribute('data-tag');
const newTags = tags.filter(function (t) {
return t != tag;
});
loadBlockedTags(page, newTags);
});
}
}
function deleteAccessSchedule(page, schedules, index) {
schedules.splice(index, 1);
renderAccessSchedule(page, schedules);
}
function renderAccessSchedule(page, schedules) {
let html = '';
let index = 0;
html += schedules.map(function (a) {
let itemHtml = '';
itemHtml += '<div class="liSchedule listItem" data-day="' + a.DayOfWeek + '" data-start="' + a.StartHour + '" data-end="' + a.EndHour + '">';
itemHtml += '<div class="listItemBody two-line">';
itemHtml += '<h3 class="listItemBodyText">';
itemHtml += globalize.translate('Option' + a.DayOfWeek);
itemHtml += '</h3>';
itemHtml += '<div class="listItemBodyText secondary">' + getDisplayTime(a.StartHour) + ' - ' + getDisplayTime(a.EndHour) + '</div>';
itemHtml += '</div>';
itemHtml += '<button type="button" is="paper-icon-button-light" class="btnDelete listItemButton" data-index="' + index + '"><span class="material-icons delete"></span></button>';
itemHtml += '</div>';
index++;
return itemHtml;
}).join('');
const accessScheduleList = page.querySelector('.accessScheduleList');
accessScheduleList.innerHTML = html;
$('.btnDelete', accessScheduleList).on('click', function () {
deleteAccessSchedule(page, schedules, parseInt(this.getAttribute('data-index'), 10));
});
}
function onSaveComplete() {
loading.hide();
toast(globalize.translate('SettingsSaved'));
}
function saveUser(user, page) {
user.Policy.MaxParentalRating = $('#selectMaxParentalRating', page).val() || null;
user.Policy.BlockUnratedItems = $('.chkUnratedItem', page).get().filter(function (i) {
return i.checked;
}).map(function (i) {
return i.getAttribute('data-itemtype');
});
user.Policy.AccessSchedules = getSchedulesFromPage(page);
user.Policy.BlockedTags = getBlockedTagsFromPage(page);
ApiClient.updateUserPolicy(user.Id, user.Policy).then(function () {
onSaveComplete();
});
}
function getDisplayTime(hours) {
let minutes = 0;
const pct = hours % 1;
if (pct) {
minutes = parseInt(60 * pct, 10);
}
return datetime.getDisplayTime(new Date(2000, 1, 1, hours, minutes, 0, 0));
}
function showSchedulePopup(page, schedule, index) {
schedule = schedule || {};
import('../../../components/accessSchedule/accessSchedule').then(({ default: accessschedule }) => {
accessschedule.show({
schedule: schedule
}).then(function (updatedSchedule) {
const schedules = getSchedulesFromPage(page);
if (index == -1) {
index = schedules.length;
}
schedules[index] = updatedSchedule;
renderAccessSchedule(page, schedules);
});
});
}
function getSchedulesFromPage(page) {
return $('.liSchedule', page).map(function () {
return {
DayOfWeek: this.getAttribute('data-day'),
StartHour: this.getAttribute('data-start'),
EndHour: this.getAttribute('data-end')
};
}).get();
}
function getBlockedTagsFromPage(page) {
return $('.blockedTag', page).map(function () {
return this.getAttribute('data-tag');
}).get();
}
function showBlockedTagPopup(page) {
import('../../../components/prompt/prompt').then(({ default: prompt }) => {
prompt({
label: globalize.translate('LabelTag')
}).then(function (value) {
const tags = getBlockedTagsFromPage(page);
if (tags.indexOf(value) == -1) {
tags.push(value);
loadBlockedTags(page, tags);
}
});
});
}
window.UserParentalControlPage = {
onSubmit: function () {
const page = $(this).parents('.page');
loading.show();
const userId = getParameterByName('userId');
ApiClient.getUser(userId).then(function (result) {
saveUser(result, page);
});
return false;
}
};
$(document).on('pageinit', '#userParentalControlPage', function () {
const page = this;
$('.btnAddSchedule', page).on('click', function () {
showSchedulePopup(page, {}, -1);
});
$('.btnAddBlockedTag', page).on('click', function () {
showBlockedTagPopup(page);
});
$('.userParentalControlForm').off('submit', UserParentalControlPage.onSubmit).on('submit', UserParentalControlPage.onSubmit);
}).on('pageshow', '#userParentalControlPage', function () {
const page = this;
loading.show();
const userId = getParameterByName('userId');
const promise1 = ApiClient.getUser(userId);
const promise2 = ApiClient.getParentalRatings();
Promise.all([promise1, promise2]).then(function (responses) {
loadUser(page, responses[0], responses[1]);
});
});

View file

@ -1,72 +0,0 @@
<div id="userPasswordPage" data-role="page" class="page type-interior userPasswordPage">
<div>
<div class="content-primary">
<div class="verticalSection">
<div class="sectionTitleContainer flex align-items-center">
<h2 class="sectionTitle username"></h2>
<a is="emby-linkbutton" rel="noopener noreferrer" class="raised button-alt headerHelpButton" target="_blank" href="https://jellyfin.org/docs/general/server/users/">${Help}</a>
</div>
</div>
<div data-role="controlgroup" data-type="horizontal" class="localnav" data-mini="true">
<a is="emby-linkbutton" href="#" data-role="button" onclick="Dashboard.navigate('useredit.html', true);">${Profile}</a>
<a is="emby-linkbutton" href="#" data-role="button" onclick="Dashboard.navigate('userlibraryaccess.html', true);">${TabAccess}</a>
<a is="emby-linkbutton" href="#" data-role="button" onclick="Dashboard.navigate('userparentalcontrol.html', true);">${TabParentalControl}</a>
<a is="emby-linkbutton" href="#" data-role="button" onclick="Dashboard.navigate('userpassword.html', true);" class="ui-btn-active">${HeaderPassword}</a>
</div>
<div class="readOnlyContent">
<form class="updatePasswordForm passwordSection hide" style="margin: 0 auto 2em;">
<div class="detailSection">
<div id="fldCurrentPassword" class="inputContainer hide">
<input is="emby-input" type="password" id="txtCurrentPassword" label="${LabelCurrentPassword}" autocomplete="off" />
</div>
<div class="inputContainer">
<input is="emby-input" type="password" id="txtNewPassword" label="${LabelNewPassword}" autocomplete="off" />
</div>
<div class="inputContainer">
<input is="emby-input" type="password" id="txtNewPasswordConfirm" label="${LabelNewPasswordConfirm}" autocomplete="off" />
</div>
<br />
<div>
<button is="emby-button" type="submit" class="raised button-submit block"><span>${Save}</span></button>
<button is="emby-button" type="button" id="btnResetPassword" class="raised button-cancel block hide">
<span>${ResetPassword}</span>
</button>
</div>
</div>
</form>
<br />
<form class="localAccessForm localAccessSection" style="margin: 0 auto;">
<div class="detailSection">
<div class="detailSectionHeader">
${HeaderEasyPinCode}
</div>
<br />
<div>${EasyPasswordHelp}</div>
<br />
<div class="inputContainer">
<input is="emby-input" type="number" id="txtEasyPassword" label="${LabelEasyPinCode}" autocomplete="off" pattern="[0-9]*" step="1" maxlength="5" />
</div>
<br />
<div class="checkboxContainer checkboxContainer-withDescription">
<label>
<input type="checkbox" is="emby-checkbox" class="chkEnableLocalEasyPassword" />
<span>${LabelInNetworkSignInWithEasyPassword}</span>
</label>
<div class="fieldDescription checkboxFieldDescription">${LabelInNetworkSignInWithEasyPasswordHelp}</div>
</div>
<div>
<button is="emby-button" type="submit" class="raised button-submit block">
<span>${Save}</span>
</button>
<button is="emby-button" type="button" id="btnResetEasyPassword" class="raised button-cancel block hide">
<span>${ButtonResetEasyPassword}</span>
</button>
</div>
</div>
</form>
</div>
</div>
</div>
</div>

View file

@ -1,179 +0,0 @@
import loading from '../../../components/loading/loading';
import libraryMenu from '../../../scripts/libraryMenu';
import globalize from '../../../scripts/globalize';
import '../../../elements/emby-button/emby-button';
import Dashboard from '../../../utils/dashboard';
import toast from '../../../components/toast/toast';
import confirm from '../../../components/confirm/confirm';
function loadUser(page, params) {
const userid = params.userId;
ApiClient.getUser(userid).then(function (user) {
Dashboard.getCurrentUser().then(function (loggedInUser) {
libraryMenu.setTitle(user.Name);
page.querySelector('.username').innerText = user.Name;
let showPasswordSection = true;
let showLocalAccessSection = false;
if (user.ConnectLinkType == 'Guest') {
page.querySelector('.localAccessSection').classList.add('hide');
showPasswordSection = false;
} else if (user.HasConfiguredPassword) {
page.querySelector('#btnResetPassword').classList.remove('hide');
page.querySelector('#fldCurrentPassword').classList.remove('hide');
showLocalAccessSection = true;
} else {
page.querySelector('#btnResetPassword').classList.add('hide');
page.querySelector('#fldCurrentPassword').classList.add('hide');
}
if (showPasswordSection && (loggedInUser.Policy.IsAdministrator || user.Policy.EnableUserPreferenceAccess)) {
page.querySelector('.passwordSection').classList.remove('hide');
} else {
page.querySelector('.passwordSection').classList.add('hide');
}
if (showLocalAccessSection && (loggedInUser.Policy.IsAdministrator || user.Policy.EnableUserPreferenceAccess)) {
page.querySelector('.localAccessSection').classList.remove('hide');
} else {
page.querySelector('.localAccessSection').classList.add('hide');
}
const txtEasyPassword = page.querySelector('#txtEasyPassword');
txtEasyPassword.value = '';
if (user.HasConfiguredEasyPassword) {
txtEasyPassword.placeholder = '******';
page.querySelector('#btnResetEasyPassword').classList.remove('hide');
} else {
txtEasyPassword.removeAttribute('placeholder');
txtEasyPassword.placeholder = '';
page.querySelector('#btnResetEasyPassword').classList.add('hide');
}
page.querySelector('.chkEnableLocalEasyPassword').checked = user.Configuration.EnableLocalPassword;
import('../../../components/autoFocuser').then(({ default: autoFocuser }) => {
autoFocuser.autoFocus(page);
});
});
});
page.querySelector('#txtCurrentPassword').value = '';
page.querySelector('#txtNewPassword').value = '';
page.querySelector('#txtNewPasswordConfirm').value = '';
}
export default function (view, params) {
function saveEasyPassword() {
const userId = params.userId;
const easyPassword = view.querySelector('#txtEasyPassword').value;
if (easyPassword) {
ApiClient.updateEasyPassword(userId, easyPassword).then(function () {
onEasyPasswordSaved(userId);
});
} else {
onEasyPasswordSaved(userId);
}
}
function onEasyPasswordSaved(userId) {
ApiClient.getUser(userId).then(function (user) {
user.Configuration.EnableLocalPassword = view.querySelector('.chkEnableLocalEasyPassword').checked;
ApiClient.updateUserConfiguration(user.Id, user.Configuration).then(function () {
loading.hide();
toast(globalize.translate('SettingsSaved'));
loadUser(view, params);
});
});
}
function savePassword() {
const userId = params.userId;
let currentPassword = view.querySelector('#txtCurrentPassword').value;
const newPassword = view.querySelector('#txtNewPassword').value;
if (view.querySelector('#fldCurrentPassword').classList.contains('hide')) {
// Firefox does not respect autocomplete=off, so clear it if the field is supposed to be hidden (and blank)
// This should only happen when user.HasConfiguredPassword is false, but this information is not passed on
currentPassword = '';
}
ApiClient.updateUserPassword(userId, currentPassword, newPassword).then(function () {
loading.hide();
toast(globalize.translate('PasswordSaved'));
loadUser(view, params);
}, function () {
loading.hide();
Dashboard.alert({
title: globalize.translate('HeaderLoginFailure'),
message: globalize.translate('MessageInvalidUser')
});
});
}
function onSubmit(e) {
const form = this;
if (form.querySelector('#txtNewPassword').value != form.querySelector('#txtNewPasswordConfirm').value) {
toast(globalize.translate('PasswordMatchError'));
} else {
loading.show();
savePassword();
}
e.preventDefault();
return false;
}
function onLocalAccessSubmit(e) {
loading.show();
saveEasyPassword();
e.preventDefault();
return false;
}
function resetPassword() {
const msg = globalize.translate('PasswordResetConfirmation');
confirm(msg, globalize.translate('ResetPassword')).then(function () {
const userId = params.userId;
loading.show();
ApiClient.resetUserPassword(userId).then(function () {
loading.hide();
Dashboard.alert({
message: globalize.translate('PasswordResetComplete'),
title: globalize.translate('ResetPassword')
});
loadUser(view, params);
});
});
}
function resetEasyPassword() {
const msg = globalize.translate('PinCodeResetConfirmation');
confirm(msg, globalize.translate('HeaderPinCodeReset')).then(function () {
const userId = params.userId;
loading.show();
ApiClient.resetEasyPassword(userId).then(function () {
loading.hide();
Dashboard.alert({
message: globalize.translate('PinCodeResetComplete'),
title: globalize.translate('HeaderPinCodeReset')
});
loadUser(view, params);
});
});
}
view.querySelector('.updatePasswordForm').addEventListener('submit', onSubmit);
view.querySelector('.localAccessForm').addEventListener('submit', onLocalAccessSubmit);
view.querySelector('#btnResetEasyPassword').addEventListener('click', resetEasyPassword);
view.querySelector('#btnResetPassword').addEventListener('click', resetPassword);
view.addEventListener('viewshow', function () {
loadUser(view, params);
});
}

View file

@ -1,16 +0,0 @@
<div id="userProfilesPage" data-role="page" class="page type-interior userProfilesPage fullWidthContent">
<div>
<div class="content-primary">
<div class="verticalSection verticalSection-extrabottompadding">
<div class="sectionTitleContainer sectionTitleContainer-cards">
<h2 class="sectionTitle sectionTitle-cards">${HeaderUsers}</h2>
<button is="emby-button" type="button" class="fab btnAddUser submit sectionTitleButton" style="margin-left:1em;" title="${ButtonAddUser}">
<span class="material-icons add"></span>
</button>
<a is="emby-linkbutton" rel="noopener noreferrer" style="margin-left:2em!important;" class="raised button-alt headerHelpButton" target="_blank" href="https://jellyfin.org/docs/general/server/users/adding-managing-users">${Help}</a>
</div>
<div class="localUsers itemsContainer vertical-wrap"></div>
</div>
</div>
</div>
</div>

View file

@ -1,184 +0,0 @@
import loading from '../../../components/loading/loading';
import dom from '../../../scripts/dom';
import globalize from '../../../scripts/globalize';
import { formatDistanceToNow } from 'date-fns';
import { getLocaleWithSuffix } from '../../../utils/dateFnsLocale.ts';
import '../../../elements/emby-button/paper-icon-button-light';
import '../../../components/cardbuilder/card.scss';
import '../../../elements/emby-button/emby-button';
import '../../../components/indicators/indicators.scss';
import '../../../styles/flexstyles.scss';
import Dashboard, { pageIdOn } from '../../../utils/dashboard';
import confirm from '../../../components/confirm/confirm';
import cardBuilder from '../../../components/cardbuilder/cardBuilder';
function deleteUser(page, id) {
const msg = globalize.translate('DeleteUserConfirmation');
confirm({
title: globalize.translate('DeleteUser'),
text: msg,
confirmText: globalize.translate('Delete'),
primary: 'delete'
}).then(function () {
loading.show();
ApiClient.deleteUser(id).then(function () {
loadData(page);
});
});
}
function showUserMenu(elem) {
const card = dom.parentWithClass(elem, 'card');
const page = dom.parentWithClass(card, 'page');
const userId = card.getAttribute('data-userid');
const menuItems = [];
menuItems.push({
name: globalize.translate('ButtonOpen'),
id: 'open',
icon: 'mode_edit'
});
menuItems.push({
name: globalize.translate('ButtonLibraryAccess'),
id: 'access',
icon: 'lock'
});
menuItems.push({
name: globalize.translate('ButtonParentalControl'),
id: 'parentalcontrol',
icon: 'person'
});
menuItems.push({
name: globalize.translate('Delete'),
id: 'delete',
icon: 'delete'
});
import('../../../components/actionSheet/actionSheet').then(({ default: actionsheet }) => {
actionsheet.show({
items: menuItems,
positionTo: card,
callback: function (id) {
switch (id) {
case 'open':
Dashboard.navigate('useredit.html?userId=' + userId);
break;
case 'access':
Dashboard.navigate('userlibraryaccess.html?userId=' + userId);
break;
case 'parentalcontrol':
Dashboard.navigate('userparentalcontrol.html?userId=' + userId);
break;
case 'delete':
deleteUser(page, userId);
}
}
});
});
}
function getUserHtml(user) {
let html = '';
let cssClass = 'card squareCard scalableCard squareCard-scalable';
if (user.Policy.IsDisabled) {
cssClass += ' grayscale';
}
html += "<div data-userid='" + user.Id + "' class='" + cssClass + "'>";
html += '<div class="cardBox visualCardBox">';
html += '<div class="cardScalable visualCardBox-cardScalable">';
html += '<div class="cardPadder cardPadder-square"></div>';
html += `<a is="emby-linkbutton" class="cardContent ${imgUrl ? '' : cardBuilder.getDefaultBackgroundClass()}" href="#!/useredit.html?userId=${user.Id}">`;
let imgUrl;
if (user.PrimaryImageTag) {
imgUrl = ApiClient.getUserImageUrl(user.Id, {
width: 300,
tag: user.PrimaryImageTag,
type: 'Primary'
});
}
let imageClass = 'cardImage';
if (user.Policy.IsDisabled) {
imageClass += ' disabledUser';
}
if (imgUrl) {
html += '<div class="' + imageClass + '" style="background-image:url(\'' + imgUrl + "');\">";
} else {
html += `<div class="${imageClass} ${imgUrl ? '' : cardBuilder.getDefaultBackgroundClass()} flex align-items-center justify-content-center">`;
html += '<span class="material-icons cardImageIcon person"></span>';
}
html += '</div>';
html += '</a>';
html += '</div>';
html += '<div class="cardFooter visualCardBox-cardFooter">';
html += '<div class="cardText flex align-items-center">';
html += '<div class="flex-grow" style="overflow:hidden;text-overflow:ellipsis;">';
html += user.Name;
html += '</div>';
html += '<button type="button" is="paper-icon-button-light" class="btnUserMenu flex-shrink-zero"><span class="material-icons more_vert"></span></button>';
html += '</div>';
html += '<div class="cardText cardText-secondary">';
const lastSeen = getLastSeenText(user.LastActivityDate);
html += lastSeen != '' ? lastSeen : '&nbsp;';
html += '</div>';
html += '</div>';
html += '</div>';
return html + '</div>';
}
// FIXME: It seems that, sometimes, server sends date in the future, so date-fns displays messages like 'in less than a minute'. We should fix
// how dates are returned by the server when the session is active and show something like 'Active now', instead of past/future sentences
function getLastSeenText(lastActivityDate) {
const localeWithSuffix = getLocaleWithSuffix();
if (lastActivityDate) {
return globalize.translate('LastSeen', formatDistanceToNow(Date.parse(lastActivityDate), localeWithSuffix));
}
return '';
}
function getUserSectionHtml(users) {
return users.map(function (u__q) {
return getUserHtml(u__q);
}).join('');
}
function renderUsers(page, users) {
page.querySelector('.localUsers').innerHTML = getUserSectionHtml(users);
}
function loadData(page) {
loading.show();
ApiClient.getUsers().then(function (users) {
renderUsers(page, users);
loading.hide();
});
}
pageIdOn('pageinit', 'userProfilesPage', function () {
const page = this;
page.querySelector('.btnAddUser').addEventListener('click', function() {
Dashboard.navigate('usernew.html');
});
page.querySelector('.localUsers').addEventListener('click', function (e__e) {
const btnUserMenu = dom.parentWithClass(e__e.target, 'btnUserMenu');
if (btnUserMenu) {
showUserMenu(btnUserMenu);
}
});
});
pageIdOn('pagebeforeshow', 'userProfilesPage', function () {
loadData(this);
});

View file

@ -1,4 +1,4 @@
import { appRouter } from '../components/appRouter';
import { appRouter } from '../components/router/appRouter';
import cardBuilder from '../components/cardbuilder/cardBuilder';
import dom from '../scripts/dom';
import globalize from '../scripts/globalize';

View file

@ -6,7 +6,7 @@ import isEqual from 'lodash-es/isEqual';
import { appHost } from '../../components/apphost';
import loading from '../../components/loading/loading';
import { appRouter } from '../../components/appRouter';
import { appRouter } from '../../components/router/appRouter';
import layoutManager from '../../components/layoutManager';
import Events from '../../utils/events.ts';
import * as userSettings from '../../scripts/settings/userSettings';

View file

@ -5,7 +5,7 @@ import libraryBrowser from '../../scripts/libraryBrowser';
import cardBuilder from '../../components/cardbuilder/cardBuilder';
import lazyLoader from '../../components/lazyLoader/lazyLoaderIntersectionObserver';
import globalize from '../../scripts/globalize';
import { appRouter } from '../../components/appRouter';
import { appRouter } from '../../components/router/appRouter';
import '../../elements/emby-button/emby-button';
export default function (view, params, tabContent) {

View file

@ -21,7 +21,7 @@ import '../../../styles/videoosd.scss';
import ServerConnections from '../../../components/ServerConnections';
import shell from '../../../scripts/shell';
import SubtitleSync from '../../../components/subtitlesync/subtitlesync';
import { appRouter } from '../../../components/appRouter';
import { appRouter } from '../../../components/router/appRouter';
import LibraryMenu from '../../../scripts/libraryMenu';
import { setBackdropTransparency, TRANSPARENCY_LEVEL } from '../../../components/backdrop/backdrop';
import { pluginManager } from '../../../components/pluginManager';

View file

@ -63,7 +63,7 @@ export default function(view) {
}
function goBack() {
import('../../../components/appRouter').then(({ appRouter }) => {
import('../../../components/router/appRouter').then(({ appRouter }) => {
appRouter.back();
});
}

View file

@ -1,6 +1,6 @@
import escapeHtml from 'escape-html';
import loading from '../../../components/loading/loading';
import { appRouter } from '../../../components/appRouter';
import { appRouter } from '../../../components/router/appRouter';
import layoutManager from '../../../components/layoutManager';
import libraryMenu from '../../../scripts/libraryMenu';
import appSettings from '../../../scripts/settings/appSettings';

View file

@ -5,7 +5,7 @@ import libraryBrowser from '../../scripts/libraryBrowser';
import cardBuilder from '../../components/cardbuilder/cardBuilder';
import lazyLoader from '../../components/lazyLoader/lazyLoaderIntersectionObserver';
import globalize from '../../scripts/globalize';
import { appRouter } from '../../components/appRouter';
import { appRouter } from '../../components/router/appRouter';
import '../../elements/emby-button/emby-button';
export default function (view, params, tabContent) {

View file

@ -1,69 +0,0 @@
<div id="userImagePage" data-role="page" class="page libraryPage userPreferencesPage userPasswordPage noSecondaryNavPage" data-title="${Profile}" data-menubutton="false">
<div class="padded-left padded-right padded-bottom-page">
<div class="readOnlyContent" style="margin: 0 auto; padding: 0 1em;">
<div style="position:relative;display:inline-block;max-width:200px;">
<input id="uploadImage" type="file" accept="image/*" style="position:absolute;right:0;width:100%;height:100%;opacity:0;" />
<div id="image" style="width:200px;height:200px;background-repeat:no-repeat;background-position:center;border-radius:100%;background-size:cover;"></div>
</div>
<div style="vertical-align:top;margin:1em 2em;display:inline-block;">
<h2 class="username" style="margin:0;font-size:xx-large;"></h2>
<br/>
<button is="emby-button" type="button" class="raised hide" id="btnAddImage">
<span>${ButtonAddImage}</span>
</button>
<button is="emby-button" type="button" class="raised hide" id="btnDeleteImage">
<span>${DeleteImage}</span>
</button>
</div>
</div>
<form class="updatePasswordForm passwordSection userProfileSettingsForm hide" style="margin: 3em auto 0;">
<div class="verticalSection">
<h2 class="sectionTitle">
${HeaderPassword}
</h2>
<div id="fldCurrentPassword" class="inputContainer hide">
<input is="emby-input" type="password" id="txtCurrentPassword" label="${LabelCurrentPassword}" autocomplete="off" />
</div>
<div class="inputContainer">
<input is="emby-input" type="password" id="txtNewPassword" label="${LabelNewPassword}" autocomplete="off" />
</div>
<div class="inputContainer">
<input is="emby-input" type="password" id="txtNewPasswordConfirm" label="${LabelNewPasswordConfirm}" autocomplete="off" />
</div>
<div>
<button is="emby-button" type="submit" class="raised button-submit block">
<span>${Save}</span>
</button>
<button is="emby-button" type="button" id="btnResetPassword" class="raised cancel block hide">
<span>${ResetPassword}</span>
</button>
</div>
</div>
</form>
<form class="localAccessForm localAccessSection userProfileSettingsForm hide" style="margin: 3em auto 0;">
<div class="verticalSection">
<h2 class="sectionTitle">${HeaderEasyPinCode}</h2>
<div>${EasyPasswordHelp}</div>
<br />
<div class="inputContainer">
<input is="emby-input" type="number" id="txtEasyPassword" label="${LabelEasyPinCode}" autocomplete="off" pattern="[0-9]*" step="1" maxlength="5" />
</div>
<div class="checkboxContainer checkboxContainer-withDescription">
<label>
<input type="checkbox" is="emby-checkbox" class="chkEnableLocalEasyPassword" />
<span>${LabelInNetworkSignInWithEasyPassword}</span>
</label>
<div class="fieldDescription checkboxFieldDescription">${LabelInNetworkSignInWithEasyPasswordHelp}</div>
</div>
<div>
<button is="emby-button" type="submit" class="raised button-submit block">
<span>${Save}</span>
</button>
<button is="emby-button" type="button" id="btnResetEasyPassword" class="raised cancel block hide">
<span>${ButtonResetEasyPassword}</span>
</button>
</div>
</div>
</form>
</div>
</div>

View file

@ -1,105 +0,0 @@
import UserPasswordPage from '../../dashboard/users/userpasswordpage';
import loading from '../../../components/loading/loading';
import libraryMenu from '../../../scripts/libraryMenu';
import { appHost } from '../../../components/apphost';
import globalize from '../../../scripts/globalize';
import '../../../elements/emby-button/emby-button';
import Dashboard from '../../../utils/dashboard';
import toast from '../../../components/toast/toast';
import confirm from '../../../components/confirm/confirm';
import { getParameterByName } from '../../../utils/url.ts';
function reloadUser(page) {
const userId = getParameterByName('userId');
loading.show();
ApiClient.getUser(userId).then(function (user) {
page.querySelector('.username').innerText = user.Name;
libraryMenu.setTitle(user.Name);
let imageUrl = 'assets/img/avatar.png';
if (user.PrimaryImageTag) {
imageUrl = ApiClient.getUserImageUrl(user.Id, {
tag: user.PrimaryImageTag,
type: 'Primary'
});
}
const userImage = page.querySelector('#image');
userImage.style.backgroundImage = 'url(' + imageUrl + ')';
Dashboard.getCurrentUser().then(function (loggedInUser) {
if (user.PrimaryImageTag) {
page.querySelector('#btnAddImage').classList.add('hide');
page.querySelector('#btnDeleteImage').classList.remove('hide');
} else if (appHost.supports('fileinput') && (loggedInUser.Policy.IsAdministrator || user.Policy.EnableUserPreferenceAccess)) {
page.querySelector('#btnDeleteImage').classList.add('hide');
page.querySelector('#btnAddImage').classList.remove('hide');
}
});
loading.hide();
});
}
function onFileReaderError(evt) {
loading.hide();
switch (evt.target.error.code) {
case evt.target.error.NOT_FOUND_ERR:
toast(globalize.translate('FileNotFound'));
break;
case evt.target.error.ABORT_ERR:
onFileReaderAbort();
break;
case evt.target.error.NOT_READABLE_ERR:
default:
toast(globalize.translate('FileReadError'));
}
}
function onFileReaderAbort() {
loading.hide();
toast(globalize.translate('FileReadCancelled'));
}
function setFiles(page, files) {
const userImage = page.querySelector('#image');
const file = files[0];
if (!file || !file.type.match('image.*')) {
return false;
}
const reader = new FileReader();
reader.onerror = onFileReaderError;
reader.onabort = onFileReaderAbort;
reader.onload = function (evt) {
userImage.style.backgroundImage = 'url(' + evt.target.result + ')';
const userId = getParameterByName('userId');
ApiClient.uploadUserImage(userId, 'Primary', file).then(function () {
loading.hide();
reloadUser(page);
});
};
reader.readAsDataURL(file);
}
export default function (view, params) {
reloadUser(view);
new UserPasswordPage(view, params);
view.querySelector('#btnDeleteImage').addEventListener('click', function () {
confirm(globalize.translate('DeleteImageConfirmation'), globalize.translate('DeleteImage')).then(function () {
loading.show();
const userId = getParameterByName('userId');
ApiClient.deleteUserImage(userId, 'primary').then(function () {
loading.hide();
reloadUser(view);
});
});
});
view.querySelector('#btnAddImage').addEventListener('click', function () {
view.querySelector('#uploadImage').click();
});
view.querySelector('#uploadImage').addEventListener('change', function (evt) {
setFiles(view, evt.target.files);
});
}

View file

@ -2,7 +2,7 @@ import React, { AnchorHTMLAttributes, DetailedHTMLProps, MouseEvent, useCallback
import classNames from 'classnames';
import layoutManager from '../../components/layoutManager';
import shell from '../../scripts/shell';
import { appRouter } from '../../components/appRouter';
import { appRouter } from '../../components/router/appRouter';
import { appHost } from '../../components/apphost';
import './emby-button.scss';

View file

@ -2,7 +2,7 @@ import 'webcomponents.js/webcomponents-lite';
import { removeEventListener, addEventListener } from '../../scripts/dom';
import layoutManager from '../../components/layoutManager';
import shell from '../../scripts/shell';
import { appRouter } from '../../components/appRouter';
import { appRouter } from '../../components/router/appRouter';
import { appHost } from '../../components/apphost';
import './emby-button.scss';

View file

@ -20,7 +20,7 @@ import { appHost } from './components/apphost';
import { getPlugins } from './scripts/settings/webSettings';
import { pluginManager } from './components/pluginManager';
import packageManager from './components/packageManager';
import { appRouter, history } from './components/appRouter';
import { appRouter, history } from './components/router/appRouter';
import './elements/emby-button/emby-button';
import './scripts/autoThemes';
import './components/themeMediaPlayer';

View file

@ -3,7 +3,7 @@ import { Archive } from 'libarchive.js';
import loading from '../../components/loading/loading';
import dialogHelper from '../../components/dialogHelper/dialogHelper';
import keyboardnavigation from '../../scripts/keyboardNavigation';
import { appRouter } from '../../components/appRouter';
import { appRouter } from '../../components/router/appRouter';
import ServerConnections from '../../components/ServerConnections';
import * as userSettings from '../../scripts/settings/userSettings';
import { PluginType } from '../../types/plugin.ts';

View file

@ -3,7 +3,7 @@ import { appHost } from '../../components/apphost';
import loading from '../../components/loading/loading';
import dom from '../../scripts/dom';
import { playbackManager } from '../../components/playback/playbackmanager';
import { appRouter } from '../../components/appRouter';
import { appRouter } from '../../components/router/appRouter';
import {
bindEventsToHlsPlayer,
destroyHlsPlayer,

View file

@ -3,7 +3,7 @@ import loading from '../../components/loading/loading';
import keyboardnavigation from '../../scripts/keyboardNavigation';
import dialogHelper from '../../components/dialogHelper/dialogHelper';
import dom from '../../scripts/dom';
import { appRouter } from '../../components/appRouter';
import { appRouter } from '../../components/router/appRouter';
import { PluginType } from '../../types/plugin.ts';
import Events from '../../utils/events.ts';

View file

@ -1,5 +1,5 @@
import browser from '../../scripts/browser';
import { appRouter } from '../../components/appRouter';
import { appRouter } from '../../components/router/appRouter';
import loading from '../../components/loading/loading';
import { setBackdropTransparency, TRANSPARENCY_LEVEL } from '../../components/backdrop/backdrop';
import { PluginType } from '../../types/plugin.ts';

View file

@ -1,6 +1,6 @@
import confirm from '../components/confirm/confirm';
import { appRouter } from '../components/appRouter';
import { appRouter } from '../components/router/appRouter';
import globalize from './globalize';
import ServerConnections from '../components/ServerConnections';
import alert from '../components/alert';

View file

@ -1,6 +1,6 @@
import { playbackManager } from '../components/playback/playbackmanager';
import focusManager from '../components/focusManager';
import { appRouter } from '../components/appRouter';
import { appRouter } from '../components/router/appRouter';
import dom from './dom';
import { appHost } from '../components/apphost';

View file

@ -5,7 +5,7 @@ import dom from './dom';
import layoutManager from '../components/layoutManager';
import inputManager from './inputManager';
import viewManager from '../components/viewManager/viewManager';
import { appRouter } from '../components/appRouter';
import { appRouter } from '../components/router/appRouter';
import { appHost } from '../components/apphost';
import { playbackManager } from '../components/playback/playbackmanager';
import { pluginManager } from '../components/pluginManager';

View file

@ -2,7 +2,7 @@ import { playbackManager } from '../components/playback/playbackmanager';
import { pluginManager } from '../components/pluginManager';
import inputManager from '../scripts/inputManager';
import focusManager from '../components/focusManager';
import { appRouter } from '../components/appRouter';
import { appRouter } from '../components/router/appRouter';
import ServerConnections from '../components/ServerConnections';
import toast from '../components/toast/toast';
import alert from '../components/alert';

View file

@ -1,7 +1,7 @@
import ServerConnections from '../components/ServerConnections';
import toast from '../components/toast/toast';
import loading from '../components/loading/loading';
import { appRouter } from '../components/appRouter';
import { appRouter } from '../components/router/appRouter';
import baseAlert from '../components/alert';
import baseConfirm from '../components/confirm/confirm';
import globalize from '../scripts/globalize';