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

125 lines
3.8 KiB
TypeScript
Raw Normal View History

import { Action } from 'history';
import { FunctionComponent, useEffect, useRef } from 'react';
import { useLocation, useNavigationType } from 'react-router-dom';
2024-08-15 02:33:50 -04:00
import globalize from 'lib/globalize';
import type { RestoreViewFailResponse } from 'types/viewManager';
import viewManager from './viewManager';
2022-10-24 02:17:35 -04:00
export interface ViewManagerPageProps {
controller: string
view: string
type?: string
isFullscreen?: boolean
isNowPlayingBarEnabled?: boolean
isThemeMediaSupported?: boolean
transition?: string
}
interface ViewOptions {
url: string
type?: string
// eslint-disable-next-line @typescript-eslint/no-explicit-any
state: any
autoFocus: boolean
fullscreen?: boolean
transition?: string
options: {
supportsThemeMedia?: boolean
enableMediaControl?: boolean
}
}
const loadView = async (controller: string, view: string, viewOptions: ViewOptions) => {
const [ controllerFactory, viewHtml ] = await Promise.all([
import(/* webpackChunkName: "[request]" */ `../../controllers/${controller}`),
import(/* webpackChunkName: "[request]" */ `../../controllers/${view}`)
.then(html => globalize.translateHtml(html))
]);
viewManager.loadView({
...viewOptions,
controllerFactory,
view: viewHtml
});
};
/**
* Page component that renders legacy views via the ViewManager.
2022-10-25 12:39:12 -04:00
* NOTE: Any new pages should use the generic Page component instead.
*/
const ViewManagerPage: FunctionComponent<ViewManagerPageProps> = ({
controller,
view,
type,
isFullscreen = false,
isNowPlayingBarEnabled = true,
isThemeMediaSupported = false,
transition
}) => {
/**
* HACK: This is a hack to workaround intentional behavior in React strict mode when running in development.
* Legacy views will break if loaded twice so we need to avoid that. This will likely stop working in React 19.
* refs: https://stackoverflow.com/a/72238236
*/
const isLoaded = useRef(false);
const location = useLocation();
const navigationType = useNavigationType();
useEffect(() => {
2022-11-04 12:27:14 -04:00
const loadPage = () => {
2022-10-24 02:17:35 -04:00
const viewOptions = {
url: location.pathname + location.search,
type,
state: location.state,
autoFocus: false,
fullscreen: isFullscreen,
transition,
options: {
supportsThemeMedia: isThemeMediaSupported,
enableMediaControl: isNowPlayingBarEnabled
}
2022-10-24 02:17:35 -04:00
};
if (navigationType !== Action.Pop) {
console.debug('[ViewManagerPage] loading view [%s]', view);
return loadView(controller, view, viewOptions);
}
console.debug('[ViewManagerPage] restoring view [%s]', view);
return viewManager.tryRestoreView(viewOptions)
2023-04-26 11:30:57 -04:00
.catch(async (result?: RestoreViewFailResponse) => {
2023-07-06 13:39:48 -04:00
if (!result?.cancelled) {
console.debug('[ViewManagerPage] restore failed; loading view [%s]', view);
return loadView(controller, view, viewOptions);
2022-10-24 02:17:35 -04:00
}
});
};
if (!isLoaded.current) loadPage();
return () => {
isLoaded.current = true;
};
},
// location.state and navigationType are NOT included as dependencies here since dialogs will update state while the current view stays the same
// eslint-disable-next-line react-hooks/exhaustive-deps
[
controller,
view,
type,
isFullscreen,
isNowPlayingBarEnabled,
isThemeMediaSupported,
transition,
location.pathname,
location.search
]);
2023-10-23 23:14:21 +03:30
return null;
};
export default ViewManagerPage;