mirror of
https://github.com/jellyfin/jellyfin-web.git
synced 2024-11-17 19:08:18 -07:00
migrate Home Page
This commit is contained in:
parent
3f94db1af8
commit
4699e9de60
@ -8,7 +8,9 @@ type PageProps = {
|
|||||||
isBackButtonEnabled?: boolean,
|
isBackButtonEnabled?: boolean,
|
||||||
isMenuButtonEnabled?: boolean,
|
isMenuButtonEnabled?: boolean,
|
||||||
isNowPlayingBarEnabled?: boolean,
|
isNowPlayingBarEnabled?: boolean,
|
||||||
isThemeMediaSupported?: boolean
|
isThemeMediaSupported?: boolean,
|
||||||
|
isDomCacheEnabled?: boolean,
|
||||||
|
backDropType?: string
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -23,7 +25,9 @@ const Page: FunctionComponent<PageProps & HTMLAttributes<HTMLDivElement>> = ({
|
|||||||
isBackButtonEnabled = true,
|
isBackButtonEnabled = true,
|
||||||
isMenuButtonEnabled = false,
|
isMenuButtonEnabled = false,
|
||||||
isNowPlayingBarEnabled = true,
|
isNowPlayingBarEnabled = true,
|
||||||
isThemeMediaSupported = false
|
isThemeMediaSupported = false,
|
||||||
|
isDomCacheEnabled = false,
|
||||||
|
backDropType
|
||||||
}) => {
|
}) => {
|
||||||
const element = useRef<HTMLDivElement>(null);
|
const element = useRef<HTMLDivElement>(null);
|
||||||
|
|
||||||
@ -63,6 +67,8 @@ const Page: FunctionComponent<PageProps & HTMLAttributes<HTMLDivElement>> = ({
|
|||||||
data-title={title}
|
data-title={title}
|
||||||
data-backbutton={`${isBackButtonEnabled}`}
|
data-backbutton={`${isBackButtonEnabled}`}
|
||||||
data-menubutton={`${isMenuButtonEnabled}`}
|
data-menubutton={`${isMenuButtonEnabled}`}
|
||||||
|
data-dom-cache={`${isDomCacheEnabled}`}
|
||||||
|
data-backdroptype={`${backDropType}`}
|
||||||
>
|
>
|
||||||
{children}
|
{children}
|
||||||
</div>
|
</div>
|
||||||
|
@ -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;
|
|
@ -1,9 +0,0 @@
|
|||||||
<div id="indexPage" style="outline: none;" data-role="page" data-dom-cache="true" class="page homePage libraryPage allLibraryPage backdropPage pageWithAbsoluteTabs withTabs" data-backdroptype="movie,series,book">
|
|
||||||
|
|
||||||
<div class="tabContent pageTabContent" id="homeTab" data-index="0">
|
|
||||||
<div class="sections"></div>
|
|
||||||
</div>
|
|
||||||
<div class="tabContent pageTabContent" id="favoritesTab" data-index="1">
|
|
||||||
<div class="sections"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
@ -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;
|
|
177
src/routes/home.tsx
Normal file
177
src/routes/home.tsx
Normal file
@ -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<IProps> = (props: IProps) => {
|
||||||
|
const getDefaultTabIndex = () => {
|
||||||
|
return 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
const tabController = useRef<ControllerProps | null>();
|
||||||
|
const currentTabIndex = useRef(parseInt(props.tab || getDefaultTabIndex().toString()));
|
||||||
|
const tabControllers = useMemo<ControllerProps[]>(() => [], []);
|
||||||
|
const initialTabIndex = useRef<number | null>(currentTabIndex.current);
|
||||||
|
const element = useRef<HTMLDivElement>(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 (
|
||||||
|
<div ref={element}>
|
||||||
|
<Page
|
||||||
|
id='indexPage'
|
||||||
|
className='mainAnimatedPage homePage libraryPage allLibraryPage backdropPage pageWithAbsoluteTabs withTabs'
|
||||||
|
isDomCacheEnabled= {true}
|
||||||
|
backDropType='movie,series,book'
|
||||||
|
>
|
||||||
|
<div className='tabContent pageTabContent' id='homeTab' data-index='0'>
|
||||||
|
<div className='sections'></div>
|
||||||
|
</div>
|
||||||
|
<div className='tabContent pageTabContent' id='favoritesTab' data-index='1'>
|
||||||
|
<div className='sections'></div>
|
||||||
|
</div>
|
||||||
|
</Page>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Home;
|
@ -10,6 +10,7 @@ import UserParentalControl from './user/userparentalcontrol';
|
|||||||
import UserPassword from './user/userpassword';
|
import UserPassword from './user/userpassword';
|
||||||
import UserProfile from './user/userprofile';
|
import UserProfile from './user/userprofile';
|
||||||
import UserProfiles from './user/userprofiles';
|
import UserProfiles from './user/userprofiles';
|
||||||
|
import Home from './home';
|
||||||
|
|
||||||
const AppRoutes = () => (
|
const AppRoutes = () => (
|
||||||
<Routes>
|
<Routes>
|
||||||
@ -18,6 +19,7 @@ const AppRoutes = () => (
|
|||||||
<Route path='/' element={<ConnectionRequired />}>
|
<Route path='/' element={<ConnectionRequired />}>
|
||||||
<Route path='search.html' element={<Search />} />
|
<Route path='search.html' element={<Search />} />
|
||||||
<Route path='userprofile.html' element={<UserProfile />} />
|
<Route path='userprofile.html' element={<UserProfile />} />
|
||||||
|
<Route path='home.html' element={<Home />} />
|
||||||
</Route>
|
</Route>
|
||||||
|
|
||||||
{/* Admin routes */}
|
{/* Admin routes */}
|
||||||
|
@ -293,14 +293,6 @@ import { appRouter } from '../components/appRouter';
|
|||||||
controller: 'dashboard/plugins/repositories/index'
|
controller: 'dashboard/plugins/repositories/index'
|
||||||
});
|
});
|
||||||
|
|
||||||
defineRoute({
|
|
||||||
alias: '/home.html',
|
|
||||||
path: 'home.html',
|
|
||||||
autoFocus: false,
|
|
||||||
controller: 'home',
|
|
||||||
type: 'home'
|
|
||||||
});
|
|
||||||
|
|
||||||
defineRoute({
|
defineRoute({
|
||||||
alias: '/list.html',
|
alias: '/list.html',
|
||||||
path: 'list.html',
|
path: 'list.html',
|
||||||
|
Loading…
Reference in New Issue
Block a user