apply suggestion
This commit is contained in:
parent
7805e86f70
commit
d7e48d30b6
21 changed files with 172 additions and 140 deletions
|
@ -11,12 +11,12 @@ import Page from '../components/Page';
|
|||
import globalize from '../scripts/globalize';
|
||||
import libraryMenu from '../scripts/libraryMenu';
|
||||
import * as userSettings from '../scripts/settings/userSettings';
|
||||
import CollectionsView from '../view/movies/CollectionsView';
|
||||
import FavoritesView from '../view/movies/FavoritesView';
|
||||
import GenresView from '../view/movies/GenresView';
|
||||
import MoviesView from '../view/movies/MoviesView';
|
||||
import SuggestionsView from '../view/movies/SuggestionsView';
|
||||
import TrailersView from '../view/movies/TrailersView';
|
||||
import CollectionsView from './movies/CollectionsView';
|
||||
import FavoritesView from './movies/FavoritesView';
|
||||
import GenresView from './movies/GenresView';
|
||||
import MoviesView from './movies/MoviesView';
|
||||
import SuggestionsView from './movies/SuggestionsView';
|
||||
import TrailersView from './movies/TrailersView';
|
||||
|
||||
const getDefaultTabIndex = (folderId: string | null) => {
|
||||
switch (userSettings.get('landing-' + folderId, false)) {
|
||||
|
|
35
src/routes/movies/CollectionsView.tsx
Normal file
35
src/routes/movies/CollectionsView.tsx
Normal file
|
@ -0,0 +1,35 @@
|
|||
import React, { FC, useCallback } from 'react';
|
||||
|
||||
import ViewItemsContainer from '../../components/common/ViewItemsContainer';
|
||||
|
||||
interface CollectionsViewI {
|
||||
topParentId: string | null;
|
||||
}
|
||||
|
||||
const CollectionsView: FC<CollectionsViewI> = ({ topParentId }) => {
|
||||
const getBasekey = useCallback(() => {
|
||||
return 'collections';
|
||||
}, []);
|
||||
|
||||
const getItemTypes = useCallback(() => {
|
||||
return ['BoxSet'];
|
||||
}, []);
|
||||
|
||||
const getNoItemsMessage = useCallback(() => {
|
||||
return 'MessageNoCollectionsAvailable';
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<ViewItemsContainer
|
||||
topParentId={topParentId}
|
||||
isBtnFilterEnabled={false}
|
||||
isBtnNewCollectionEnabled={true}
|
||||
isAlphaPickerEnabled={false}
|
||||
getBasekey={getBasekey}
|
||||
getItemTypes={getItemTypes}
|
||||
getNoItemsMessage={getNoItemsMessage}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export default CollectionsView;
|
32
src/routes/movies/FavoritesView.tsx
Normal file
32
src/routes/movies/FavoritesView.tsx
Normal file
|
@ -0,0 +1,32 @@
|
|||
import React, { FC, useCallback } from 'react';
|
||||
|
||||
import ViewItemsContainer from '../../components/common/ViewItemsContainer';
|
||||
|
||||
interface FavoritesViewI {
|
||||
topParentId: string | null;
|
||||
}
|
||||
|
||||
const FavoritesView: FC<FavoritesViewI> = ({ topParentId }) => {
|
||||
const getBasekey = useCallback(() => {
|
||||
return 'favorites';
|
||||
}, []);
|
||||
|
||||
const getItemTypes = useCallback(() => {
|
||||
return ['Movie'];
|
||||
}, []);
|
||||
|
||||
const getNoItemsMessage = useCallback(() => {
|
||||
return 'MessageNoFavoritesAvailable';
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<ViewItemsContainer
|
||||
topParentId={topParentId}
|
||||
getBasekey={getBasekey}
|
||||
getItemTypes={getItemTypes}
|
||||
getNoItemsMessage={getNoItemsMessage}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export default FavoritesView;
|
59
src/routes/movies/GenresView.tsx
Normal file
59
src/routes/movies/GenresView.tsx
Normal file
|
@ -0,0 +1,59 @@
|
|||
import type { BaseItemDtoQueryResult } from '@jellyfin/sdk/lib/generated-client';
|
||||
import React, { FC, useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
||||
|
||||
import loading from '../../components/loading/loading';
|
||||
import * as userSettings from '../../scripts/settings/userSettings';
|
||||
import GenresItemsContainer from '../../components/common/GenresItemsContainer';
|
||||
import { Query } from '../../types/interface';
|
||||
|
||||
interface GenresViewI {
|
||||
topParentId: string | null;
|
||||
}
|
||||
|
||||
const GenresView: FC<GenresViewI> = ({ topParentId }) => {
|
||||
const [ itemsResult, setItemsResult ] = useState<BaseItemDtoQueryResult>({});
|
||||
const element = useRef<HTMLDivElement>(null);
|
||||
|
||||
const getSettingsKey = useCallback(() => {
|
||||
return topParentId + '-genres';
|
||||
}, [topParentId]);
|
||||
|
||||
const getViewSettings = useCallback(() => {
|
||||
return getSettingsKey() + '-view';
|
||||
}, [getSettingsKey]);
|
||||
|
||||
let query = useMemo<Query>(() => ({
|
||||
SortBy: 'SortName',
|
||||
SortOrder: 'Ascending',
|
||||
IncludeItemTypes: 'Movie',
|
||||
Recursive: true,
|
||||
EnableTotalRecordCount: false,
|
||||
Limit: userSettings.libraryPageSize(undefined),
|
||||
StartIndex: 0,
|
||||
ParentId: topParentId }), [topParentId]);
|
||||
|
||||
query = userSettings.loadQuerySettings(getSettingsKey(), query);
|
||||
|
||||
const getCurrentViewStyle = useCallback(() => {
|
||||
return userSettings.get(getViewSettings(), false) || 'Poster';
|
||||
}, [getViewSettings]);
|
||||
|
||||
const reloadItems = useCallback(() => {
|
||||
loading.show();
|
||||
window.ApiClient.getGenres(window.ApiClient.getCurrentUserId(), query).then((result) => {
|
||||
setItemsResult(result);
|
||||
loading.hide();
|
||||
});
|
||||
}, [query]);
|
||||
|
||||
useEffect(() => {
|
||||
reloadItems();
|
||||
}, [reloadItems]);
|
||||
return (
|
||||
<div ref={element}>
|
||||
<GenresItemsContainer topParentId={topParentId} getCurrentViewStyle={getCurrentViewStyle} itemsResult={itemsResult} />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default GenresView;
|
33
src/routes/movies/MoviesView.tsx
Normal file
33
src/routes/movies/MoviesView.tsx
Normal file
|
@ -0,0 +1,33 @@
|
|||
import React, { FC, useCallback } from 'react';
|
||||
|
||||
import ViewItemsContainer from '../../components/common/ViewItemsContainer';
|
||||
|
||||
interface MoviesViewI {
|
||||
topParentId: string | null;
|
||||
}
|
||||
|
||||
const MoviesView: FC<MoviesViewI> = ({ topParentId }) => {
|
||||
const getBasekey = useCallback(() => {
|
||||
return 'movies';
|
||||
}, []);
|
||||
|
||||
const getItemTypes = useCallback(() => {
|
||||
return ['Movie'];
|
||||
}, []);
|
||||
|
||||
const getNoItemsMessage = useCallback(() => {
|
||||
return 'MessageNoItemsAvailable';
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<ViewItemsContainer
|
||||
topParentId={topParentId}
|
||||
isBtnShuffleEnabled={true}
|
||||
getBasekey={getBasekey}
|
||||
getItemTypes={getItemTypes}
|
||||
getNoItemsMessage={getNoItemsMessage}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export default MoviesView;
|
156
src/routes/movies/SuggestionsView.tsx
Normal file
156
src/routes/movies/SuggestionsView.tsx
Normal file
|
@ -0,0 +1,156 @@
|
|||
import type { BaseItemDto, BaseItemDtoQueryResult, RecommendationDto } from '@jellyfin/sdk/lib/generated-client';
|
||||
import React, { FC, useCallback, useEffect, useRef, useState } from 'react';
|
||||
|
||||
import layoutManager from '../../components/layoutManager';
|
||||
import loading from '../../components/loading/loading';
|
||||
import dom from '../../scripts/dom';
|
||||
import globalize from '../../scripts/globalize';
|
||||
import RecommendationContainer from '../../components/common/RecommendationContainer';
|
||||
import SectionContainer from '../../components/common/SectionContainer';
|
||||
|
||||
interface SuggestionsViewI {
|
||||
topParentId: string | null;
|
||||
}
|
||||
|
||||
const SuggestionsView: FC<SuggestionsViewI> = ({topParentId}) => {
|
||||
const [ latestItems, setLatestItems ] = useState<BaseItemDto[]>([]);
|
||||
const [ resumeResult, setResumeResult ] = useState<BaseItemDtoQueryResult>({});
|
||||
const [ recommendations, setRecommendations ] = useState<RecommendationDto[]>([]);
|
||||
const element = useRef<HTMLDivElement>(null);
|
||||
|
||||
const enableScrollX = useCallback(() => {
|
||||
return !layoutManager.desktop;
|
||||
}, []);
|
||||
|
||||
const getPortraitShape = useCallback(() => {
|
||||
return enableScrollX() ? 'overflowPortrait' : 'portrait';
|
||||
}, [enableScrollX]);
|
||||
|
||||
const getThumbShape = useCallback(() => {
|
||||
return enableScrollX() ? 'overflowBackdrop' : 'backdrop';
|
||||
}, [enableScrollX]);
|
||||
|
||||
const autoFocus = useCallback((page) => {
|
||||
import('../../components/autoFocuser').then(({default: autoFocuser}) => {
|
||||
autoFocuser.autoFocus(page);
|
||||
});
|
||||
}, []);
|
||||
|
||||
const loadResume = useCallback((page, userId, parentId) => {
|
||||
loading.show();
|
||||
const screenWidth = dom.getWindowSize().innerWidth;
|
||||
const options = {
|
||||
SortBy: 'DatePlayed',
|
||||
SortOrder: 'Descending',
|
||||
IncludeItemTypes: 'Movie',
|
||||
Filters: 'IsResumable',
|
||||
Limit: screenWidth >= 1600 ? 5 : 3,
|
||||
Recursive: true,
|
||||
Fields: 'PrimaryImageAspectRatio,MediaSourceCount,BasicSyncInfo',
|
||||
CollapseBoxSetItems: false,
|
||||
ParentId: parentId,
|
||||
ImageTypeLimit: 1,
|
||||
EnableImageTypes: 'Primary,Backdrop,Banner,Thumb',
|
||||
EnableTotalRecordCount: false
|
||||
};
|
||||
window.ApiClient.getItems(userId, options).then(result => {
|
||||
setResumeResult(result);
|
||||
|
||||
loading.hide();
|
||||
autoFocus(page);
|
||||
});
|
||||
}, [autoFocus]);
|
||||
|
||||
const loadLatest = useCallback((page: HTMLDivElement, userId: string, parentId: string | null) => {
|
||||
const options = {
|
||||
IncludeItemTypes: 'Movie',
|
||||
Limit: 18,
|
||||
Fields: 'PrimaryImageAspectRatio,MediaSourceCount,BasicSyncInfo',
|
||||
ParentId: parentId,
|
||||
ImageTypeLimit: 1,
|
||||
EnableImageTypes: 'Primary,Backdrop,Banner,Thumb',
|
||||
EnableTotalRecordCount: false
|
||||
};
|
||||
window.ApiClient.getJSON(window.ApiClient.getUrl('Users/' + userId + '/Items/Latest', options)).then(items => {
|
||||
setLatestItems(items);
|
||||
|
||||
autoFocus(page);
|
||||
});
|
||||
}, [autoFocus]);
|
||||
|
||||
const loadSuggestions = useCallback((page, userId) => {
|
||||
const screenWidth = dom.getWindowSize().innerWidth;
|
||||
let itemLimit = 5;
|
||||
if (screenWidth >= 1600) {
|
||||
itemLimit = 8;
|
||||
} else if (screenWidth >= 1200) {
|
||||
itemLimit = 6;
|
||||
}
|
||||
const url = window.window.ApiClient.getUrl('Movies/Recommendations', {
|
||||
userId: userId,
|
||||
categoryLimit: 6,
|
||||
ItemLimit: itemLimit,
|
||||
Fields: 'PrimaryImageAspectRatio,MediaSourceCount,BasicSyncInfo',
|
||||
ImageTypeLimit: 1,
|
||||
EnableImageTypes: 'Primary,Backdrop,Banner,Thumb'
|
||||
});
|
||||
window.ApiClient.getJSON(url).then(result => {
|
||||
setRecommendations(result);
|
||||
|
||||
autoFocus(page);
|
||||
});
|
||||
}, [autoFocus]);
|
||||
|
||||
const loadSuggestionsTab = useCallback((view) => {
|
||||
const parentId = topParentId;
|
||||
const userId = window.ApiClient.getCurrentUserId();
|
||||
loadResume(view, userId, parentId);
|
||||
loadLatest(view, userId, parentId);
|
||||
loadSuggestions(view, userId);
|
||||
}, [loadLatest, loadResume, loadSuggestions, topParentId]);
|
||||
|
||||
useEffect(() => {
|
||||
const page = element.current;
|
||||
|
||||
if (!page) {
|
||||
console.error('Unexpected null reference');
|
||||
return;
|
||||
}
|
||||
|
||||
loadSuggestionsTab(page);
|
||||
}, [loadSuggestionsTab]);
|
||||
|
||||
return (
|
||||
<div ref={element}>
|
||||
<SectionContainer
|
||||
sectionTitle={globalize.translate('HeaderContinueWatching')}
|
||||
enableScrollX={enableScrollX}
|
||||
items={resumeResult.Items || []}
|
||||
cardOptions={{
|
||||
preferThumb: true,
|
||||
shape: getThumbShape(),
|
||||
showYear: true
|
||||
}}
|
||||
/>
|
||||
|
||||
<SectionContainer
|
||||
sectionTitle={globalize.translate('HeaderLatestMovies')}
|
||||
enableScrollX={enableScrollX}
|
||||
items={latestItems}
|
||||
cardOptions={{
|
||||
shape: getPortraitShape(),
|
||||
showYear: true
|
||||
}}
|
||||
/>
|
||||
|
||||
{!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>
|
||||
);
|
||||
};
|
||||
|
||||
export default SuggestionsView;
|
33
src/routes/movies/TrailersView.tsx
Normal file
33
src/routes/movies/TrailersView.tsx
Normal file
|
@ -0,0 +1,33 @@
|
|||
|
||||
import React, { FC, useCallback } from 'react';
|
||||
|
||||
import ViewItemsContainer from '../../components/common/ViewItemsContainer';
|
||||
|
||||
interface TrailersViewI {
|
||||
topParentId: string | null;
|
||||
}
|
||||
|
||||
const TrailersView: FC<TrailersViewI> = ({ topParentId }) => {
|
||||
const getBasekey = useCallback(() => {
|
||||
return 'trailers';
|
||||
}, []);
|
||||
|
||||
const getItemTypes = useCallback(() => {
|
||||
return ['Trailer'];
|
||||
}, []);
|
||||
|
||||
const getNoItemsMessage = useCallback(() => {
|
||||
return 'MessageNoTrailersFound';
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<ViewItemsContainer
|
||||
topParentId={topParentId}
|
||||
getBasekey={getBasekey}
|
||||
getItemTypes={getItemTypes}
|
||||
getNoItemsMessage={getNoItemsMessage}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export default TrailersView;
|
Loading…
Add table
Add a link
Reference in a new issue