mirror of
https://github.com/jellyfin/jellyfin-web.git
synced 2025-04-10 22:39:49 +00:00
Fix async route page refresh not loading header Original-merge: 3d20694109c6ad86203bb7b2c791dedcf5ade223 Merged-by: thornbill <thornbill@users.noreply.github.com> Backported-by: thornbill <thornbill@users.noreply.github.com>
268 lines
8.8 KiB
JavaScript
268 lines
8.8 KiB
JavaScript
// Import legacy browser polyfills
|
|
import 'lib/legacy';
|
|
|
|
import React from 'react';
|
|
import { createRoot } from 'react-dom/client';
|
|
|
|
// NOTE: We need to import this first to initialize the connection
|
|
import ServerConnections from './components/ServerConnections';
|
|
|
|
import { appHost } from './components/apphost';
|
|
import autoFocuser from './components/autoFocuser';
|
|
import loading from 'components/loading/loading';
|
|
import { pluginManager } from './components/pluginManager';
|
|
import { appRouter } from './components/router/appRouter';
|
|
import globalize from './lib/globalize';
|
|
import { loadCoreDictionary } from 'lib/globalize/loader';
|
|
import { initialize as initializeAutoCast } from 'scripts/autocast';
|
|
import browser from './scripts/browser';
|
|
import keyboardNavigation from './scripts/keyboardNavigation';
|
|
import { currentSettings } from './scripts/settings/userSettings';
|
|
import { getPlugins } from './scripts/settings/webSettings';
|
|
import taskButton from './scripts/taskbutton';
|
|
import { pageClassOn, serverAddress } from './utils/dashboard';
|
|
import Events from './utils/events';
|
|
|
|
import RootApp from './RootApp';
|
|
import { history } from 'RootAppRouter';
|
|
|
|
// Import the button webcomponent for use throughout the site
|
|
// NOTE: This is a bit of a hack, files should ensure the component is imported before use
|
|
import './elements/emby-button/emby-button';
|
|
|
|
// Import auto-running components
|
|
// NOTE: This is an anti-pattern
|
|
import './components/playback/displayMirrorManager';
|
|
import './components/playback/playerSelectionMenu';
|
|
import './components/themeMediaPlayer';
|
|
import './scripts/autoThemes';
|
|
import './scripts/mouseManager';
|
|
import './scripts/screensavermanager';
|
|
import './scripts/serverNotifications';
|
|
|
|
// Import site styles
|
|
import './styles/site.scss';
|
|
import './styles/livetv.scss';
|
|
import './styles/dashboard.scss';
|
|
import './styles/detailtable.scss';
|
|
import './styles/librarybrowser.scss';
|
|
|
|
async function init() {
|
|
// Log current version to console to help out with issue triage and debugging
|
|
console.info(
|
|
`[${__PACKAGE_JSON_NAME__}]
|
|
version: ${__PACKAGE_JSON_VERSION__}
|
|
commit: ${__COMMIT_SHA__}
|
|
build: ${__JF_BUILD_VERSION__}`);
|
|
|
|
// Register globals used in plugins
|
|
window.Events = Events;
|
|
window.TaskButton = taskButton;
|
|
|
|
// Register handlers to update header classes
|
|
pageClassOn('viewshow', 'standalonePage', function () {
|
|
document.querySelector('.skinHeader').classList.add('noHeaderRight');
|
|
});
|
|
pageClassOn('viewhide', 'standalonePage', function () {
|
|
document.querySelector('.skinHeader').classList.remove('noHeaderRight');
|
|
});
|
|
|
|
// Initialize the api client
|
|
const serverUrl = await serverAddress();
|
|
if (serverUrl) {
|
|
ServerConnections.initApiClient(serverUrl);
|
|
}
|
|
|
|
// Initialize automatic (default) cast target
|
|
initializeAutoCast();
|
|
|
|
// Load the translation dictionary
|
|
await loadCoreDictionary();
|
|
// Update localization on user changes
|
|
Events.on(ServerConnections, 'localusersignedin', globalize.updateCurrentCulture);
|
|
Events.on(ServerConnections, 'localusersignedout', globalize.updateCurrentCulture);
|
|
// Localize the document title
|
|
document.title = globalize.translateHtml(document.title, 'core');
|
|
|
|
// Load the font styles
|
|
loadFonts();
|
|
|
|
// Load iOS specific styles
|
|
if (browser.iOS) {
|
|
import('./styles/ios.scss');
|
|
}
|
|
|
|
// Load frontend plugins
|
|
await loadPlugins();
|
|
|
|
// Establish the websocket connection
|
|
Events.on(appHost, 'resume', () => {
|
|
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
|
|
await renderApp();
|
|
|
|
// Load platform specific features
|
|
loadPlatformFeatures();
|
|
|
|
// Load custom CSS styles
|
|
loadCustomCss();
|
|
|
|
// Enable navigation controls
|
|
keyboardNavigation.enable();
|
|
autoFocuser.enable();
|
|
}
|
|
|
|
function loadFonts() {
|
|
if (browser.tv && !browser.android) {
|
|
console.debug('using system fonts with explicit sizes');
|
|
import('./styles/fonts.sized.scss');
|
|
} else if (__USE_SYSTEM_FONTS__) {
|
|
console.debug('using system fonts');
|
|
import('./styles/fonts.scss');
|
|
} else {
|
|
console.debug('using default fonts');
|
|
import('./styles/fonts.scss');
|
|
import('./styles/fonts.noto.scss');
|
|
}
|
|
}
|
|
|
|
async function loadPlugins() {
|
|
console.groupCollapsed('loading installed plugins');
|
|
console.dir(pluginManager);
|
|
|
|
let list = await getPlugins();
|
|
if (!appHost.supports('remotecontrol')) {
|
|
// Disable remote player plugins if not supported
|
|
list = list.filter(plugin => !plugin.startsWith('sessionPlayer')
|
|
&& !plugin.startsWith('chromecastPlayer'));
|
|
} else if (!browser.chrome && !browser.edgeChromium && !browser.opera) {
|
|
// Disable chromecast player in unsupported browsers
|
|
list = list.filter(plugin => !plugin.startsWith('chromecastPlayer'));
|
|
}
|
|
|
|
// add any native plugins
|
|
if (window.NativeShell) {
|
|
list = list.concat(window.NativeShell.getPlugins());
|
|
}
|
|
|
|
try {
|
|
await Promise.all(list.map(plugin => pluginManager.loadPlugin(plugin)));
|
|
console.debug('finished loading plugins');
|
|
} catch (e) {
|
|
console.warn('failed loading plugins', e);
|
|
}
|
|
|
|
console.groupEnd('loading installed plugins');
|
|
}
|
|
|
|
function loadPlatformFeatures() {
|
|
if (!browser.tv && !browser.xboxOne && !browser.ps4) {
|
|
import('./components/nowPlayingBar/nowPlayingBar');
|
|
}
|
|
|
|
if (appHost.supports('remotecontrol')) {
|
|
import('./components/playback/playerSelectionMenu');
|
|
import('./components/playback/remotecontrolautoplay');
|
|
}
|
|
|
|
if (!appHost.supports('physicalvolumecontrol') || browser.touch) {
|
|
import('./components/playback/volumeosd');
|
|
}
|
|
|
|
if (!browser.tv && !browser.xboxOne) {
|
|
import('./components/playback/playbackorientation');
|
|
registerServiceWorker();
|
|
|
|
if (window.Notification) {
|
|
import('./components/notifications/notifications');
|
|
}
|
|
}
|
|
}
|
|
|
|
function loadCustomCss() {
|
|
// Apply custom CSS
|
|
const apiClient = ServerConnections.currentApiClient();
|
|
if (apiClient) {
|
|
const brandingCss = fetch(apiClient.getUrl('Branding/Css'))
|
|
.then(function(response) {
|
|
if (!response.ok) {
|
|
throw new Error(response.status + ' ' + response.statusText);
|
|
}
|
|
return response.text();
|
|
})
|
|
.catch(function(err) {
|
|
console.warn('Error applying custom css', err);
|
|
});
|
|
|
|
const handleStyleChange = async () => {
|
|
let style = document.querySelector('#cssBranding');
|
|
if (!style) {
|
|
// Inject the branding css as a dom element in body so it will take
|
|
// precedence over other stylesheets
|
|
style = document.createElement('style');
|
|
style.id = 'cssBranding';
|
|
document.body.appendChild(style);
|
|
}
|
|
|
|
const css = [];
|
|
// Only add branding CSS when enabled
|
|
if (!currentSettings.disableCustomCss()) css.push(await brandingCss);
|
|
// Always add user CSS
|
|
css.push(currentSettings.customCss());
|
|
|
|
style.textContent = css.join('\n');
|
|
};
|
|
|
|
Events.on(ServerConnections, 'localusersignedin', handleStyleChange);
|
|
Events.on(ServerConnections, 'localusersignedout', handleStyleChange);
|
|
Events.on(currentSettings, 'change', (e, prop) => {
|
|
if (prop == 'disableCustomCss' || prop == 'customCss') {
|
|
handleStyleChange();
|
|
}
|
|
});
|
|
|
|
handleStyleChange();
|
|
}
|
|
}
|
|
|
|
function registerServiceWorker() {
|
|
/* eslint-disable compat/compat */
|
|
if (navigator.serviceWorker && window.appMode !== 'cordova' && window.appMode !== 'android') {
|
|
navigator.serviceWorker.register('serviceworker.js').then(() =>
|
|
console.log('serviceWorker registered')
|
|
).catch(error =>
|
|
console.log('error registering serviceWorker: ' + error)
|
|
);
|
|
} else {
|
|
console.warn('serviceWorker unsupported');
|
|
}
|
|
/* eslint-enable compat/compat */
|
|
}
|
|
|
|
async function renderApp() {
|
|
const container = document.getElementById('reactRoot');
|
|
// Remove the splash logo
|
|
container.innerHTML = '';
|
|
|
|
loading.show();
|
|
|
|
const root = createRoot(container);
|
|
root.render(
|
|
<RootApp history={history} />
|
|
);
|
|
}
|
|
|
|
init();
|