diff --git a/src/components/Page.tsx b/src/components/Page.tsx index c4677fee48..7c072b5376 100644 --- a/src/components/Page.tsx +++ b/src/components/Page.tsx @@ -8,7 +8,8 @@ type PageProps = { isBackButtonEnabled?: boolean, isMenuButtonEnabled?: boolean, isNowPlayingBarEnabled?: boolean, - isThemeMediaSupported?: boolean + isThemeMediaSupported?: boolean, + backDropType?: string }; /** @@ -23,7 +24,8 @@ const Page: FunctionComponent> = ({ isBackButtonEnabled = true, isMenuButtonEnabled = false, isNowPlayingBarEnabled = true, - isThemeMediaSupported = false + isThemeMediaSupported = false, + backDropType }) => { const element = useRef(null); @@ -61,8 +63,9 @@ const Page: FunctionComponent> = ({ data-role='page' className={`page ${className}`} data-title={title} - data-backbutton={`${isBackButtonEnabled}`} - data-menubutton={`${isMenuButtonEnabled}`} + data-backbutton={isBackButtonEnabled} + data-menubutton={isMenuButtonEnabled} + data-backdroptype={backDropType} > {children} diff --git a/src/components/tabbedview/tabbedview.js b/src/components/tabbedview/tabbedview.js deleted file mode 100644 index 46b14ed36e..0000000000 --- a/src/components/tabbedview/tabbedview.js +++ /dev/null @@ -1,114 +0,0 @@ -import { clearBackdrop } from '../backdrop/backdrop'; -import * as mainTabsManager from '../maintabsmanager'; -import layoutManager from '../layoutManager'; -import '../../elements/emby-tabs/emby-tabs'; -import LibraryMenu from '../../scripts/libraryMenu'; - -function onViewDestroy() { - const tabControllers = this.tabControllers; - - if (tabControllers) { - tabControllers.forEach(function (t) { - if (t.destroy) { - t.destroy(); - } - }); - - this.tabControllers = null; - } - - this.view = null; - this.params = null; - this.currentTabController = null; - this.initialTabIndex = null; -} - -class TabbedView { - constructor(view, params) { - this.tabControllers = []; - this.view = view; - this.params = params; - - const self = this; - - let currentTabIndex = parseInt(params.tab || this.getDefaultTabIndex(params.parentId)); - this.initialTabIndex = currentTabIndex; - - function validateTabLoad(index) { - return self.validateTabLoad ? self.validateTabLoad(index) : Promise.resolve(); - } - - function loadTab(index, previousIndex) { - validateTabLoad(index).then(function () { - self.getTabController(index).then(function (controller) { - const refresh = !controller.refreshed; - - controller.onResume({ - autoFocus: previousIndex == null && layoutManager.tv, - refresh: refresh - }); - - controller.refreshed = true; - - currentTabIndex = index; - self.currentTabController = controller; - }); - }); - } - - function getTabContainers() { - return view.querySelectorAll('.tabContent'); - } - - function onTabChange(e) { - const newIndex = parseInt(e.detail.selectedTabIndex); - const previousIndex = e.detail.previousIndex; - - const previousTabController = previousIndex == null ? null : self.tabControllers[previousIndex]; - if (previousTabController && previousTabController.onPause) { - previousTabController.onPause(); - } - - loadTab(newIndex, previousIndex); - } - - view.addEventListener('viewbeforehide', this.onPause.bind(this)); - - view.addEventListener('viewbeforeshow', function () { - mainTabsManager.setTabs(view, currentTabIndex, self.getTabs, getTabContainers, null, onTabChange, false); - }); - - view.addEventListener('viewshow', function (e) { - self.onResume(e.detail); - }); - - view.addEventListener('viewdestroy', onViewDestroy.bind(this)); - } - - onResume() { - this.setTitle(); - clearBackdrop(); - - const currentTabController = this.currentTabController; - - if (!currentTabController) { - mainTabsManager.selectedTabIndex(this.initialTabIndex); - } else if (currentTabController && currentTabController.onResume) { - currentTabController.onResume({}); - } - } - - onPause() { - const currentTabController = this.currentTabController; - - if (currentTabController && currentTabController.onPause) { - currentTabController.onPause(); - } - } - - setTitle() { - LibraryMenu.setTitle(''); - } -} - -export default TabbedView; diff --git a/src/controllers/home.html b/src/controllers/home.html deleted file mode 100644 index 240caef6c6..0000000000 --- a/src/controllers/home.html +++ /dev/null @@ -1,9 +0,0 @@ -
- -
-
-
-
-
-
-
diff --git a/src/controllers/home.js b/src/controllers/home.js deleted file mode 100644 index feb29b542c..0000000000 --- a/src/controllers/home.js +++ /dev/null @@ -1,69 +0,0 @@ -import TabbedView from '../components/tabbedview/tabbedview'; -import globalize from '../scripts/globalize'; -import '../elements/emby-tabs/emby-tabs'; -import '../elements/emby-button/emby-button'; -import '../elements/emby-scroller/emby-scroller'; -import LibraryMenu from '../scripts/libraryMenu'; - -class HomeView extends TabbedView { - constructor(view, params) { - super(view, params); - } - - setTitle() { - LibraryMenu.setTitle(null); - } - - onPause() { - super.onPause(this); - document.querySelector('.skinHeader').classList.remove('noHomeButtonHeader'); - } - - onResume(options) { - super.onResume(this, options); - document.querySelector('.skinHeader').classList.add('noHomeButtonHeader'); - } - - getDefaultTabIndex() { - return 0; - } - - getTabs() { - return [{ - name: globalize.translate('Home') - }, { - name: globalize.translate('Favorites') - }]; - } - - getTabController(index) { - if (index == null) { - throw new Error('index cannot be null'); - } - - let depends = ''; - - switch (index) { - case 0: - depends = 'hometab'; - break; - - case 1: - depends = 'favorites'; - } - - const instance = this; - return import(/* webpackChunkName: "[request]" */ `../controllers/${depends}`).then(({ default: controllerFactory }) => { - let controller = instance.tabControllers[index]; - - if (!controller) { - controller = new controllerFactory(instance.view.querySelector(".tabContent[data-index='" + index + "']"), instance.params); - instance.tabControllers[index] = controller; - } - - return controller; - }); - } -} - -export default HomeView; diff --git a/src/routes/home.tsx b/src/routes/home.tsx new file mode 100644 index 0000000000..182475b0b3 --- /dev/null +++ b/src/routes/home.tsx @@ -0,0 +1,177 @@ +import React, { FunctionComponent, useCallback, useEffect, useMemo, useRef } from 'react'; +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 IProps = { + tab?: string; +} + +type OnResumeOptions = { + autoFocus?: boolean; + refresh?: boolean +} + +type ControllerProps = { + onResume: ( + options: OnResumeOptions + ) => void; + refreshed: boolean; + onPause: () => void; + destroy: () => void; +} + +const Home: FunctionComponent = (props: IProps) => { + const getDefaultTabIndex = () => { + return 0; + }; + + const tabController = useRef(); + const currentTabIndex = useRef(parseInt(props.tab || getDefaultTabIndex().toString())); + const tabControllers = useMemo(() => [], []); + const initialTabIndex = useRef(currentTabIndex.current); + const element = useRef(null); + + const setTitle = () => { + LibraryMenu.setTitle(null); + }; + + const getTabs = () => { + return [{ + name: globalize.translate('Home') + }, { + name: globalize.translate('Favorites') + }]; + }; + + const getTabContainers = () => { + return element.current?.querySelectorAll('.tabContent'); + }; + + const getTabController = useCallback((index: number) => { + if (index == null) { + throw new Error('index cannot be null'); + } + + let depends = ''; + + switch (index) { + case 0: + depends = 'hometab'; + break; + + case 1: + depends = 'favorites'; + } + + return import(/* webpackChunkName: "[request]" */ `../controllers/${depends}`).then(({ default: controllerFactory }) => { + let controller = tabControllers[index]; + + if (!controller) { + const tabContent = element.current?.querySelector(".tabContent[data-index='" + index + "']"); + controller = new controllerFactory(tabContent, props); + tabControllers[index] = controller; + } + + return controller; + }); + }, [props, tabControllers]); + + const onViewDestroy = useCallback(() => { + if (tabControllers) { + tabControllers.forEach(function (t) { + if (t.destroy) { + t.destroy(); + } + }); + } + + tabController.current = null; + initialTabIndex.current = null; + }, [tabControllers]); + + const loadTab = useCallback((index: number, previousIndex: number | null) => { + getTabController(index).then((controller) => { + const refresh = !controller.refreshed; + + controller.onResume({ + autoFocus: previousIndex == null && layoutManager.tv, + refresh: refresh + }); + + controller.refreshed = true; + currentTabIndex.current = index; + tabController.current = controller; + }); + }, [getTabController]); + + const onTabChange = useCallback((e: { detail: { selectedTabIndex: string; previousIndex: number | null }; }) => { + const newIndex = parseInt(e.detail.selectedTabIndex); + const previousIndex = e.detail.previousIndex; + + const previousTabController = previousIndex == null ? null : tabControllers[previousIndex]; + if (previousTabController && previousTabController.onPause) { + previousTabController.onPause(); + } + + loadTab(newIndex, previousIndex); + }, [loadTab, tabControllers]); + + const onResume = useCallback(() => { + setTitle(); + clearBackdrop(); + + const currentTabController = tabController.current; + + if (!currentTabController) { + mainTabsManager.selectedTabIndex(initialTabIndex.current); + } else if (currentTabController && currentTabController.onResume) { + currentTabController.onResume({}); + } + (document.querySelector('.skinHeader') as HTMLDivElement).classList.add('noHomeButtonHeader'); + }, []); + + const onPause = useCallback(() => { + const currentTabController = tabController.current; + if (currentTabController && currentTabController.onPause) { + currentTabController.onPause(); + } + (document.querySelector('.skinHeader') as HTMLDivElement).classList.remove('noHomeButtonHeader'); + }, []); + + useEffect(() => { + mainTabsManager.setTabs(element.current, currentTabIndex.current, getTabs, getTabContainers, null, onTabChange, false); + + onResume(); + return () => { + onPause(); + onViewDestroy(); + }; + }, [onPause, onResume, onTabChange, onViewDestroy]); + + return ( +
+ +
+
+
+
+
+
+
+
+ ); +}; + +export default Home; diff --git a/src/routes/index.tsx b/src/routes/index.tsx index 6d23886c57..1ab3277e7e 100644 --- a/src/routes/index.tsx +++ b/src/routes/index.tsx @@ -10,6 +10,7 @@ import UserParentalControl from './user/userparentalcontrol'; import UserPassword from './user/userpassword'; import UserProfile from './user/userprofile'; import UserProfiles from './user/userprofiles'; +import Home from './home'; const AppRoutes = () => ( @@ -18,6 +19,7 @@ const AppRoutes = () => ( }> } /> } /> + } /> {/* Admin routes */} diff --git a/src/scripts/routes.js b/src/scripts/routes.js index 2135c65ba5..8367f9b483 100644 --- a/src/scripts/routes.js +++ b/src/scripts/routes.js @@ -293,14 +293,6 @@ import { appRouter } from '../components/appRouter'; controller: 'dashboard/plugins/repositories/index' }); - defineRoute({ - alias: '/home.html', - path: 'home.html', - autoFocus: false, - controller: 'home', - type: 'home' - }); - defineRoute({ alias: '/list.html', path: 'list.html',