mirror of
https://github.com/jellyfin/jellyfin-web.git
synced 2024-11-17 19:08:18 -07:00
Merge pull request #3563 from thornbill/history-router
Use history for app router
This commit is contained in:
commit
6412156210
35
package-lock.json
generated
35
package-lock.json
generated
@ -2083,7 +2083,6 @@
|
||||
"version": "7.13.10",
|
||||
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.13.10.tgz",
|
||||
"integrity": "sha512-4QPkjJq6Ns3V/RgpEahRk+AGfL0eO6RHHtTWoNNr5mO49G6B5+X6d6THgWEAvTrznU5xYpbAlVKRYcsCgh/Akw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"regenerator-runtime": "^0.13.4"
|
||||
}
|
||||
@ -7020,6 +7019,14 @@
|
||||
"resolved": "https://registry.npmjs.org/headroom.js/-/headroom.js-0.12.0.tgz",
|
||||
"integrity": "sha512-iXnAafUm3FdzfJ91uixLws2hkKI1jC8bAKK/pt7XYr8Ie1jO7xbK48Ycpl9tUPyBgkzuj1p/PhJS0fy4E/5anA=="
|
||||
},
|
||||
"history": {
|
||||
"version": "5.3.0",
|
||||
"resolved": "https://registry.npmjs.org/history/-/history-5.3.0.tgz",
|
||||
"integrity": "sha512-ZqaKwjjrAYUYfLG+htGaIIZ4nioX2L70ZUMIFysS3xvBsSG4x/n1V6TXV3N8ZYNuFGlDirFg32T7B6WOUPDYcQ==",
|
||||
"requires": {
|
||||
"@babel/runtime": "^7.7.6"
|
||||
}
|
||||
},
|
||||
"hls.js": {
|
||||
"version": "0.14.17",
|
||||
"resolved": "https://registry.npmjs.org/hls.js/-/hls.js-0.14.17.tgz",
|
||||
@ -8902,14 +8909,6 @@
|
||||
"integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==",
|
||||
"dev": true
|
||||
},
|
||||
"page": {
|
||||
"version": "1.11.6",
|
||||
"resolved": "https://registry.npmjs.org/page/-/page-1.11.6.tgz",
|
||||
"integrity": "sha512-P6e2JfzkBrPeFCIPplLP7vDDiU84RUUZMrWdsH4ZBGJ8OosnwFkcUkBHp1DTIjuipLliw9yQn/ZJsXZvarsO+g==",
|
||||
"requires": {
|
||||
"path-to-regexp": "~1.2.1"
|
||||
}
|
||||
},
|
||||
"pako": {
|
||||
"version": "1.0.11",
|
||||
"resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz",
|
||||
@ -9016,21 +9015,6 @@
|
||||
"integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==",
|
||||
"dev": true
|
||||
},
|
||||
"path-to-regexp": {
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-1.2.1.tgz",
|
||||
"integrity": "sha1-szcFwUAjTYc8hyHHuf2LVB7Tr/k=",
|
||||
"requires": {
|
||||
"isarray": "0.0.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"isarray": {
|
||||
"version": "0.0.1",
|
||||
"resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz",
|
||||
"integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8="
|
||||
}
|
||||
}
|
||||
},
|
||||
"path-type": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz",
|
||||
@ -10841,8 +10825,7 @@
|
||||
"regenerator-runtime": {
|
||||
"version": "0.13.7",
|
||||
"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.7.tgz",
|
||||
"integrity": "sha512-a54FxoJDIr27pgf7IgeQGxmqUNYrcV338lf/6gH456HZ/PhX+5BcwHXG9ajESmwe6WRO0tAzRUrRmNONWgkrew==",
|
||||
"dev": true
|
||||
"integrity": "sha512-a54FxoJDIr27pgf7IgeQGxmqUNYrcV338lf/6gH456HZ/PhX+5BcwHXG9ajESmwe6WRO0tAzRUrRmNONWgkrew=="
|
||||
},
|
||||
"regenerator-transform": {
|
||||
"version": "0.14.5",
|
||||
|
@ -80,6 +80,7 @@
|
||||
"fast-text-encoding": "1.0.3",
|
||||
"flv.js": "1.6.2",
|
||||
"headroom.js": "0.12.0",
|
||||
"history": "5.3.0",
|
||||
"hls.js": "0.14.17",
|
||||
"intersection-observer": "0.12.0",
|
||||
"jellyfin-apiclient": "1.10.0",
|
||||
@ -91,7 +92,6 @@
|
||||
"marked": "4.0.10",
|
||||
"material-design-icons-iconfont": "6.1.1",
|
||||
"native-promise-only": "0.8.1",
|
||||
"page": "1.11.6",
|
||||
"pdfjs-dist": "2.12.313",
|
||||
"react": "17.0.2",
|
||||
"react-dom": "17.0.2",
|
||||
|
@ -1,20 +1,22 @@
|
||||
import { Events } from 'jellyfin-apiclient';
|
||||
import { Action, createHashHistory } from 'history';
|
||||
|
||||
import { appHost } from './apphost';
|
||||
import appSettings from '../scripts/settings/appSettings';
|
||||
import { clearBackdrop, setBackdropTransparency } from './backdrop/backdrop';
|
||||
import browser from '../scripts/browser';
|
||||
import { Events } from 'jellyfin-apiclient';
|
||||
import globalize from '../scripts/globalize';
|
||||
import itemHelper from './itemHelper';
|
||||
import loading from './loading/loading';
|
||||
import page from 'page';
|
||||
import viewManager from './viewManager/viewManager';
|
||||
import Dashboard from '../utils/dashboard';
|
||||
import ServerConnections from './ServerConnections';
|
||||
import alert from './alert';
|
||||
import reactControllerFactory from './reactControllerFactory';
|
||||
|
||||
const history = createHashHistory();
|
||||
|
||||
class AppRouter {
|
||||
allRoutes = [];
|
||||
allRoutes = new Map();
|
||||
backdropContainer;
|
||||
backgroundContainer;
|
||||
currentRouteInfo;
|
||||
@ -23,7 +25,6 @@ class AppRouter {
|
||||
forcedLogoutMsg;
|
||||
isDummyBackToHome;
|
||||
msgTimeout;
|
||||
popstateOccurred = false;
|
||||
promiseShow;
|
||||
resolveOnNextShow;
|
||||
previousRoute = {};
|
||||
@ -33,58 +34,24 @@ class AppRouter {
|
||||
startPages = ['home', 'login', 'selectserver'];
|
||||
|
||||
constructor() {
|
||||
// WebKit fires a popstate event on document load
|
||||
// Skip it using boolean
|
||||
// For Tizen 2.x
|
||||
// See `page` node module
|
||||
let loaded = document.readyState === 'complete';
|
||||
if (!loaded) {
|
||||
window.addEventListener('load', () => {
|
||||
setTimeout(() => {
|
||||
loaded = true;
|
||||
}, 0);
|
||||
});
|
||||
}
|
||||
window.addEventListener('popstate', () => {
|
||||
if (!loaded) return;
|
||||
this.popstateOccurred = true;
|
||||
});
|
||||
|
||||
document.addEventListener('viewshow', () => this.onViewShow());
|
||||
|
||||
// TODO: Can this baseRoute logic be simplified?
|
||||
this.baseRoute = window.location.href.split('?')[0].replace(this.getRequestFile(), '');
|
||||
// support hashbang
|
||||
this.baseRoute = this.baseRoute.split('#')[0];
|
||||
if (this.baseRoute.endsWith('/') && !this.baseRoute.endsWith('://')) {
|
||||
this.baseRoute = this.baseRoute.substring(0, this.baseRoute.length - 1);
|
||||
}
|
||||
}
|
||||
|
||||
this.setBaseRoute();
|
||||
|
||||
// paths that start with a hashbang (i.e. /#!/page.html) get transformed to starting with //
|
||||
// we need to strip one "/" for our routes to work
|
||||
page('//*', (ctx) => {
|
||||
page.redirect(ctx.path.substring(1));
|
||||
addRoute(path, route) {
|
||||
this.allRoutes.set(path, {
|
||||
route,
|
||||
handler: this.#getHandler(route)
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
setBaseRoute() {
|
||||
let baseRoute = window.location.pathname.replace(this.getRequestFile(), '');
|
||||
if (baseRoute.lastIndexOf('/') === baseRoute.length - 1) {
|
||||
baseRoute = baseRoute.substring(0, baseRoute.length - 1);
|
||||
}
|
||||
console.debug('setting page base to ' + baseRoute);
|
||||
page.base(baseRoute);
|
||||
}
|
||||
|
||||
addRoute(path, newRoute) {
|
||||
page(path, this.getHandler(newRoute));
|
||||
this.allRoutes.push(newRoute);
|
||||
}
|
||||
|
||||
showLocalLogin(serverId) {
|
||||
Dashboard.navigate('login.html?serverid=' + serverId);
|
||||
}
|
||||
@ -124,7 +91,7 @@ class AppRouter {
|
||||
|
||||
this.promiseShow = new Promise((resolve) => {
|
||||
this.resolveOnNextShow = resolve;
|
||||
page.back();
|
||||
history.back();
|
||||
});
|
||||
|
||||
return this.promiseShow;
|
||||
@ -155,7 +122,7 @@ class AppRouter {
|
||||
this.promiseShow = new Promise((resolve) => {
|
||||
this.resolveOnNextShow = resolve;
|
||||
// Schedule a call to return the promise
|
||||
setTimeout(() => page.show(path, options), 0);
|
||||
setTimeout(() => history.push(path, options), 0);
|
||||
});
|
||||
|
||||
return this.promiseShow;
|
||||
@ -167,27 +134,50 @@ class AppRouter {
|
||||
this.promiseShow = new Promise((resolve) => {
|
||||
this.resolveOnNextShow = resolve;
|
||||
// Schedule a call to return the promise
|
||||
setTimeout(() => page.show(this.baseUrl() + path), 0);
|
||||
setTimeout(() => history.push(this.baseUrl() + path), 0);
|
||||
});
|
||||
|
||||
return this.promiseShow;
|
||||
}
|
||||
|
||||
start(options) {
|
||||
#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 {
|
||||
console.warn('[appRouter] "%s" route not found', normalizedPath, location);
|
||||
}
|
||||
}
|
||||
|
||||
start() {
|
||||
loading.show();
|
||||
this.initApiClients();
|
||||
|
||||
Events.on(appHost, 'beforeexit', this.onBeforeExit);
|
||||
Events.on(appHost, 'resume', this.onAppResume);
|
||||
|
||||
ServerConnections.connect({
|
||||
enableAutoLogin: appSettings.enableAutoLogin()
|
||||
}).then((result) => {
|
||||
this.firstConnectionResult = result;
|
||||
options = options || {};
|
||||
page({
|
||||
click: options.click !== false,
|
||||
hashbang: options.hashbang !== false
|
||||
|
||||
// Handle the initial route
|
||||
this.#goToRoute({ location: history.location });
|
||||
|
||||
// Handle route changes
|
||||
history.listen(params => {
|
||||
this.#goToRoute(params);
|
||||
});
|
||||
}).catch().then(() => {
|
||||
loading.hide();
|
||||
@ -262,19 +252,6 @@ class AppRouter {
|
||||
setBackdropTransparency(level);
|
||||
}
|
||||
|
||||
getRoutes() {
|
||||
return this.allRoutes;
|
||||
}
|
||||
|
||||
pushState(state, title, url) {
|
||||
state.navigate = false;
|
||||
window.history.pushState(state, title, url);
|
||||
}
|
||||
|
||||
enableNativeHistory() {
|
||||
return false;
|
||||
}
|
||||
|
||||
handleConnectionResult(result) {
|
||||
switch (result.State) {
|
||||
case 'SignedIn':
|
||||
@ -452,12 +429,6 @@ class AppRouter {
|
||||
}
|
||||
}
|
||||
|
||||
onBeforeExit() {
|
||||
if (browser.web0s) {
|
||||
page.restorePreviousState();
|
||||
}
|
||||
}
|
||||
|
||||
normalizeImageOptions(options) {
|
||||
let setQuality;
|
||||
if (options.maxWidth || options.width || options.maxHeight || options.height || options.fillWidth || options.fillHeight) {
|
||||
@ -544,12 +515,12 @@ class AppRouter {
|
||||
const apiClient = ServerConnections.currentApiClient();
|
||||
const pathname = ctx.pathname.toLowerCase();
|
||||
|
||||
console.debug('processing path request: ' + pathname);
|
||||
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 || !apiClient.isLoggedIn()) && !route.anonymous) {
|
||||
console.debug('route does not allow anonymous access: redirecting to login');
|
||||
console.debug('[appRouter] route does not allow anonymous access: redirecting to login');
|
||||
this.beginConnectionWizard();
|
||||
return;
|
||||
}
|
||||
@ -563,10 +534,10 @@ class AppRouter {
|
||||
}
|
||||
|
||||
if (apiClient && apiClient.isLoggedIn()) {
|
||||
console.debug('user is authenticated');
|
||||
console.debug('[appRouter] user is authenticated');
|
||||
|
||||
if (route.isDefaultRoute) {
|
||||
console.debug('loading home page');
|
||||
console.debug('[appRouter] loading home page');
|
||||
this.goHome();
|
||||
return;
|
||||
} else if (route.roles) {
|
||||
@ -577,7 +548,7 @@ class AppRouter {
|
||||
}
|
||||
}
|
||||
|
||||
console.debug('proceeding to page: ' + pathname);
|
||||
console.debug('[appRouter] proceeding to page: ' + pathname);
|
||||
callback();
|
||||
}
|
||||
|
||||
@ -632,11 +603,8 @@ class AppRouter {
|
||||
return path;
|
||||
}
|
||||
|
||||
getHandler(route) {
|
||||
#getHandler(route) {
|
||||
return (ctx, next) => {
|
||||
ctx.isBack = this.popstateOccurred;
|
||||
this.popstateOccurred = false;
|
||||
|
||||
const ignore = route.dummyRoute === true || this.previousRoute.dummyRoute === true;
|
||||
this.previousRoute = route;
|
||||
if (ignore) {
|
||||
|
@ -566,13 +566,7 @@ import { appRouter } from '../components/appRouter';
|
||||
});
|
||||
|
||||
defineRoute({
|
||||
path: '',
|
||||
isDefaultRoute: true,
|
||||
autoFocus: false
|
||||
});
|
||||
|
||||
defineRoute({
|
||||
path: 'index.html',
|
||||
path: '/',
|
||||
autoFocus: false,
|
||||
isDefaultRoute: true
|
||||
});
|
||||
|
@ -163,10 +163,7 @@ async function onAppReady() {
|
||||
import('../assets/css/ios.scss');
|
||||
}
|
||||
|
||||
appRouter.start({
|
||||
click: false,
|
||||
hashbang: true
|
||||
});
|
||||
appRouter.start();
|
||||
|
||||
if (!browser.tv && !browser.xboxOne && !browser.ps4) {
|
||||
import('../components/nowPlayingBar/nowPlayingBar');
|
||||
|
Loading…
Reference in New Issue
Block a user