mirror of
https://github.com/jellyfin/jellyfin-web
synced 2025-03-30 19:56:21 +00:00
splitting SuggestionsView component
This commit is contained in:
parent
f4b878bea2
commit
7543e494c9
12 changed files with 311 additions and 187 deletions
|
@ -1,9 +1,9 @@
|
|||
import React, { FunctionComponent } from 'react';
|
||||
|
||||
const createButtonElement = ({ id, className }: IProps) => ({
|
||||
const createElement = ({ id, className }: IProps) => ({
|
||||
__html: `<div
|
||||
is="emby-itemscontainer"
|
||||
id="${id}"
|
||||
${id}
|
||||
class="${className}"
|
||||
>
|
||||
</div>`
|
||||
|
@ -17,8 +17,8 @@ type IProps = {
|
|||
const ItemsContainerElement: FunctionComponent<IProps> = ({ id, className }: IProps) => {
|
||||
return (
|
||||
<div
|
||||
dangerouslySetInnerHTML={createButtonElement({
|
||||
id: id,
|
||||
dangerouslySetInnerHTML={createElement({
|
||||
id: id ? `id='${id}'` : '',
|
||||
className: className
|
||||
})}
|
||||
/>
|
||||
|
|
43
src/elements/ItemsScrollerContainerElement.tsx
Normal file
43
src/elements/ItemsScrollerContainerElement.tsx
Normal file
|
@ -0,0 +1,43 @@
|
|||
import React, { FunctionComponent } from 'react';
|
||||
|
||||
const createScroller = ({ scrollerclassName, dataHorizontal, dataMousewheel, dataCenterfocus, id, className }: IProps) => ({
|
||||
__html: `<div is="emby-scroller"
|
||||
class="${scrollerclassName}"
|
||||
${dataHorizontal}
|
||||
${dataMousewheel}
|
||||
${dataCenterfocus}
|
||||
>
|
||||
<div
|
||||
is="emby-itemscontainer"
|
||||
${id}
|
||||
class="${className}"
|
||||
>
|
||||
</div>
|
||||
</div>`
|
||||
});
|
||||
|
||||
type IProps = {
|
||||
scrollerclassName?: string;
|
||||
dataHorizontal?: string;
|
||||
dataMousewheel?: string;
|
||||
dataCenterfocus?: string;
|
||||
id?: string;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
const ItemsScrollerContainerElement: FunctionComponent<IProps> = ({ scrollerclassName, dataHorizontal, dataMousewheel, dataCenterfocus, id, className }: IProps) => {
|
||||
return (
|
||||
<div
|
||||
dangerouslySetInnerHTML={createScroller({
|
||||
scrollerclassName: scrollerclassName,
|
||||
dataHorizontal: dataHorizontal ? `data-horizontal="${dataHorizontal}"` : '',
|
||||
dataMousewheel: dataMousewheel ? `data-mousewheel="${dataMousewheel}"` : '',
|
||||
dataCenterfocus: dataCenterfocus ? `data-centerfocus="${dataCenterfocus}"` : '',
|
||||
id: id ? `id='${id}'` : '',
|
||||
className: className
|
||||
})}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export default ItemsScrollerContainerElement;
|
60
src/view/components/RecentlyAddedItemsContainer.tsx
Normal file
60
src/view/components/RecentlyAddedItemsContainer.tsx
Normal file
|
@ -0,0 +1,60 @@
|
|||
import '../../elements/emby-itemscontainer/emby-itemscontainer';
|
||||
|
||||
import { BaseItemDto } from '@thornbill/jellyfin-sdk/dist/generated-client';
|
||||
import React, { FunctionComponent, useEffect, useRef } from 'react';
|
||||
|
||||
import cardBuilder from '../../components/cardbuilder/cardBuilder';
|
||||
import globalize from '../../scripts/globalize';
|
||||
import ItemsContainerElement from '../../elements/ItemsContainerElement';
|
||||
|
||||
type RecentlyAddedItemsContainerProps = {
|
||||
getPortraitShape: () => string;
|
||||
enableScrollX: () => boolean;
|
||||
items?: BaseItemDto[];
|
||||
}
|
||||
|
||||
const RecentlyAddedItemsContainer: FunctionComponent<RecentlyAddedItemsContainerProps> = ({ getPortraitShape, enableScrollX, items = [] }: RecentlyAddedItemsContainerProps) => {
|
||||
const element = useRef<HTMLDivElement>(null);
|
||||
|
||||
useEffect(() => {
|
||||
const section = element.current?.querySelector('#recentlyAddedItemsSection') as HTMLDivElement;
|
||||
if (items?.length) {
|
||||
section.classList.remove('hide');
|
||||
} else {
|
||||
section.classList.add('hide');
|
||||
}
|
||||
|
||||
const allowBottomPadding = !enableScrollX();
|
||||
const container = element.current?.querySelector('#recentlyAddedItems');
|
||||
cardBuilder.buildCards(items, {
|
||||
itemsContainer: container,
|
||||
shape: getPortraitShape(),
|
||||
scalable: true,
|
||||
overlayPlayButton: true,
|
||||
allowBottomPadding: allowBottomPadding,
|
||||
showTitle: true,
|
||||
showYear: true,
|
||||
centerText: true
|
||||
});
|
||||
}, [enableScrollX, getPortraitShape, items]);
|
||||
|
||||
return (
|
||||
<div ref={element}>
|
||||
<div id='recentlyAddedItemsSection' className='verticalSection hide'>
|
||||
<div className='sectionTitleContainer sectionTitleContainer-cards'>
|
||||
<h2 className='sectionTitle sectionTitle-cards padded-left'>
|
||||
{globalize.translate('HeaderLatestMovies')}
|
||||
</h2>
|
||||
</div>
|
||||
|
||||
<ItemsContainerElement
|
||||
id='recentlyAddedItems'
|
||||
className='itemsContainer padded-left padded-right'
|
||||
/>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default RecentlyAddedItemsContainer;
|
80
src/view/components/RecommendationContainer.tsx
Normal file
80
src/view/components/RecommendationContainer.tsx
Normal file
|
@ -0,0 +1,80 @@
|
|||
import '../../elements/emby-itemscontainer/emby-itemscontainer';
|
||||
|
||||
import { RecommendationDto } from '@thornbill/jellyfin-sdk/dist/generated-client';
|
||||
import React, { FunctionComponent, useEffect, useRef } from 'react';
|
||||
|
||||
import cardBuilder from '../../components/cardbuilder/cardBuilder';
|
||||
import globalize from '../../scripts/globalize';
|
||||
import ItemsContainerElement from '../../elements/ItemsContainerElement';
|
||||
import ItemsScrollerContainerElement from '../../elements/ItemsScrollerContainerElement';
|
||||
import escapeHTML from 'escape-html';
|
||||
|
||||
type RecommendationContainerProps = {
|
||||
getPortraitShape: () => string;
|
||||
enableScrollX: () => boolean;
|
||||
recommendation?: RecommendationDto;
|
||||
}
|
||||
|
||||
const RecommendationContainer: FunctionComponent<RecommendationContainerProps> = ({ getPortraitShape, enableScrollX, recommendation = {} }: RecommendationContainerProps) => {
|
||||
const element = useRef<HTMLDivElement>(null);
|
||||
|
||||
let title = '';
|
||||
|
||||
switch (recommendation.RecommendationType) {
|
||||
case 'SimilarToRecentlyPlayed':
|
||||
title = globalize.translate('RecommendationBecauseYouWatched', recommendation.BaselineItemName);
|
||||
break;
|
||||
|
||||
case 'SimilarToLikedItem':
|
||||
title = globalize.translate('RecommendationBecauseYouLike', recommendation.BaselineItemName);
|
||||
break;
|
||||
|
||||
case 'HasDirectorFromRecentlyPlayed':
|
||||
case 'HasLikedDirector':
|
||||
title = globalize.translate('RecommendationDirectedBy', recommendation.BaselineItemName);
|
||||
break;
|
||||
|
||||
case 'HasActorFromRecentlyPlayed':
|
||||
case 'HasLikedActor':
|
||||
title = globalize.translate('RecommendationStarring', recommendation.BaselineItemName);
|
||||
break;
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
cardBuilder.buildCards(recommendation.Items || [], {
|
||||
itemsContainer: element.current?.querySelector('.itemsContainer'),
|
||||
parentContainer: element.current,
|
||||
shape: getPortraitShape(),
|
||||
scalable: true,
|
||||
overlayPlayButton: true,
|
||||
allowBottomPadding: true,
|
||||
showTitle: true,
|
||||
showYear: true,
|
||||
centerText: true
|
||||
});
|
||||
}, [enableScrollX, getPortraitShape, recommendation]);
|
||||
|
||||
return (
|
||||
<div ref={element}>
|
||||
<div className='verticalSection'>
|
||||
<div className='sectionTitleContainer sectionTitleContainer-cards'>
|
||||
<h2 className='sectionTitle sectionTitle-cards padded-left'>
|
||||
{escapeHTML(title)}
|
||||
</h2>
|
||||
</div>
|
||||
|
||||
{enableScrollX() ? <ItemsScrollerContainerElement
|
||||
scrollerclassName='padded-top-focusscale padded-bottom-focusscale'
|
||||
dataMousewheel='false'
|
||||
dataCenterfocus='true'
|
||||
className='itemsContainer scrollSlider focuscontainer-x'
|
||||
/> : <ItemsContainerElement
|
||||
className='itemsContainer focuscontainer-x padded-left padded-right vertical-wrap'
|
||||
/>}
|
||||
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default RecommendationContainer;
|
62
src/view/components/ResumableItemsContainer.tsx
Normal file
62
src/view/components/ResumableItemsContainer.tsx
Normal file
|
@ -0,0 +1,62 @@
|
|||
import '../../elements/emby-itemscontainer/emby-itemscontainer';
|
||||
|
||||
import { BaseItemDtoQueryResult } from '@thornbill/jellyfin-sdk/dist/generated-client';
|
||||
import React, { FunctionComponent, useEffect, useRef } from 'react';
|
||||
|
||||
import cardBuilder from '../../components/cardbuilder/cardBuilder';
|
||||
import globalize from '../../scripts/globalize';
|
||||
import ItemsContainerElement from '../../elements/ItemsContainerElement';
|
||||
|
||||
type ResumableItemsContainerProps = {
|
||||
getThumbShape: () => string;
|
||||
enableScrollX: () => boolean;
|
||||
itemsResult?: BaseItemDtoQueryResult;
|
||||
}
|
||||
|
||||
const ResumableItemsContainer: FunctionComponent<ResumableItemsContainerProps> = ({ getThumbShape, enableScrollX, itemsResult = {} }: ResumableItemsContainerProps) => {
|
||||
const element = useRef<HTMLDivElement>(null);
|
||||
|
||||
useEffect(() => {
|
||||
const section = element.current?.querySelector('#resumableSection') as HTMLDivElement;
|
||||
if (itemsResult.Items?.length) {
|
||||
section.classList.remove('hide');
|
||||
} else {
|
||||
section.classList.add('hide');
|
||||
}
|
||||
|
||||
const allowBottomPadding = !enableScrollX();
|
||||
const container = element.current?.querySelector('#resumableItems') as HTMLDivElement;
|
||||
cardBuilder.buildCards(itemsResult.Items || [], {
|
||||
itemsContainer: container,
|
||||
preferThumb: true,
|
||||
shape: getThumbShape(),
|
||||
scalable: true,
|
||||
overlayPlayButton: true,
|
||||
allowBottomPadding: allowBottomPadding,
|
||||
cardLayout: false,
|
||||
showTitle: true,
|
||||
showYear: true,
|
||||
centerText: true
|
||||
});
|
||||
}, [enableScrollX, getThumbShape, itemsResult.Items]);
|
||||
|
||||
return (
|
||||
<div ref={element}>
|
||||
<div id='resumableSection' className='verticalSection hide'>
|
||||
<div className='sectionTitleContainer sectionTitleContainer-cards'>
|
||||
<h2 className='sectionTitle sectionTitle-cards padded-left'>
|
||||
{globalize.translate('HeaderContinueWatching')}
|
||||
</h2>
|
||||
</div>
|
||||
|
||||
<ItemsContainerElement
|
||||
id='resumableItems'
|
||||
className='itemsContainer padded-left padded-right'
|
||||
/>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default ResumableItemsContainer;
|
|
@ -5,13 +5,13 @@ import * as userSettings from '../../scripts/settings/userSettings';
|
|||
import { IQuery } from './type';
|
||||
|
||||
type SortProps = {
|
||||
SortMenuOptions: () => { name: string; id: string}[];
|
||||
sortMenuOptions: () => { name: string; id: string}[];
|
||||
query: IQuery;
|
||||
savedQueryKey: string;
|
||||
reloadItems: () => void;
|
||||
}
|
||||
|
||||
const Sort: FunctionComponent<SortProps> = ({ SortMenuOptions, query, savedQueryKey, reloadItems }: SortProps) => {
|
||||
const Sort: FunctionComponent<SortProps> = ({ sortMenuOptions, query, savedQueryKey, reloadItems }: SortProps) => {
|
||||
const element = useRef<HTMLDivElement>(null);
|
||||
|
||||
useEffect(() => {
|
||||
|
@ -20,7 +20,7 @@ const Sort: FunctionComponent<SortProps> = ({ SortMenuOptions, query, savedQuery
|
|||
if (btnSort) {
|
||||
btnSort.addEventListener('click', (e) => {
|
||||
libraryBrowser.showSortMenu({
|
||||
items: SortMenuOptions(),
|
||||
items: sortMenuOptions(),
|
||||
callback: () => {
|
||||
query.StartIndex = 0;
|
||||
userSettings.saveQuerySettings(savedQueryKey, query);
|
||||
|
@ -31,7 +31,7 @@ const Sort: FunctionComponent<SortProps> = ({ SortMenuOptions, query, savedQuery
|
|||
});
|
||||
});
|
||||
}
|
||||
}, [SortMenuOptions, query, reloadItems, savedQueryKey]);
|
||||
}, [sortMenuOptions, query, reloadItems, savedQueryKey]);
|
||||
|
||||
return (
|
||||
<div ref={element}>
|
||||
|
|
|
@ -105,7 +105,7 @@ const CollectionsView: FunctionComponent<IProps> = ({ topParentId }: IProps) =>
|
|||
<Pagination itemsResult= {itemsResult} query={query} reloadItems={reloadItems} />
|
||||
|
||||
<SelectView getCurrentViewStyle={getCurrentViewStyle} savedViewKey={savedViewKey} query={query} onViewStyleChange={onViewStyleChange} reloadItems={reloadItems} />
|
||||
<Sort SortMenuOptions={SortMenuOptions} query={query} savedQueryKey={savedQueryKey} reloadItems={reloadItems} />
|
||||
<Sort sortMenuOptions={SortMenuOptions} query={query} savedQueryKey={savedQueryKey} reloadItems={reloadItems} />
|
||||
<NewCollection />
|
||||
|
||||
</div>
|
||||
|
|
|
@ -128,7 +128,7 @@ const FavoritesView: FunctionComponent<IProps> = ({ topParentId }: IProps) => {
|
|||
<Pagination itemsResult= {itemsResult} query={query} reloadItems={reloadItems} />
|
||||
|
||||
<SelectView getCurrentViewStyle={getCurrentViewStyle} savedViewKey={savedViewKey} query={query} onViewStyleChange={onViewStyleChange} reloadItems={reloadItems} />
|
||||
<Sort SortMenuOptions={SortMenuOptions} query={query} savedQueryKey={savedQueryKey} reloadItems={reloadItems} />
|
||||
<Sort sortMenuOptions={SortMenuOptions} query={query} savedQueryKey={savedQueryKey} reloadItems={reloadItems} />
|
||||
<Filter query={query} reloadItems={reloadItems} />
|
||||
|
||||
</div>
|
||||
|
|
|
@ -134,7 +134,7 @@ const MoviesView: FunctionComponent<IProps> = ({ topParentId }: IProps) => {
|
|||
<Shuffle itemsResult= {itemsResult} topParentId={topParentId} />
|
||||
|
||||
<SelectView getCurrentViewStyle={getCurrentViewStyle} savedViewKey={savedViewKey} query={query} onViewStyleChange={onViewStyleChange} reloadItems={reloadItems} />
|
||||
<Sort SortMenuOptions={SortMenuOptions} query={query} savedQueryKey={savedQueryKey} reloadItems={reloadItems} />
|
||||
<Sort sortMenuOptions={SortMenuOptions} query={query} savedQueryKey={savedQueryKey} reloadItems={reloadItems} />
|
||||
<Filter query={query} reloadItems={reloadItems} />
|
||||
|
||||
</div>
|
||||
|
|
|
@ -1,9 +0,0 @@
|
|||
import React from 'react';
|
||||
|
||||
function ResumableItems() {
|
||||
return (
|
||||
<div>ResumableItems</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default ResumableItems;
|
|
@ -1,19 +1,22 @@
|
|||
import escapeHtml from 'escape-html';
|
||||
import React, { FunctionComponent, useCallback, useEffect, useRef } from 'react';
|
||||
import { BaseItemDto, BaseItemDtoQueryResult, RecommendationDto } from '@thornbill/jellyfin-sdk/dist/generated-client';
|
||||
import React, { FunctionComponent, useCallback, useEffect, useRef, useState } from 'react';
|
||||
|
||||
import cardBuilder from '../../components/cardbuilder/cardBuilder';
|
||||
import imageLoader from '../../components/images/imageLoader';
|
||||
import layoutManager from '../../components/layoutManager';
|
||||
import loading from '../../components/loading/loading';
|
||||
import ItemsContainerElement from '../../elements/ItemsContainerElement';
|
||||
import dom from '../../scripts/dom';
|
||||
import globalize from '../../scripts/globalize';
|
||||
import RecentlyAddedItemsContainer from '../components/RecentlyAddedItemsContainer';
|
||||
import RecommendationContainer from '../components/RecommendationContainer';
|
||||
import ResumableItemsContainer from '../components/ResumableItemsContainer';
|
||||
|
||||
type IProps = {
|
||||
topParentId: string | null;
|
||||
}
|
||||
|
||||
const SuggestionsView: FunctionComponent<IProps> = (props: IProps) => {
|
||||
const [ latestItems, setLatestItems ] = useState<BaseItemDto[]>([]);
|
||||
const [ resumeItemsResult, setResumeItemsResult ] = useState<BaseItemDtoQueryResult>();
|
||||
const [ recommendations, setRecommendations ] = useState<RecommendationDto[]>([]);
|
||||
const element = useRef<HTMLDivElement>(null);
|
||||
|
||||
const enableScrollX = useCallback(() => {
|
||||
|
@ -45,23 +48,12 @@ const SuggestionsView: FunctionComponent<IProps> = (props: IProps) => {
|
|||
EnableTotalRecordCount: false
|
||||
};
|
||||
window.ApiClient.getJSON(window.ApiClient.getUrl('Users/' + userId + '/Items/Latest', options)).then(items => {
|
||||
const allowBottomPadding = !enableScrollX();
|
||||
const container = page.querySelector('#recentlyAddedItems');
|
||||
cardBuilder.buildCards(items, {
|
||||
itemsContainer: container,
|
||||
shape: getPortraitShape(),
|
||||
scalable: true,
|
||||
overlayPlayButton: true,
|
||||
allowBottomPadding: allowBottomPadding,
|
||||
showTitle: true,
|
||||
showYear: true,
|
||||
centerText: true
|
||||
});
|
||||
setLatestItems(items);
|
||||
|
||||
// FIXME: Wait for all sections to load
|
||||
autoFocus(page);
|
||||
});
|
||||
}, [autoFocus, enableScrollX, getPortraitShape]);
|
||||
}, [autoFocus]);
|
||||
|
||||
const loadResume = useCallback((page, userId, parentId) => {
|
||||
loading.show();
|
||||
|
@ -81,84 +73,13 @@ const SuggestionsView: FunctionComponent<IProps> = (props: IProps) => {
|
|||
EnableTotalRecordCount: false
|
||||
};
|
||||
window.ApiClient.getItems(userId, options).then(result => {
|
||||
if (result.Items?.length) {
|
||||
page.querySelector('#resumableSection').classList.remove('hide');
|
||||
} else {
|
||||
page.querySelector('#resumableSection').classList.add('hide');
|
||||
}
|
||||
setResumeItemsResult(result);
|
||||
|
||||
const allowBottomPadding = !enableScrollX();
|
||||
const container = page.querySelector('#resumableItems');
|
||||
cardBuilder.buildCards(result.Items || [], {
|
||||
itemsContainer: container,
|
||||
preferThumb: true,
|
||||
shape: getThumbShape(),
|
||||
scalable: true,
|
||||
overlayPlayButton: true,
|
||||
allowBottomPadding: allowBottomPadding,
|
||||
cardLayout: false,
|
||||
showTitle: true,
|
||||
showYear: true,
|
||||
centerText: true
|
||||
});
|
||||
loading.hide();
|
||||
// FIXME: Wait for all sections to load
|
||||
autoFocus(page);
|
||||
});
|
||||
}, [autoFocus, enableScrollX, getThumbShape]);
|
||||
|
||||
const getRecommendationHtml = useCallback((recommendation) => {
|
||||
let html = '';
|
||||
let title = '';
|
||||
|
||||
switch (recommendation.RecommendationType) {
|
||||
case 'SimilarToRecentlyPlayed':
|
||||
title = globalize.translate('RecommendationBecauseYouWatched', recommendation.BaselineItemName);
|
||||
break;
|
||||
|
||||
case 'SimilarToLikedItem':
|
||||
title = globalize.translate('RecommendationBecauseYouLike', recommendation.BaselineItemName);
|
||||
break;
|
||||
|
||||
case 'HasDirectorFromRecentlyPlayed':
|
||||
case 'HasLikedDirector':
|
||||
title = globalize.translate('RecommendationDirectedBy', recommendation.BaselineItemName);
|
||||
break;
|
||||
|
||||
case 'HasActorFromRecentlyPlayed':
|
||||
case 'HasLikedActor':
|
||||
title = globalize.translate('RecommendationStarring', recommendation.BaselineItemName);
|
||||
break;
|
||||
}
|
||||
|
||||
html += '<div class="verticalSection">';
|
||||
html += `<h2 class="sectionTitle sectionTitle-cards padded-left">${escapeHtml(title)}</h2>`;
|
||||
const allowBottomPadding = true;
|
||||
|
||||
if (enableScrollX()) {
|
||||
html += '<div is="emby-scroller" class="padded-top-focusscale padded-bottom-focusscale" data-mousewheel="false" data-centerfocus="true">';
|
||||
html += '<div is="emby-itemscontainer" class="itemsContainer scrollSlider focuscontainer-x">';
|
||||
} else {
|
||||
html += '<div is="emby-itemscontainer" class="itemsContainer focuscontainer-x padded-left padded-right vertical-wrap">';
|
||||
}
|
||||
|
||||
html += cardBuilder.getCardsHtml(recommendation.Items, {
|
||||
shape: getPortraitShape(),
|
||||
scalable: true,
|
||||
overlayPlayButton: true,
|
||||
allowBottomPadding: allowBottomPadding,
|
||||
showTitle: true,
|
||||
showYear: true,
|
||||
centerText: true
|
||||
});
|
||||
|
||||
if (enableScrollX()) {
|
||||
html += '</div>';
|
||||
}
|
||||
html += '</div>';
|
||||
html += '</div>';
|
||||
return html;
|
||||
}, [enableScrollX, getPortraitShape]);
|
||||
}, [autoFocus]);
|
||||
|
||||
const loadSuggestions = useCallback((page, userId) => {
|
||||
const screenWidth: any = dom.getWindowSize();
|
||||
|
@ -177,22 +98,12 @@ const SuggestionsView: FunctionComponent<IProps> = (props: IProps) => {
|
|||
EnableImageTypes: 'Primary,Backdrop,Banner,Thumb'
|
||||
});
|
||||
window.ApiClient.getJSON(url).then(recommendations => {
|
||||
if (!recommendations.length) {
|
||||
page.querySelector('.noItemsMessage').classList.remove('hide');
|
||||
page.querySelector('.recommendations').innerHTML = '';
|
||||
return;
|
||||
}
|
||||
|
||||
const html = recommendations.map(getRecommendationHtml).join('');
|
||||
page.querySelector('.noItemsMessage').classList.add('hide');
|
||||
const recs = page.querySelector('.recommendations');
|
||||
recs.innerHTML = html;
|
||||
imageLoader.lazyChildren(recs);
|
||||
setRecommendations(recommendations);
|
||||
|
||||
// FIXME: Wait for all sections to load
|
||||
autoFocus(page);
|
||||
});
|
||||
}, [autoFocus, getRecommendationHtml]);
|
||||
}, [autoFocus]);
|
||||
|
||||
const loadSuggestionsTab = useCallback((view) => {
|
||||
const parentId = props.topParentId;
|
||||
|
@ -202,8 +113,14 @@ const SuggestionsView: FunctionComponent<IProps> = (props: IProps) => {
|
|||
loadSuggestions(view, userId);
|
||||
}, [loadLatest, loadResume, loadSuggestions, props.topParentId]);
|
||||
|
||||
const initSuggestedTab = useCallback((tabContent) => {
|
||||
function setScrollClasses(elem: { classList: { add: (arg0: string) => void; remove: (arg0: string) => void; }; }, scrollX: boolean) {
|
||||
const setScrollClasses = useCallback((elem, scrollX) => {
|
||||
const page = element.current;
|
||||
|
||||
if (!page) {
|
||||
console.error('Unexpected null reference');
|
||||
return;
|
||||
}
|
||||
|
||||
if (scrollX) {
|
||||
elem.classList.add('hiddenScrollX');
|
||||
|
||||
|
@ -221,13 +138,15 @@ const SuggestionsView: FunctionComponent<IProps> = (props: IProps) => {
|
|||
elem.classList.remove('scrollX');
|
||||
elem.classList.add('vertical-wrap');
|
||||
}
|
||||
}
|
||||
const containers = tabContent.querySelectorAll('.itemsContainer');
|
||||
}, []);
|
||||
|
||||
const initSuggestedTab = useCallback((view) => {
|
||||
const containers = view.querySelectorAll('.itemsContainer');
|
||||
|
||||
for (const container of containers) {
|
||||
setScrollClasses(container, enableScrollX());
|
||||
}
|
||||
}, [enableScrollX]);
|
||||
}, [enableScrollX, setScrollClasses]);
|
||||
|
||||
useEffect(() => {
|
||||
const page = element.current;
|
||||
|
@ -238,54 +157,23 @@ const SuggestionsView: FunctionComponent<IProps> = (props: IProps) => {
|
|||
}
|
||||
|
||||
initSuggestedTab(page);
|
||||
}, [initSuggestedTab]);
|
||||
|
||||
useEffect(() => {
|
||||
const page = element.current;
|
||||
|
||||
if (!page) {
|
||||
console.error('Unexpected null reference');
|
||||
return;
|
||||
}
|
||||
loadSuggestionsTab(page);
|
||||
}, [loadSuggestionsTab]);
|
||||
}, [initSuggestedTab, loadSuggestionsTab]);
|
||||
|
||||
return (
|
||||
<div ref={element}>
|
||||
<div id='resumableSection' className='verticalSection hide'>
|
||||
<div className='sectionTitleContainer sectionTitleContainer-cards'>
|
||||
<h2 className='sectionTitle sectionTitle-cards padded-left'>
|
||||
{globalize.translate('HeaderContinueWatching')}
|
||||
</h2>
|
||||
</div>
|
||||
<ResumableItemsContainer getThumbShape={getThumbShape} enableScrollX={enableScrollX} itemsResult={resumeItemsResult} />
|
||||
|
||||
<ItemsContainerElement
|
||||
id='resumableItems'
|
||||
className='itemsContainer padded-left padded-right'
|
||||
/>
|
||||
<RecentlyAddedItemsContainer getPortraitShape={getPortraitShape} enableScrollX={enableScrollX} items={latestItems} />
|
||||
|
||||
</div>
|
||||
|
||||
<div className='verticalSection'>
|
||||
<div className='sectionTitleContainer sectionTitleContainer-cards'>
|
||||
<h2 className='sectionTitle sectionTitle-cards padded-left'>
|
||||
{globalize.translate('HeaderLatestMovies')}
|
||||
</h2>
|
||||
</div>
|
||||
|
||||
<ItemsContainerElement
|
||||
id='recentlyAddedItems'
|
||||
className='itemsContainer padded-left padded-right'
|
||||
/>
|
||||
|
||||
</div>
|
||||
|
||||
<div className='recommendations'>
|
||||
</div>
|
||||
<div className='noItemsMessage hide padded-left padded-right'>
|
||||
<br />
|
||||
<p>
|
||||
{globalize.translate('MessageNoMovieSuggestionsAvailable')}
|
||||
</p>
|
||||
<div id='recommendations'>
|
||||
{!recommendations.length ? <div className='noItemsMessage centerMessage'>
|
||||
<h1>{globalize.translate('MessageNothingHere')}</h1>
|
||||
<p>{globalize.translate('MessageNoMovieSuggestionsAvailable')}</p>
|
||||
</div> : recommendations.map((recommendation, index) => {
|
||||
return <RecommendationContainer key={index} getPortraitShape={getPortraitShape} enableScrollX={enableScrollX} recommendation={recommendation} />;
|
||||
})}
|
||||
{}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
|
|
@ -113,7 +113,7 @@ const TrailersView: FunctionComponent<IProps> = ({ topParentId }: IProps) => {
|
|||
<div className='flex align-items-center justify-content-center flex-wrap-wrap padded-top padded-left padded-right padded-bottom focuscontainer-x'>
|
||||
<Pagination itemsResult= {itemsResult} query={query} reloadItems={reloadItems} />
|
||||
|
||||
<Sort SortMenuOptions={SortMenuOptions} query={query} savedQueryKey={savedQueryKey} reloadItems={reloadItems} />
|
||||
<Sort sortMenuOptions={SortMenuOptions} query={query} savedQueryKey={savedQueryKey} reloadItems={reloadItems} />
|
||||
<Filter query={query} reloadItems={reloadItems} />
|
||||
|
||||
</div>
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue