mirror of
https://github.com/jellyfin/jellyfin-web
synced 2025-03-30 19:56:21 +00:00
Remove app router routing
This commit is contained in:
parent
9d6e266cf8
commit
99b2bd4f6e
4 changed files with 54 additions and 345 deletions
|
@ -3,7 +3,6 @@ import { Outlet, useLocation, useNavigate } from 'react-router-dom';
|
||||||
import type { ConnectResponse } from 'jellyfin-apiclient';
|
import type { ConnectResponse } from 'jellyfin-apiclient';
|
||||||
|
|
||||||
import alert from './alert';
|
import alert from './alert';
|
||||||
import { appRouter } from './router/appRouter';
|
|
||||||
import Loading from './loading/LoadingComponent';
|
import Loading from './loading/LoadingComponent';
|
||||||
import ServerConnections from './ServerConnections';
|
import ServerConnections from './ServerConnections';
|
||||||
import globalize from '../lib/globalize';
|
import globalize from '../lib/globalize';
|
||||||
|
@ -149,24 +148,20 @@ const ConnectionRequired: FunctionComponent<ConnectionRequiredProps> = ({
|
||||||
}, [bounce, isAdminRequired, isUserRequired]);
|
}, [bounce, isAdminRequired, isUserRequired]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
// TODO: appRouter will call appHost.exit() if navigating back when you are already at the default route.
|
|
||||||
// This case will need to be handled elsewhere before appRouter can be killed.
|
|
||||||
|
|
||||||
// Check connection status on initial page load
|
// Check connection status on initial page load
|
||||||
const firstConnection = appRouter.firstConnectionResult;
|
ServerConnections.connect()
|
||||||
appRouter.firstConnectionResult = null;
|
.then(firstConnection => {
|
||||||
|
console.debug('[ConnectionRequired] connection state', firstConnection?.State);
|
||||||
|
|
||||||
if (firstConnection && firstConnection.State !== ConnectionState.SignedIn) {
|
if (firstConnection && firstConnection.State !== ConnectionState.SignedIn) {
|
||||||
handleIncompleteWizard(firstConnection)
|
return handleIncompleteWizard(firstConnection);
|
||||||
.catch(err => {
|
} else {
|
||||||
console.error('[ConnectionRequired] failed to start wizard', err);
|
return validateUserAccess();
|
||||||
});
|
}
|
||||||
} else {
|
})
|
||||||
validateUserAccess()
|
.catch(err => {
|
||||||
.catch(err => {
|
console.error('[ConnectionRequired] failed to connect to server', err);
|
||||||
console.error('[ConnectionRequired] failed to validate user access', err);
|
});
|
||||||
});
|
|
||||||
}
|
|
||||||
}, [handleIncompleteWizard, validateUserAccess]);
|
}, [handleIncompleteWizard, validateUserAccess]);
|
||||||
|
|
||||||
if (isLoading) {
|
if (isLoading) {
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
import { appRouter, isLyricsPage } from 'components/router/appRouter';
|
||||||
import datetime from '../../scripts/datetime';
|
import datetime from '../../scripts/datetime';
|
||||||
import Events from '../../utils/events.ts';
|
import Events from '../../utils/events.ts';
|
||||||
import browser from '../../scripts/browser';
|
import browser from '../../scripts/browser';
|
||||||
|
@ -16,7 +17,6 @@ import appFooter from '../appFooter/appFooter';
|
||||||
import itemShortcuts from '../shortcuts';
|
import itemShortcuts from '../shortcuts';
|
||||||
import './nowPlayingBar.scss';
|
import './nowPlayingBar.scss';
|
||||||
import '../../elements/emby-slider/emby-slider';
|
import '../../elements/emby-slider/emby-slider';
|
||||||
import { appRouter } from '../router/appRouter';
|
|
||||||
|
|
||||||
let currentPlayer;
|
let currentPlayer;
|
||||||
let currentPlayerSupportedCommands = [];
|
let currentPlayerSupportedCommands = [];
|
||||||
|
@ -773,7 +773,7 @@ function refreshFromPlayer(player, type) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function bindToPlayer(player) {
|
function bindToPlayer(player) {
|
||||||
isLyricPageActive = appRouter.currentRouteInfo.path.toLowerCase() === '/lyrics';
|
isLyricPageActive = isLyricsPage();
|
||||||
if (player === currentPlayer) {
|
if (player === currentPlayer) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -806,7 +806,7 @@ Events.on(playbackManager, 'playerchange', function () {
|
||||||
bindToPlayer(playbackManager.getCurrentPlayer());
|
bindToPlayer(playbackManager.getCurrentPlayer());
|
||||||
|
|
||||||
document.addEventListener('viewbeforeshow', function (e) {
|
document.addEventListener('viewbeforeshow', function (e) {
|
||||||
isLyricPageActive = appRouter.currentRouteInfo.path.toLowerCase() === '/lyrics';
|
isLyricPageActive = isLyricsPage();
|
||||||
setLyricButtonActiveStatus();
|
setLyricButtonActiveStatus();
|
||||||
if (!e.detail.options.enableMediaControl) {
|
if (!e.detail.options.enableMediaControl) {
|
||||||
if (isVisibilityAllowed) {
|
if (isVisibilityAllowed) {
|
||||||
|
|
|
@ -1,33 +1,36 @@
|
||||||
import { CollectionType } from '@jellyfin/sdk/lib/generated-client/models/collection-type';
|
import { CollectionType } from '@jellyfin/sdk/lib/generated-client/models/collection-type';
|
||||||
import { Action } from 'history';
|
|
||||||
|
|
||||||
import { appHost } from '../apphost';
|
import { setBackdropTransparency } from '../backdrop/backdrop';
|
||||||
import { clearBackdrop, setBackdropTransparency } from '../backdrop/backdrop';
|
|
||||||
import globalize from '../../lib/globalize';
|
import globalize from '../../lib/globalize';
|
||||||
import Events from '../../utils/events.ts';
|
|
||||||
import itemHelper from '../itemHelper';
|
import itemHelper from '../itemHelper';
|
||||||
import loading from '../loading/loading';
|
import loading from '../loading/loading';
|
||||||
import viewManager from '../viewManager/viewManager';
|
|
||||||
import ServerConnections from '../ServerConnections';
|
import ServerConnections from '../ServerConnections';
|
||||||
import alert from '../alert';
|
import alert from '../alert';
|
||||||
|
|
||||||
import { queryClient } from 'utils/query/queryClient';
|
import { queryClient } from 'utils/query/queryClient';
|
||||||
import { getItemQuery } from 'hooks/useItem';
|
import { getItemQuery } from 'hooks/useItem';
|
||||||
import { toApi } from 'utils/jellyfin-apiclient/compat';
|
import { toApi } from 'utils/jellyfin-apiclient/compat';
|
||||||
import { ConnectionState } from 'utils/jellyfin-apiclient/ConnectionState.ts';
|
|
||||||
import { history } from 'RootAppRouter';
|
import { history } from 'RootAppRouter';
|
||||||
|
|
||||||
/**
|
/** Pages of "no return" (when "Go back" should behave differently, probably quitting the application). */
|
||||||
* Page types of "no return" (when "Go back" should behave differently, probably quitting the application).
|
|
||||||
*/
|
|
||||||
const START_PAGE_TYPES = ['home', 'login', 'selectserver'];
|
|
||||||
const START_PAGE_PATHS = ['/home.html', '/login.html', '/selectserver.html'];
|
const START_PAGE_PATHS = ['/home.html', '/login.html', '/selectserver.html'];
|
||||||
|
|
||||||
|
/** Pages that do not require a user to be logged in to view. */
|
||||||
|
const PUBLIC_PATHS = [
|
||||||
|
'/addserver.html',
|
||||||
|
'/selectserver.html',
|
||||||
|
'/login.html',
|
||||||
|
'/forgotpassword.html',
|
||||||
|
'/forgotpasswordpin.html',
|
||||||
|
'/wizardremoteaccess.html',
|
||||||
|
'/wizardfinish.html',
|
||||||
|
'/wizardlibrary.html',
|
||||||
|
'/wizardsettings.html',
|
||||||
|
'/wizardstart.html',
|
||||||
|
'/wizarduser.html'
|
||||||
|
];
|
||||||
|
|
||||||
class AppRouter {
|
class AppRouter {
|
||||||
allRoutes = new Map();
|
|
||||||
currentRouteInfo = { route: {} };
|
|
||||||
currentViewLoadRequest;
|
|
||||||
firstConnectionResult;
|
|
||||||
forcedLogoutMsg;
|
forcedLogoutMsg;
|
||||||
msgTimeout;
|
msgTimeout;
|
||||||
promiseShow;
|
promiseShow;
|
||||||
|
@ -45,21 +48,6 @@ class AppRouter {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
addRoute(path, route) {
|
|
||||||
this.allRoutes.set(path, {
|
|
||||||
route,
|
|
||||||
handler: this.#getHandler(route)
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
#beginConnectionWizard() {
|
|
||||||
clearBackdrop();
|
|
||||||
loading.show();
|
|
||||||
ServerConnections.connect().then(result => {
|
|
||||||
this.#handleConnectionResult(result);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
ready() {
|
ready() {
|
||||||
return this.promiseShow || Promise.resolve();
|
return this.promiseShow || Promise.resolve();
|
||||||
}
|
}
|
||||||
|
@ -98,7 +86,7 @@ class AppRouter {
|
||||||
path = path.replace(this.baseUrl(), '');
|
path = path.replace(this.baseUrl(), '');
|
||||||
|
|
||||||
// can't use this with home right now due to the back menu
|
// can't use this with home right now due to the back menu
|
||||||
if (this.currentRouteInfo?.path === path && this.currentRouteInfo.route.type !== 'home') {
|
if (history.location.pathname === path && path !== '/home.html') {
|
||||||
loading.hide();
|
loading.hide();
|
||||||
return Promise.resolve();
|
return Promise.resolve();
|
||||||
}
|
}
|
||||||
|
@ -112,72 +100,17 @@ class AppRouter {
|
||||||
return this.promiseShow;
|
return this.promiseShow;
|
||||||
}
|
}
|
||||||
|
|
||||||
#goToRoute({ location, action }) {
|
|
||||||
// Strip the leading "!" if present
|
|
||||||
const normalizedPath = location.pathname.replace(/^!/, '');
|
|
||||||
|
|
||||||
const route = this.allRoutes.get(normalizedPath);
|
|
||||||
if (route) {
|
|
||||||
console.debug('[appRouter] "%s" route found', normalizedPath, location, route);
|
|
||||||
route.handler({
|
|
||||||
// Recreate the default context used by page.js: https://github.com/visionmedia/page.js#context
|
|
||||||
path: normalizedPath + location.search,
|
|
||||||
pathname: normalizedPath,
|
|
||||||
querystring: location.search.replace(/^\?/, ''),
|
|
||||||
state: location.state,
|
|
||||||
// Custom context variables
|
|
||||||
isBack: action === Action.Pop
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
// The route is not registered here, so it should be handled by react-router
|
|
||||||
this.currentRouteInfo = {
|
|
||||||
route: {},
|
|
||||||
path: normalizedPath + location.search
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
start() {
|
|
||||||
loading.show();
|
|
||||||
|
|
||||||
ServerConnections.getApiClients().forEach(apiClient => {
|
|
||||||
Events.off(apiClient, 'requestfail', this.onRequestFail);
|
|
||||||
Events.on(apiClient, 'requestfail', this.onRequestFail);
|
|
||||||
});
|
|
||||||
|
|
||||||
Events.on(ServerConnections, 'apiclientcreated', (_e, apiClient) => {
|
|
||||||
Events.off(apiClient, 'requestfail', this.onRequestFail);
|
|
||||||
Events.on(apiClient, 'requestfail', this.onRequestFail);
|
|
||||||
});
|
|
||||||
|
|
||||||
return ServerConnections.connect().then(result => {
|
|
||||||
this.firstConnectionResult = result;
|
|
||||||
|
|
||||||
// Handle the initial route
|
|
||||||
this.#goToRoute({ location: history.location });
|
|
||||||
|
|
||||||
// Handle route changes
|
|
||||||
history.listen(params => {
|
|
||||||
this.#goToRoute(params);
|
|
||||||
});
|
|
||||||
}).catch().then(() => {
|
|
||||||
loading.hide();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
baseUrl() {
|
baseUrl() {
|
||||||
return this.baseRoute;
|
return this.baseRoute;
|
||||||
}
|
}
|
||||||
|
|
||||||
canGoBack() {
|
canGoBack() {
|
||||||
const { path, route } = this.currentRouteInfo;
|
const path = history.location.pathname;
|
||||||
const pathOnly = path?.split('?')[0] ?? '';
|
|
||||||
|
|
||||||
if (!route) {
|
if (
|
||||||
return false;
|
!document.querySelector('.dialogContainer')
|
||||||
}
|
&& START_PAGE_PATHS.includes(path)
|
||||||
|
) {
|
||||||
if (!document.querySelector('.dialogContainer') && (START_PAGE_TYPES.includes(route.type) || START_PAGE_PATHS.includes(pathOnly))) {
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -219,126 +152,6 @@ class AppRouter {
|
||||||
setBackdropTransparency(level);
|
setBackdropTransparency(level);
|
||||||
}
|
}
|
||||||
|
|
||||||
#handleConnectionResult(result) {
|
|
||||||
switch (result.State) {
|
|
||||||
case ConnectionState.SignedIn:
|
|
||||||
loading.hide();
|
|
||||||
this.goHome();
|
|
||||||
break;
|
|
||||||
case ConnectionState.ServerSignIn:
|
|
||||||
this.showLocalLogin(result.ApiClient.serverId());
|
|
||||||
break;
|
|
||||||
case ConnectionState.ServerSelection:
|
|
||||||
this.showSelectServer();
|
|
||||||
break;
|
|
||||||
case ConnectionState.ServerUpdateNeeded:
|
|
||||||
alert({
|
|
||||||
text: globalize.translate('ServerUpdateNeeded', 'https://github.com/jellyfin/jellyfin'),
|
|
||||||
html: globalize.translate('ServerUpdateNeeded', '<a href="https://github.com/jellyfin/jellyfin">https://github.com/jellyfin/jellyfin</a>')
|
|
||||||
}).then(() => {
|
|
||||||
this.showSelectServer();
|
|
||||||
});
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#loadContentUrl(ctx, _next, route, request) {
|
|
||||||
let url;
|
|
||||||
if (route.contentPath && typeof (route.contentPath) === 'function') {
|
|
||||||
url = route.contentPath(ctx.querystring);
|
|
||||||
} else {
|
|
||||||
url = route.contentPath || route.path;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ctx.querystring && route.enableContentQueryString) {
|
|
||||||
url += '?' + ctx.querystring;
|
|
||||||
}
|
|
||||||
|
|
||||||
let promise;
|
|
||||||
if (route.serverRequest) {
|
|
||||||
const apiClient = ServerConnections.currentApiClient();
|
|
||||||
url = apiClient.getUrl(`/web${url}`);
|
|
||||||
promise = apiClient.get(url);
|
|
||||||
} else {
|
|
||||||
promise = import(/* webpackChunkName: "[request]" */ `../../controllers/${url}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
promise.then((html) => {
|
|
||||||
this.#loadContent(ctx, route, html, request);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
#handleRoute(ctx, next, route) {
|
|
||||||
this.#authenticate(ctx, route, () => {
|
|
||||||
this.#initRoute(ctx, next, route);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
#initRoute(ctx, next, route) {
|
|
||||||
const onInitComplete = (controllerFactory) => {
|
|
||||||
this.#sendRouteToViewManager(ctx, next, route, controllerFactory);
|
|
||||||
};
|
|
||||||
|
|
||||||
if (route.controller) {
|
|
||||||
import(/* webpackChunkName: "[request]" */ '../../controllers/' + route.controller).then(onInitComplete);
|
|
||||||
} else {
|
|
||||||
onInitComplete();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#cancelCurrentLoadRequest() {
|
|
||||||
const currentRequest = this.currentViewLoadRequest;
|
|
||||||
if (currentRequest) {
|
|
||||||
currentRequest.cancel = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#sendRouteToViewManager(ctx, next, route, controllerFactory) {
|
|
||||||
this.#cancelCurrentLoadRequest();
|
|
||||||
const isBackNav = ctx.isBack;
|
|
||||||
|
|
||||||
const currentRequest = {
|
|
||||||
url: this.baseUrl() + ctx.path,
|
|
||||||
transition: route.transition,
|
|
||||||
isBack: isBackNav,
|
|
||||||
state: ctx.state,
|
|
||||||
type: route.type,
|
|
||||||
fullscreen: route.fullscreen,
|
|
||||||
controllerFactory: controllerFactory,
|
|
||||||
options: {
|
|
||||||
supportsThemeMedia: route.supportsThemeMedia || false,
|
|
||||||
enableMediaControl: route.enableMediaControl !== false
|
|
||||||
},
|
|
||||||
autoFocus: route.autoFocus
|
|
||||||
};
|
|
||||||
this.currentViewLoadRequest = currentRequest;
|
|
||||||
|
|
||||||
const onNewViewNeeded = () => {
|
|
||||||
if (typeof route.path === 'string') {
|
|
||||||
this.#loadContentUrl(ctx, next, route, currentRequest);
|
|
||||||
} else {
|
|
||||||
next();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
if (!isBackNav) {
|
|
||||||
onNewViewNeeded();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
viewManager.tryRestoreView(currentRequest, () => {
|
|
||||||
this.currentRouteInfo = {
|
|
||||||
route: route,
|
|
||||||
path: ctx.path
|
|
||||||
};
|
|
||||||
}).catch((result) => {
|
|
||||||
if (!result?.cancelled) {
|
|
||||||
onNewViewNeeded();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
onViewShow() {
|
onViewShow() {
|
||||||
const resolve = this.resolveOnNextShow;
|
const resolve = this.resolveOnNextShow;
|
||||||
if (resolve) {
|
if (resolve) {
|
||||||
|
@ -370,114 +183,16 @@ class AppRouter {
|
||||||
const apiClient = this;
|
const apiClient = this;
|
||||||
|
|
||||||
if (data.status === 403 && data.errorCode === 'ParentalControl') {
|
if (data.status === 403 && data.errorCode === 'ParentalControl') {
|
||||||
const isCurrentAllowed = appRouter.currentRouteInfo ? (appRouter.currentRouteInfo.route.anonymous || appRouter.currentRouteInfo.route.startup) : true;
|
const isPublicPage = PUBLIC_PATHS.includes(history.location.pathname);
|
||||||
|
|
||||||
// Bounce to the login screen, but not if a password entry fails, obviously
|
// Bounce to the login screen, but not if a password entry fails, obviously
|
||||||
if (!isCurrentAllowed) {
|
if (!isPublicPage) {
|
||||||
appRouter.showForcedLogoutMessage(globalize.translate('AccessRestrictedTryAgainLater'));
|
appRouter.showForcedLogoutMessage(globalize.translate('AccessRestrictedTryAgainLater'));
|
||||||
appRouter.showLocalLogin(apiClient.serverId());
|
appRouter.showLocalLogin(apiClient.serverId());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#authenticate(ctx, route, callback) {
|
|
||||||
const firstResult = this.firstConnectionResult;
|
|
||||||
|
|
||||||
this.firstConnectionResult = null;
|
|
||||||
if (firstResult) {
|
|
||||||
if (firstResult.State === ConnectionState.ServerSignIn) {
|
|
||||||
const url = firstResult.ApiClient.serverAddress() + '/System/Info/Public';
|
|
||||||
fetch(url, { cache: 'no-cache' }).then(response => {
|
|
||||||
if (!response.ok) return Promise.reject(new Error('fetch failed'));
|
|
||||||
return response.json();
|
|
||||||
}).then(data => {
|
|
||||||
if (data !== null && data.StartupWizardCompleted === false) {
|
|
||||||
ServerConnections.setLocalApiClient(firstResult.ApiClient);
|
|
||||||
this.show('wizardstart.html');
|
|
||||||
} else {
|
|
||||||
this.#handleConnectionResult(firstResult);
|
|
||||||
}
|
|
||||||
}).catch(error => {
|
|
||||||
console.error(error);
|
|
||||||
});
|
|
||||||
|
|
||||||
return;
|
|
||||||
} else if (firstResult.State !== ConnectionState.SignedIn) {
|
|
||||||
this.#handleConnectionResult(firstResult);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const apiClient = ServerConnections.currentApiClient();
|
|
||||||
const pathname = ctx.pathname.toLowerCase();
|
|
||||||
|
|
||||||
console.debug('[appRouter] processing path request: ' + pathname);
|
|
||||||
const isCurrentRouteStartup = this.currentRouteInfo ? this.currentRouteInfo.route.startup : true;
|
|
||||||
const shouldExitApp = ctx.isBack && route.isDefaultRoute && isCurrentRouteStartup;
|
|
||||||
|
|
||||||
if (!shouldExitApp && (!apiClient?.isLoggedIn()) && !route.anonymous) {
|
|
||||||
console.debug('[appRouter] route does not allow anonymous access: redirecting to login');
|
|
||||||
this.#beginConnectionWizard();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (shouldExitApp) {
|
|
||||||
if (appHost.supports('exit')) {
|
|
||||||
appHost.exit();
|
|
||||||
}
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (apiClient?.isLoggedIn()) {
|
|
||||||
console.debug('[appRouter] user is authenticated');
|
|
||||||
|
|
||||||
if (route.roles) {
|
|
||||||
this.#validateRoles(apiClient, route.roles).then(() => {
|
|
||||||
callback();
|
|
||||||
}, this.#beginConnectionWizard.bind(this));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
console.debug('[appRouter] proceeding to page: ' + pathname);
|
|
||||||
callback();
|
|
||||||
}
|
|
||||||
|
|
||||||
#validateRoles(apiClient, roles) {
|
|
||||||
return Promise.all(roles.split(',').map((role) => {
|
|
||||||
return this.#validateRole(apiClient, role);
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
|
|
||||||
#validateRole(apiClient, role) {
|
|
||||||
if (role === 'admin') {
|
|
||||||
return apiClient.getCurrentUser().then((user) => {
|
|
||||||
if (user.Policy.IsAdministrator) {
|
|
||||||
return Promise.resolve();
|
|
||||||
}
|
|
||||||
return Promise.reject();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Unknown role
|
|
||||||
return Promise.resolve();
|
|
||||||
}
|
|
||||||
|
|
||||||
#loadContent(ctx, route, html, request) {
|
|
||||||
html = globalize.translateHtml(html, route.dictionary);
|
|
||||||
request.view = html;
|
|
||||||
|
|
||||||
viewManager.loadView(request);
|
|
||||||
|
|
||||||
this.currentRouteInfo = {
|
|
||||||
route: route,
|
|
||||||
path: ctx.path
|
|
||||||
};
|
|
||||||
|
|
||||||
ctx.handled = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
#getRequestFile() {
|
#getRequestFile() {
|
||||||
let path = window.location.pathname || '';
|
let path = window.location.pathname || '';
|
||||||
|
|
||||||
|
@ -495,20 +210,6 @@ class AppRouter {
|
||||||
return path;
|
return path;
|
||||||
}
|
}
|
||||||
|
|
||||||
#getHandler(route) {
|
|
||||||
return (ctx, next) => {
|
|
||||||
const ignore = ctx.path === this.currentRouteInfo.path;
|
|
||||||
if (ignore) {
|
|
||||||
console.debug('[appRouter] path did not change, ignoring route change');
|
|
||||||
// Resolve 'show' promise
|
|
||||||
this.onViewShow();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.#handleRoute(ctx, next, route);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
getRouteUrl(item, options) {
|
getRouteUrl(item, options) {
|
||||||
if (!item) {
|
if (!item) {
|
||||||
throw new Error('item cannot be null');
|
throw new Error('item cannot be null');
|
||||||
|
@ -783,5 +484,7 @@ class AppRouter {
|
||||||
|
|
||||||
export const appRouter = new AppRouter();
|
export const appRouter = new AppRouter();
|
||||||
|
|
||||||
|
export const isLyricsPage = () => history.location.pathname.toLowerCase() === '/lyrics';
|
||||||
|
|
||||||
window.Emby = window.Emby || {};
|
window.Emby = window.Emby || {};
|
||||||
window.Emby.Page = appRouter;
|
window.Emby.Page = appRouter;
|
||||||
|
|
|
@ -9,6 +9,7 @@ import ServerConnections from './components/ServerConnections';
|
||||||
|
|
||||||
import { appHost } from './components/apphost';
|
import { appHost } from './components/apphost';
|
||||||
import autoFocuser from './components/autoFocuser';
|
import autoFocuser from './components/autoFocuser';
|
||||||
|
import loading from 'components/loading/loading';
|
||||||
import { pluginManager } from './components/pluginManager';
|
import { pluginManager } from './components/pluginManager';
|
||||||
import { appRouter } from './components/router/appRouter';
|
import { appRouter } from './components/router/appRouter';
|
||||||
import globalize from './lib/globalize';
|
import globalize from './lib/globalize';
|
||||||
|
@ -99,6 +100,16 @@ build: ${__JF_BUILD_VERSION__}`);
|
||||||
ServerConnections.currentApiClient()?.ensureWebSocket();
|
ServerConnections.currentApiClient()?.ensureWebSocket();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Register API request error handlers
|
||||||
|
ServerConnections.getApiClients().forEach(apiClient => {
|
||||||
|
Events.off(apiClient, 'requestfail', appRouter.onRequestFail);
|
||||||
|
Events.on(apiClient, 'requestfail', appRouter.onRequestFail);
|
||||||
|
});
|
||||||
|
Events.on(ServerConnections, 'apiclientcreated', (_e, apiClient) => {
|
||||||
|
Events.off(apiClient, 'requestfail', appRouter.onRequestFail);
|
||||||
|
Events.on(apiClient, 'requestfail', appRouter.onRequestFail);
|
||||||
|
});
|
||||||
|
|
||||||
// Render the app
|
// Render the app
|
||||||
await renderApp();
|
await renderApp();
|
||||||
|
|
||||||
|
@ -250,7 +261,7 @@ async function renderApp() {
|
||||||
// Remove the splash logo
|
// Remove the splash logo
|
||||||
container.innerHTML = '';
|
container.innerHTML = '';
|
||||||
|
|
||||||
await appRouter.start();
|
loading.show();
|
||||||
|
|
||||||
const root = createRoot(container);
|
const root = createRoot(container);
|
||||||
root.render(
|
root.render(
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue