diff --git a/src/scripts/dom.js b/src/scripts/dom.js index e336088c87..06d30235b2 100644 --- a/src/scripts/dom.js +++ b/src/scripts/dom.js @@ -145,9 +145,15 @@ windowSize = null; } + /** + * @typedef {Object} windowSize + * @property {number} innerHeight - window innerHeight. + * @property {number} innerWidth - window innerWidth. + */ + /** * Returns window size. - * @returns {Object} Window size. + * @returns {windowSize} Window size. */ export function getWindowSize() { if (!windowSize) { diff --git a/src/scripts/settings/userSettings.js b/src/scripts/settings/userSettings.js index 062749020c..d670bb0102 100644 --- a/src/scripts/settings/userSettings.js +++ b/src/scripts/settings/userSettings.js @@ -467,11 +467,17 @@ export class UserSettings { return this.get('soundeffects', false); } + /** + * @typedef {Object} Query + * @property {number} StartIndex - query StartIndex. + * @property {number} Limit - query Limit. + */ + /** * Load query settings. * @param {string} key - Query key. * @param {Object} query - Query base. - * @return {Object} Query. + * @return {Query} Query. */ loadQuerySettings(key, query) { let values = this.get(key); diff --git a/src/strings/en-us.json b/src/strings/en-us.json index 37e3c5bb43..7106cbceee 100644 --- a/src/strings/en-us.json +++ b/src/strings/en-us.json @@ -1065,6 +1065,7 @@ "MessageItemsAdded": "Items added.", "MessageItemSaved": "Item saved.", "MessageLeaveEmptyToInherit": "Leave empty to inherit settings from a parent item or the global default value.", + "MessageNoItemsAvailable": "No Items are currently available.", "MessageNoFavoritesAvailable": "No favorites are currently available.", "MessageNoAvailablePlugins": "No available plugins.", "MessageNoCollectionsAvailable": "Collections allow you to enjoy personalized groupings of Movies, Series, and Albums. Click the '+' button to start creating collections.", diff --git a/src/view/components/AlphaPickerContainer.tsx b/src/view/components/AlphaPickerContainer.tsx index dd4cc36f2e..b85e99e0bd 100644 --- a/src/view/components/AlphaPickerContainer.tsx +++ b/src/view/components/AlphaPickerContainer.tsx @@ -1,4 +1,4 @@ -import React, { FunctionComponent, useEffect, useRef, useState } from 'react'; +import React, { FunctionComponent, useCallback, useEffect, useRef, useState } from 'react'; import AlphaPicker from '../../components/alphaPicker/alphaPicker'; import { IQuery } from './type'; @@ -13,34 +13,35 @@ const AlphaPickerContainer: FunctionComponent = ({ query, relo alphaPicker?.updateControls(query); + const onAlphaPickerChange = useCallback((e) => { + const newValue = (e as CustomEvent).detail.value; + if (newValue === '#') { + query.NameLessThan = 'A'; + delete query.NameStartsWith; + } else { + query.NameStartsWith = newValue; + delete query.NameLessThan; + } + query.StartIndex = 0; + reloadItems(); + }, [query, reloadItems]); + useEffect(() => { const alphaPickerElement = element.current?.querySelector('.alphaPicker'); - if (alphaPickerElement) { - alphaPickerElement.addEventListener('alphavaluechanged', (e) => { - const newValue = (e as CustomEvent).detail.value; - if (newValue === '#') { - query.NameLessThan = 'A'; - delete query.NameStartsWith; - } else { - query.NameStartsWith = newValue; - delete query.NameLessThan; - } - query.StartIndex = 0; - reloadItems(); - }); - setAlphaPicker(new AlphaPicker({ - element: alphaPickerElement, - valueChangeEvent: 'click' - })); + setAlphaPicker(new AlphaPicker({ + element: alphaPickerElement, + valueChangeEvent: 'click' + })); - alphaPickerElement.classList.add('alphaPicker-fixed-right'); + if (alphaPickerElement) { + alphaPickerElement.addEventListener('alphavaluechanged', onAlphaPickerChange); } - }, [query, reloadItems, setAlphaPicker]); + }, [onAlphaPickerChange]); return (
-
+
); }; diff --git a/src/view/components/Filter.tsx b/src/view/components/Filter.tsx index d0fc0274f9..8e0dfd5548 100644 --- a/src/view/components/Filter.tsx +++ b/src/view/components/Filter.tsx @@ -5,17 +5,18 @@ import { IQuery } from './type'; type FilterProps = { query: IQuery; + getFilterMode: () => string | null; reloadItems: () => void; } -const Filter: FunctionComponent = ({ query, reloadItems }: FilterProps) => { +const Filter: FunctionComponent = ({ query, getFilterMode, reloadItems }: FilterProps) => { const element = useRef(null); const showFilterMenu = useCallback(() => { import('../../components/filterdialog/filterdialog').then(({default: filterDialogFactory}) => { const filterDialog = new filterDialogFactory({ query: query, - mode: 'movies', + mode: getFilterMode(), serverId: window.ApiClient.serverId() }); Events.on(filterDialog, 'filterchange', () => { @@ -24,15 +25,13 @@ const Filter: FunctionComponent = ({ query, reloadItems }: FilterPr }); filterDialog.show(); }); - }, [query, reloadItems]); + }, [getFilterMode, query, reloadItems]); useEffect(() => { const btnFilter = element.current?.querySelector('.btnFilter'); if (btnFilter) { - btnFilter.addEventListener('click', () => { - showFilterMenu(); - }); + btnFilter.addEventListener('click', showFilterMenu); } }, [showFilterMenu]); diff --git a/src/view/components/GenresItemsContainer.tsx b/src/view/components/GenresItemsContainer.tsx index cb003ba053..9abccbee01 100644 --- a/src/view/components/GenresItemsContainer.tsx +++ b/src/view/components/GenresItemsContainer.tsx @@ -126,13 +126,8 @@ const GenresItemsContainer: FunctionComponent = ({ to html += ''; html += '
'; if (enableScrollX()) { - let scrollXClass = 'scrollX hiddenScrollX'; - - if (layoutManager.tv) { - scrollXClass += 'smoothScrollX padded-top-focusscale padded-bottom-focusscale'; - } - - html += '
'; + html += '
'; + html += '
'; } else { html += '
'; } diff --git a/src/view/components/ItemsContainer.tsx b/src/view/components/ItemsContainer.tsx index 8cc2992cca..6a6b24d034 100644 --- a/src/view/components/ItemsContainer.tsx +++ b/src/view/components/ItemsContainer.tsx @@ -12,11 +12,12 @@ import { IQuery } from './type'; type ItemsContainerProps = { getCurrentViewStyle: () => string; query: IQuery; + getContext: () => string | null; items?: BaseItemDto[] | null; noItemsMessage?: string; } -const ItemsContainer: FunctionComponent = ({ getCurrentViewStyle, query, items = [], noItemsMessage }: ItemsContainerProps) => { +const ItemsContainer: FunctionComponent = ({ getCurrentViewStyle, query, getContext, items = [], noItemsMessage }: ItemsContainerProps) => { const element = useRef(null); const viewStyle = getCurrentViewStyle(); @@ -28,7 +29,7 @@ const ItemsContainer: FunctionComponent = ({ getCurrentView items: items, shape: 'backdrop', preferThumb: true, - context: 'movies', + context: getContext(), lazy: true, overlayPlayButton: true, showTitle: true, @@ -40,7 +41,7 @@ const ItemsContainer: FunctionComponent = ({ getCurrentView items: items, shape: 'backdrop', preferThumb: true, - context: 'movies', + context: getContext(), lazy: true, cardLayout: true, showTitle: true, @@ -52,20 +53,20 @@ const ItemsContainer: FunctionComponent = ({ getCurrentView items: items, shape: 'banner', preferBanner: true, - context: 'movies', + context: getContext(), lazy: true }); } else if (viewStyle == 'List') { html = listview.getListViewHtml({ items: items, - context: 'movies', + context: getContext(), sortBy: query.SortBy }); } else if (viewStyle == 'PosterCard') { html = cardBuilder.getCardsHtml(items, { items: items, shape: 'portrait', - context: 'movies', + context: getContext(), showTitle: true, showYear: true, centerText: true, @@ -76,7 +77,7 @@ const ItemsContainer: FunctionComponent = ({ getCurrentView html = cardBuilder.getCardsHtml(items, { items: items, shape: 'portrait', - context: 'movies', + context: getContext(), overlayPlayButton: true, showTitle: true, showYear: true, @@ -96,14 +97,14 @@ const ItemsContainer: FunctionComponent = ({ getCurrentView const itemsContainer = element.current?.querySelector('.itemsContainer') as HTMLDivElement; itemsContainer.innerHTML = html; imageLoader.lazyChildren(itemsContainer); - }, [query.SortBy, items, noItemsMessage, viewStyle]); + }, [query.SortBy, items, noItemsMessage, viewStyle, getContext]); const cssClass = viewStyle == 'List' ? 'vertical-list' : 'vertical-wrap'; return (
); diff --git a/src/view/components/NewCollection.tsx b/src/view/components/NewCollection.tsx index 1dbd625e64..d773295714 100644 --- a/src/view/components/NewCollection.tsx +++ b/src/view/components/NewCollection.tsx @@ -1,26 +1,28 @@ -import React, { FunctionComponent, useEffect, useRef } from 'react'; +import React, { FunctionComponent, useCallback, useEffect, useRef } from 'react'; import IconButtonElement from '../../elements/IconButtonElement'; const NewCollection: FunctionComponent = () => { const element = useRef(null); - useEffect(() => { - const btnNewCollection = element.current?.querySelector('.btnNewCollection') as HTMLButtonElement; - if (btnNewCollection) { - btnNewCollection.addEventListener('click', () => { - import('../../components/collectionEditor/collectionEditor').then(({default: CollectionEditor}) => { - const serverId = window.ApiClient.serverId(); - const collectionEditor = new CollectionEditor(); - collectionEditor.show({ - items: [], - serverId: serverId - }); - }); + const showCollectionEditor = useCallback(() => { + import('../../components/collectionEditor/collectionEditor').then(({default: CollectionEditor}) => { + const serverId = window.ApiClient.serverId(); + const collectionEditor = new CollectionEditor(); + collectionEditor.show({ + items: [], + serverId: serverId }); - } + }); }, []); + useEffect(() => { + const btnNewCollection = element.current?.querySelector('.btnNewCollection'); + if (btnNewCollection) { + btnNewCollection.addEventListener('click', showCollectionEditor); + } + }, [showCollectionEditor]); + return (
= ({ query, itemsResult = {}, reloadItems }: PaginationProps) => { + const startIndex = query.StartIndex; + const limit = query.Limit; + const totalRecordCount = itemsResult.TotalRecordCount || 0; + const recordsEnd = Math.min(startIndex + limit, totalRecordCount); + const showControls = limit < totalRecordCount; const element = useRef(null); + + const onNextPageClick = useCallback(() => { + if (query.Limit > 0) { + query.StartIndex += query.Limit; + } + reloadItems(); + }, [query, reloadItems]); + + const onPreviousPageClick = useCallback(() => { + if (query.Limit > 0) { + query.StartIndex = Math.max(0, query.StartIndex - query.Limit); + } + reloadItems(); + }, [query, reloadItems]); + useEffect(() => { - function onNextPageClick() { - if (userSettings.libraryPageSize(undefined) > 0) { - query.StartIndex += query.Limit; - } - reloadItems(); - } - - function onPreviousPageClick() { - if (userSettings.libraryPageSize(undefined) > 0) { - query.StartIndex = Math.max(0, query.StartIndex - query.Limit); - } - reloadItems(); - } - const pagingHtml = libraryBrowser.getQueryPagingHtml({ - startIndex: query.StartIndex, - limit: query.Limit, - totalRecordCount: itemsResult.TotalRecordCount, - showLimit: false, - updatePageSizeSetting: false, - addLayoutButton: false, - sortButton: false, - filterButton: false - }); - - const paging = element.current?.querySelector('.paging') as HTMLDivElement; - paging.innerHTML = pagingHtml; - const btnNextPage = element.current?.querySelector('.btnNextPage') as HTMLButtonElement; if (btnNextPage) { + if (startIndex + limit >= totalRecordCount) { + btnNextPage.disabled = true; + } else { + btnNextPage.disabled = false; + } btnNextPage.addEventListener('click', onNextPageClick); } const btnPreviousPage = element.current?.querySelector('.btnPreviousPage') as HTMLButtonElement; if (btnPreviousPage) { + if (startIndex) { + btnPreviousPage.disabled = false; + } else { + btnPreviousPage.disabled = true; + } btnPreviousPage.addEventListener('click', onPreviousPageClick); } - }, [itemsResult, query, reloadItems]); + }, [totalRecordCount, onNextPageClick, onPreviousPageClick, limit, startIndex]); return (
-
+
+ {showControls && ( +
+ + + {globalize.translate('ListPaging', (totalRecordCount ? startIndex + 1 : 0), recordsEnd, totalRecordCount)} + + + + +
+ )} +
); }; diff --git a/src/view/components/SelectView.tsx b/src/view/components/SelectView.tsx index eb857ca120..c1f2633c54 100644 --- a/src/view/components/SelectView.tsx +++ b/src/view/components/SelectView.tsx @@ -8,11 +8,11 @@ import { IQuery } from './type'; type SelectViewProps = { getCurrentViewStyle: () => string; query: IQuery; - savedViewKey: string; + getViewSettings: () => string; reloadItems: () => void; } -const SelectView: FunctionComponent = ({ getCurrentViewStyle, savedViewKey, query, reloadItems }: SelectViewProps) => { +const SelectView: FunctionComponent = ({ getCurrentViewStyle, getViewSettings, query, reloadItems }: SelectViewProps) => { const element = useRef(null); useEffect(() => { @@ -22,11 +22,11 @@ const SelectView: FunctionComponent = ({ getCurrentViewStyle, s }); btnSelectView.addEventListener('layoutchange', (e) => { const viewStyle = (e as CustomEvent).detail.viewStyle; - userSettings.set(savedViewKey, viewStyle, false); + userSettings.set(getViewSettings(), viewStyle, false); query.StartIndex = 0; reloadItems(); }); - }, [getCurrentViewStyle, query, reloadItems, savedViewKey]); + }, [getCurrentViewStyle, query, reloadItems, getViewSettings]); return (
diff --git a/src/view/components/Shuffle.tsx b/src/view/components/Shuffle.tsx index fe8d92023f..3ff64253f2 100644 --- a/src/view/components/Shuffle.tsx +++ b/src/view/components/Shuffle.tsx @@ -22,8 +22,7 @@ const Shuffle: FunctionComponent = ({ itemsResult = {}, topParentI }, [topParentId]); useEffect(() => { - const btnShuffle = element.current?.querySelector('.btnShuffle') as HTMLButtonElement; - btnShuffle.classList.toggle('hide', typeof itemsResult.TotalRecordCount === 'number' && itemsResult.TotalRecordCount < 1); + const btnShuffle = element.current?.querySelector('.btnShuffle'); if (btnShuffle) { btnShuffle.addEventListener('click', shuffle); } @@ -33,7 +32,7 @@ const Shuffle: FunctionComponent = ({ itemsResult = {}, topParentI
diff --git a/src/view/components/Sort.tsx b/src/view/components/Sort.tsx index 9cdb127777..47f1553079 100644 --- a/src/view/components/Sort.tsx +++ b/src/view/components/Sort.tsx @@ -5,13 +5,16 @@ import * as userSettings from '../../scripts/settings/userSettings'; import { IQuery } from './type'; type SortProps = { - sortMenuOptions: () => { name: string; id: string}[]; + getSortMenuOptions: () => { + name: string; + id: string; + }[]; query: IQuery; - savedQueryKey: string; + getSettingsKey: () => string; reloadItems: () => void; } -const Sort: FunctionComponent = ({ sortMenuOptions, query, savedQueryKey, reloadItems }: SortProps) => { +const Sort: FunctionComponent = ({ getSortMenuOptions, query, getSettingsKey, reloadItems }: SortProps) => { const element = useRef(null); useEffect(() => { @@ -20,10 +23,10 @@ const Sort: FunctionComponent = ({ sortMenuOptions, query, savedQuery if (btnSort) { btnSort.addEventListener('click', (e) => { libraryBrowser.showSortMenu({ - items: sortMenuOptions(), + items: getSortMenuOptions(), callback: () => { query.StartIndex = 0; - userSettings.saveQuerySettings(savedQueryKey, query); + userSettings.saveQuerySettings(getSettingsKey(), query); reloadItems(); }, query: query, @@ -31,7 +34,7 @@ const Sort: FunctionComponent = ({ sortMenuOptions, query, savedQuery }); }); } - }, [sortMenuOptions, query, reloadItems, savedQueryKey]); + }, [getSortMenuOptions, query, reloadItems, getSettingsKey]); return (
diff --git a/src/view/components/ViewItemsContainer.tsx b/src/view/components/ViewItemsContainer.tsx new file mode 100644 index 0000000000..974f2c4fc8 --- /dev/null +++ b/src/view/components/ViewItemsContainer.tsx @@ -0,0 +1,144 @@ +import { BaseItemDtoQueryResult } from '@thornbill/jellyfin-sdk/dist/generated-client'; +import React, { FunctionComponent, useCallback, useEffect, useMemo, useRef, useState } from 'react'; + +import loading from '../../components/loading/loading'; +import * as userSettings from '../../scripts/settings/userSettings'; +import AlphaPickerContainer from './AlphaPickerContainer'; +import Filter from './Filter'; +import ItemsContainer from './ItemsContainer'; +import Pagination from './Pagination'; +import SelectView from './SelectView'; +import Shuffle from './Shuffle'; +import Sort from './Sort'; +import { IQuery } from './type'; +import NewCollection from './NewCollection'; + +type IProps = { + topParentId: string | null; + isBtnShuffleEnabled?: boolean; + isBtnFilterEnabled?: boolean; + isBtnNewCollectionEnabled?: boolean; + isAlphaPickerEnabled?: boolean; + getBasekey: () => string; + getFilterMode: () => string; + getItemTypes: () => string; + getSortMenuOptions: () => { + name: string; + id: string; + }[]; + getNoItemsMessage: () => string; +} + +const ViewItemsContainer: FunctionComponent = ({ + topParentId, + isBtnShuffleEnabled = false, + isBtnFilterEnabled = true, + isBtnNewCollectionEnabled = false, + isAlphaPickerEnabled = true, + getBasekey, + getFilterMode, + getItemTypes, + getSortMenuOptions, + getNoItemsMessage +}: IProps) => { + const [ itemsResult, setItemsResult ] = useState({}); + + const element = useRef(null); + + const getSettingsKey = useCallback(() => { + return `${topParentId} - ${getBasekey()}`; + }, [getBasekey, topParentId]); + + const getViewSettings = useCallback(() => { + return `${getSettingsKey()} -view`; + }, [getSettingsKey]); + + let query = useMemo(() => ({ + SortBy: 'SortName,ProductionYear', + SortOrder: 'Ascending', + IncludeItemTypes: getItemTypes(), + Recursive: true, + Fields: 'PrimaryImageAspectRatio,MediaSourceCount,BasicSyncInfo', + ImageTypeLimit: 1, + EnableImageTypes: 'Primary,Backdrop,Banner,Thumb', + Limit: userSettings.libraryPageSize(undefined), + StartIndex: 0, + ParentId: topParentId + }), [getItemTypes, topParentId]); + + if (getBasekey() === 'favorites') { + query.IsFavorite = true; + } + + query = userSettings.loadQuerySettings(getSettingsKey(), query); + + const getCurrentViewStyle = useCallback(() => { + return userSettings.get(getViewSettings(), false) || 'Poster'; + }, [getViewSettings]); + + const getContext = useCallback(() => { + const itemType = getItemTypes(); + if (itemType === 'Movie' || itemType === 'BoxSet') { + return 'movies'; + } + + return null; + }, [getItemTypes]); + + const reloadItems = useCallback(() => { + const page = element.current; + + if (!page) { + console.error('Unexpected null reference'); + return; + } + loading.show(); + window.ApiClient.getItems(window.ApiClient.getCurrentUserId(), query).then((result) => { + setItemsResult(result); + window.scrollTo(0, 0); + + loading.hide(); + + import('../../components/autoFocuser').then(({ default: autoFocuser }) => { + autoFocuser.autoFocus(page); + }); + }); + }, [query]); + + useEffect(() => { + reloadItems(); + }, [reloadItems]); + + return ( +
+
+ + + {isBtnShuffleEnabled && } + + + + {isBtnFilterEnabled && } + + {isBtnNewCollectionEnabled && } + +
+ + {isAlphaPickerEnabled && } + + + +
+ +
+
+ ); +}; + +export default ViewItemsContainer; diff --git a/src/view/components/type.ts b/src/view/components/type.ts index 2dde6bb24f..b54ad5334f 100644 --- a/src/view/components/type.ts +++ b/src/view/components/type.ts @@ -5,6 +5,7 @@ export type IQuery = { Recursive?: boolean; Fields?: string; ImageTypeLimit?: number; + EnableTotalRecordCount?: boolean; EnableImageTypes?: string; StartIndex: number; ParentId?: string | null; diff --git a/src/view/movies/CollectionsView.tsx b/src/view/movies/CollectionsView.tsx index 612df7deef..17bd14cf52 100644 --- a/src/view/movies/CollectionsView.tsx +++ b/src/view/movies/CollectionsView.tsx @@ -1,99 +1,51 @@ -import '../../elements/emby-itemscontainer/emby-itemscontainer'; +import React, { FunctionComponent, useCallback } from 'react'; -import { BaseItemDtoQueryResult } from '@thornbill/jellyfin-sdk/dist/generated-client'; -import React, { FunctionComponent, useCallback, useEffect, useMemo, useRef, useState } from 'react'; - -import loading from '../../components/loading/loading'; import globalize from '../../scripts/globalize'; -import * as userSettings from '../../scripts/settings/userSettings'; -import ItemsContainer from '../components/ItemsContainer'; -import NewCollection from '../components/NewCollection'; -import Pagination from '../components/Pagination'; -import SelectView from '../components/SelectView'; -import Sort from '../components/Sort'; -import { IQuery } from '../components/type'; - -const SortMenuOptions = () => { - return [{ - name: globalize.translate('Name'), - id: 'SortName' - }, { - name: globalize.translate('OptionDateAdded'), - id: 'DateCreated,SortName' - }]; -}; +import ViewItemsContainer from '../components/ViewItemsContainer'; type IProps = { topParentId: string | null; } const CollectionsView: FunctionComponent = ({ topParentId }: IProps) => { - const savedQueryKey = topParentId + '-moviecollections'; - const savedViewKey = savedQueryKey + '-view'; + const getBasekey = useCallback(() => { + return 'collections'; + }, []); - const [ itemsResult, setItemsResult ] = useState({}); - const element = useRef(null); + const getFilterMode = useCallback(() => { + return 'movies'; + }, []); - const query = useMemo(() => ({ - SortBy: 'SortName', - SortOrder: 'Ascending', - IncludeItemTypes: 'BoxSet', - Recursive: true, - Fields: 'PrimaryImageAspectRatio,SortName', - ImageTypeLimit: 1, - EnableImageTypes: 'Primary,Backdrop,Banner,Thumb', - Limit: userSettings.libraryPageSize(undefined), - StartIndex: 0, - ParentId: topParentId }), [topParentId]); + const getItemTypes = useCallback(() => { + return 'BoxSet'; + }, []); - userSettings.loadQuerySettings(savedQueryKey, query); + const getNoItemsMessage = useCallback(() => { + return 'MessageNoCollectionsAvailable'; + }, []); - const getCurrentViewStyle = useCallback(() => { - return userSettings.get(savedViewKey, false) || 'Poster'; - }, [savedViewKey]); - - const reloadItems = useCallback(() => { - const page = element.current; - - if (!page) { - console.error('Unexpected null reference'); - return; - } - loading.show(); - window.ApiClient.getItems(window.ApiClient.getCurrentUserId(), query).then((result) => { - setItemsResult(result); - - window.scrollTo(0, 0); - - loading.hide(); - - import('../../components/autoFocuser').then(({ default: autoFocuser }) => { - autoFocuser.autoFocus(page); - }); - }); - }, [query]); - - useEffect(() => { - reloadItems(); - }, [reloadItems]); + const getSortMenuOptions = useCallback(() => { + return [{ + name: globalize.translate('Name'), + id: 'SortName' + }, { + name: globalize.translate('OptionDateAdded'), + id: 'DateCreated,SortName' + }]; + }, []); return ( -
-
- - - - - - -
- - - -
- -
-
+ ); }; diff --git a/src/view/movies/FavoritesView.tsx b/src/view/movies/FavoritesView.tsx index 713b02376e..8ec222af75 100644 --- a/src/view/movies/FavoritesView.tsx +++ b/src/view/movies/FavoritesView.tsx @@ -1,124 +1,72 @@ -import { BaseItemDtoQueryResult } from '@thornbill/jellyfin-sdk/dist/generated-client'; -import React, { FunctionComponent, useCallback, useEffect, useMemo, useRef, useState } from 'react'; +import React, { FunctionComponent, useCallback } from 'react'; -import loading from '../../components/loading/loading'; import globalize from '../../scripts/globalize'; -import * as userSettings from '../../scripts/settings/userSettings'; -import AlphaPickerContainer from '../components/AlphaPickerContainer'; -import Filter from '../components/Filter'; -import ItemsContainer from '../components/ItemsContainer'; -import Pagination from '../components/Pagination'; -import SelectView from '../components/SelectView'; -import Sort from '../components/Sort'; -import { IQuery } from '../components/type'; +import ViewItemsContainer from '../components/ViewItemsContainer'; type IProps = { topParentId: string | null; } -const SortMenuOptions = () => { - return [{ - name: globalize.translate('Name'), - id: 'SortName,ProductionYear' - }, { - name: globalize.translate('OptionRandom'), - id: 'Random' - }, { - name: globalize.translate('OptionImdbRating'), - id: 'CommunityRating,SortName,ProductionYear' - }, { - name: globalize.translate('OptionCriticRating'), - id: 'CriticRating,SortName,ProductionYear' - }, { - name: globalize.translate('OptionDateAdded'), - id: 'DateCreated,SortName,ProductionYear' - }, { - name: globalize.translate('OptionDatePlayed'), - id: 'DatePlayed,SortName,ProductionYear' - }, { - name: globalize.translate('OptionParentalRating'), - id: 'OfficialRating,SortName,ProductionYear' - }, { - name: globalize.translate('OptionPlayCount'), - id: 'PlayCount,SortName,ProductionYear' - }, { - name: globalize.translate('OptionReleaseDate'), - id: 'PremiereDate,SortName,ProductionYear' - }, { - name: globalize.translate('Runtime'), - id: 'Runtime,SortName,ProductionYear' - }]; -}; - const FavoritesView: FunctionComponent = ({ topParentId }: IProps) => { - const savedQueryKey = topParentId + '-favorites'; - const savedViewKey = savedQueryKey + '-view'; + const getBasekey = useCallback(() => { + return 'favorites'; + }, []); - const [ itemsResult, setItemsResult ] = useState({}); - const element = useRef(null); + const getFilterMode = useCallback(() => { + return 'movies'; + }, []); - const query = useMemo(() => ({ - SortBy: 'SortName,ProductionYear', - SortOrder: 'Ascending', - IncludeItemTypes: 'Movie', - Recursive: true, - Fields: 'PrimaryImageAspectRatio,MediaSourceCount,BasicSyncInfo', - ImageTypeLimit: 1, - EnableImageTypes: 'Primary,Backdrop,Banner,Thumb', - Limit: userSettings.libraryPageSize(undefined), - IsFavorite: true, - StartIndex: 0, - ParentId: topParentId }), [topParentId]); + const getItemTypes = useCallback(() => { + return 'Movie'; + }, []); - userSettings.loadQuerySettings(savedQueryKey, query); + const getNoItemsMessage = useCallback(() => { + return 'MessageNoFavoritesAvailable'; + }, []); - const getCurrentViewStyle = useCallback(() => { - return userSettings.get(savedViewKey, false) || 'Poster'; - }, [savedViewKey]); - - const reloadItems = useCallback(() => { - const page = element.current; - - if (!page) { - console.error('Unexpected null reference'); - return; - } - - loading.show(); - window.ApiClient.getItems(window.ApiClient.getCurrentUserId(), query).then((result) => { - setItemsResult(result); - window.scrollTo(0, 0); - loading.hide(); - - import('../../components/autoFocuser').then(({ default: autoFocuser }) => { - autoFocuser.autoFocus(page); - }); - }); - }, [query]); - - useEffect(() => { - reloadItems(); - }, [query, reloadItems]); + const getSortMenuOptions = useCallback(() => { + return [{ + name: globalize.translate('Name'), + id: 'SortName,ProductionYear' + }, { + name: globalize.translate('OptionRandom'), + id: 'Random' + }, { + name: globalize.translate('OptionImdbRating'), + id: 'CommunityRating,SortName,ProductionYear' + }, { + name: globalize.translate('OptionCriticRating'), + id: 'CriticRating,SortName,ProductionYear' + }, { + name: globalize.translate('OptionDateAdded'), + id: 'DateCreated,SortName,ProductionYear' + }, { + name: globalize.translate('OptionDatePlayed'), + id: 'DatePlayed,SortName,ProductionYear' + }, { + name: globalize.translate('OptionParentalRating'), + id: 'OfficialRating,SortName,ProductionYear' + }, { + name: globalize.translate('OptionPlayCount'), + id: 'PlayCount,SortName,ProductionYear' + }, { + name: globalize.translate('OptionReleaseDate'), + id: 'PremiereDate,SortName,ProductionYear' + }, { + name: globalize.translate('Runtime'), + id: 'Runtime,SortName,ProductionYear' + }]; + }, []); return ( -
-
- - - - - - -
- - - - - -
- -
-
+ ); }; diff --git a/src/view/movies/GenresView.tsx b/src/view/movies/GenresView.tsx index d442def733..b75dc9e96a 100644 --- a/src/view/movies/GenresView.tsx +++ b/src/view/movies/GenresView.tsx @@ -11,13 +11,18 @@ type IProps = { } const GenresView: FunctionComponent = ({ topParentId }: IProps) => { - const savedQueryKey = topParentId + '-moviegenres'; - const savedViewKey = savedQueryKey + '-view'; - const [ itemsResult, setItemsResult ] = useState({}); const element = useRef(null); - const query = useMemo(() => ({ + const getSettingsKey = useCallback(() => { + return topParentId + '-genres'; + }, [topParentId]); + + const getViewSettings = useCallback(() => { + return getSettingsKey() + '-view'; + }, [getSettingsKey]); + + let query = useMemo(() => ({ SortBy: 'SortName', SortOrder: 'Ascending', IncludeItemTypes: 'Movie', @@ -27,20 +32,13 @@ const GenresView: FunctionComponent = ({ topParentId }: IProps) => { StartIndex: 0, ParentId: topParentId }), [topParentId]); - userSettings.loadQuerySettings(savedQueryKey, query); + query = userSettings.loadQuerySettings(getSettingsKey(), query); const getCurrentViewStyle = useCallback(() => { - return userSettings.get(savedViewKey, false) || 'Poster'; - }, [savedViewKey]); + return userSettings.get(getViewSettings(), false) || 'Poster'; + }, [getViewSettings]); const reloadItems = useCallback(() => { - const page = element.current; - - if (!page) { - console.error('Unexpected null reference'); - return; - } - loading.show(); window.ApiClient.getGenres(window.ApiClient.getCurrentUserId(), query).then((result) => { setItemsResult(result); diff --git a/src/view/movies/MoviesView.tsx b/src/view/movies/MoviesView.tsx index e0fee74a5a..3dd2b3caff 100644 --- a/src/view/movies/MoviesView.tsx +++ b/src/view/movies/MoviesView.tsx @@ -1,127 +1,73 @@ -import { BaseItemDtoQueryResult } from '@thornbill/jellyfin-sdk/dist/generated-client'; -import React, { FunctionComponent, useCallback, useEffect, useMemo, useRef, useState } from 'react'; - -import loading from '../../components/loading/loading'; +import React, { FunctionComponent, useCallback } from 'react'; import globalize from '../../scripts/globalize'; -import * as userSettings from '../../scripts/settings/userSettings'; -import AlphaPickerContainer from '../components/AlphaPickerContainer'; -import Filter from '../components/Filter'; -import ItemsContainer from '../components/ItemsContainer'; -import Pagination from '../components/Pagination'; -import SelectView from '../components/SelectView'; -import Shuffle from '../components/Shuffle'; -import Sort from '../components/Sort'; -import { IQuery } from '../components/type'; + +import ViewItemsContainer from '../components/ViewItemsContainer'; type IProps = { topParentId: string | null; } -const SortMenuOptions = () => { - return [{ - name: globalize.translate('Name'), - id: 'SortName,ProductionYear' - }, { - name: globalize.translate('OptionRandom'), - id: 'Random' - }, { - name: globalize.translate('OptionImdbRating'), - id: 'CommunityRating,SortName,ProductionYear' - }, { - name: globalize.translate('OptionCriticRating'), - id: 'CriticRating,SortName,ProductionYear' - }, { - name: globalize.translate('OptionDateAdded'), - id: 'DateCreated,SortName,ProductionYear' - }, { - name: globalize.translate('OptionDatePlayed'), - id: 'DatePlayed,SortName,ProductionYear' - }, { - name: globalize.translate('OptionParentalRating'), - id: 'OfficialRating,SortName,ProductionYear' - }, { - name: globalize.translate('OptionPlayCount'), - id: 'PlayCount,SortName,ProductionYear' - }, { - name: globalize.translate('OptionReleaseDate'), - id: 'PremiereDate,SortName,ProductionYear' - }, { - name: globalize.translate('Runtime'), - id: 'Runtime,SortName,ProductionYear' - }]; -}; - const MoviesView: FunctionComponent = ({ topParentId }: IProps) => { - const savedQueryKey = topParentId + '-movies'; - const savedViewKey = savedQueryKey + '-view'; + const getBasekey = useCallback(() => { + return 'movies'; + }, []); - const [ itemsResult, setItemsResult ] = useState(); + const getFilterMode = useCallback(() => { + return 'movies'; + }, []); - const element = useRef(null); + const getItemTypes = useCallback(() => { + return 'Movie'; + }, []); - const query = useMemo(() => ({ - SortBy: 'SortName,ProductionYear', - SortOrder: 'Ascending', - IncludeItemTypes: 'Movie', - Recursive: true, - Fields: 'PrimaryImageAspectRatio,MediaSourceCount,BasicSyncInfo', - ImageTypeLimit: 1, - EnableImageTypes: 'Primary,Backdrop,Banner,Thumb', - Limit: userSettings.libraryPageSize(undefined), - StartIndex: 0, - ParentId: topParentId }), [topParentId]); + const getNoItemsMessage = useCallback(() => { + return 'MessageNoItemsAvailable'; + }, []); - userSettings.loadQuerySettings(savedQueryKey, query); - - const getCurrentViewStyle = useCallback(() => { - return userSettings.get(savedViewKey, false) || 'Poster'; - }, [savedViewKey]); - - const reloadItems = useCallback(() => { - const page = element.current; - - if (!page) { - console.error('Unexpected null reference'); - return; - } - loading.show(); - window.ApiClient.getItems(window.ApiClient.getCurrentUserId(), query).then((result) => { - setItemsResult(result); - window.scrollTo(0, 0); - - loading.hide(); - - import('../../components/autoFocuser').then(({ default: autoFocuser }) => { - autoFocuser.autoFocus(page); - }); - }); - }, [query]); - - useEffect(() => { - reloadItems(); - }, [query, reloadItems]); + const getSortMenuOptions = useCallback(() => { + return [{ + name: globalize.translate('Name'), + id: 'SortName,ProductionYear' + }, { + name: globalize.translate('OptionRandom'), + id: 'Random' + }, { + name: globalize.translate('OptionImdbRating'), + id: 'CommunityRating,SortName,ProductionYear' + }, { + name: globalize.translate('OptionCriticRating'), + id: 'CriticRating,SortName,ProductionYear' + }, { + name: globalize.translate('OptionDateAdded'), + id: 'DateCreated,SortName,ProductionYear' + }, { + name: globalize.translate('OptionDatePlayed'), + id: 'DatePlayed,SortName,ProductionYear' + }, { + name: globalize.translate('OptionParentalRating'), + id: 'OfficialRating,SortName,ProductionYear' + }, { + name: globalize.translate('OptionPlayCount'), + id: 'PlayCount,SortName,ProductionYear' + }, { + name: globalize.translate('OptionReleaseDate'), + id: 'PremiereDate,SortName,ProductionYear' + }, { + name: globalize.translate('Runtime'), + id: 'Runtime,SortName,ProductionYear' + }]; + }, []); return ( -
-
- - - - - - - - -
- - - - - -
- -
-
+ ); }; diff --git a/src/view/movies/SuggestionsView.tsx b/src/view/movies/SuggestionsView.tsx index 34a049783f..9ff96a50f5 100644 --- a/src/view/movies/SuggestionsView.tsx +++ b/src/view/movies/SuggestionsView.tsx @@ -57,7 +57,7 @@ const SuggestionsView: FunctionComponent = (props: IProps) => { const loadResume = useCallback((page, userId, parentId) => { loading.show(); - const screenWidth: any = dom.getWindowSize(); + const screenWidth = dom.getWindowSize(); const options = { SortBy: 'DatePlayed', SortOrder: 'Descending', @@ -82,7 +82,7 @@ const SuggestionsView: FunctionComponent = (props: IProps) => { }, [autoFocus]); const loadSuggestions = useCallback((page, userId) => { - const screenWidth: any = dom.getWindowSize(); + const screenWidth = dom.getWindowSize(); let itemLimit = 5; if (screenWidth.innerWidth >= 1600) { itemLimit = 8; diff --git a/src/view/movies/TrailersView.tsx b/src/view/movies/TrailersView.tsx index e776c01174..9cce2ebe41 100644 --- a/src/view/movies/TrailersView.tsx +++ b/src/view/movies/TrailersView.tsx @@ -1,110 +1,64 @@ -import '../../elements/emby-itemscontainer/emby-itemscontainer'; -import { BaseItemDtoQueryResult } from '@thornbill/jellyfin-sdk/dist/generated-client'; -import React, { FunctionComponent, useCallback, useEffect, useMemo, useRef, useState } from 'react'; +import React, { FunctionComponent, useCallback } from 'react'; -import loading from '../../components/loading/loading'; import globalize from '../../scripts/globalize'; -import * as userSettings from '../../scripts/settings/userSettings'; -import AlphaPickerContainer from '../components/AlphaPickerContainer'; -import Filter from '../components/Filter'; -import ItemsContainer from '../components/ItemsContainer'; -import Pagination from '../components/Pagination'; -import Sort from '../components/Sort'; -import { IQuery } from '../components/type'; - -const SortMenuOptions = () => { - return [{ - name: globalize.translate('Name'), - id: 'SortName' - }, { - name: globalize.translate('OptionImdbRating'), - id: 'CommunityRating,SortName' - }, { - name: globalize.translate('OptionDateAdded'), - id: 'DateCreated,SortName' - }, { - name: globalize.translate('OptionDatePlayed'), - id: 'DatePlayed,SortName' - }, { - name: globalize.translate('OptionParentalRating'), - id: 'OfficialRating,SortName' - }, { - name: globalize.translate('OptionPlayCount'), - id: 'PlayCount,SortName' - }, { - name: globalize.translate('OptionReleaseDate'), - id: 'PremiereDate,SortName' - }]; -}; +import ViewItemsContainer from '../components/ViewItemsContainer'; type IProps = { topParentId: string | null; } const TrailersView: FunctionComponent = ({ topParentId }: IProps) => { - const savedQueryKey = topParentId + '-trailers'; - const savedViewKey = savedQueryKey + '-view'; + const getBasekey = useCallback(() => { + return 'trailers'; + }, []); - const [ itemsResult, setItemsResult ] = useState(); - const element = useRef(null); + const getFilterMode = useCallback(() => { + return 'movies'; + }, []); - const query = useMemo(() => ({ - SortBy: 'SortName', - SortOrder: 'Ascending', - IncludeItemTypes: 'Trailer', - Recursive: true, - Fields: 'PrimaryImageAspectRatio,SortName,BasicSyncInfo', - ImageTypeLimit: 1, - EnableImageTypes: 'Primary,Backdrop,Banner,Thumb', - Limit: userSettings.libraryPageSize(undefined), - StartIndex: 0, - ParentId: topParentId }), [topParentId]); + const getItemTypes = useCallback(() => { + return 'Trailer'; + }, []); - userSettings.loadQuerySettings(savedQueryKey, query); + const getNoItemsMessage = useCallback(() => { + return 'MessageNoTrailersFound'; + }, []); - const getCurrentViewStyle = useCallback(() => { - return userSettings.get(savedViewKey, false) || 'Poster'; - }, [savedViewKey]); - - const reloadItems = useCallback(() => { - const page = element.current; - - if (!page) { - console.error('Unexpected null reference'); - return; - } - loading.show(); - window.ApiClient.getItems(window.ApiClient.getCurrentUserId(), query).then((result) => { - setItemsResult(result); - window.scrollTo(0, 0); - - loading.hide(); - }); - }, [query]); - - useEffect(() => { - reloadItems(); - }, [query, reloadItems]); + const getSortMenuOptions = useCallback(() => { + return [{ + name: globalize.translate('Name'), + id: 'SortName' + }, { + name: globalize.translate('OptionImdbRating'), + id: 'CommunityRating,SortName' + }, { + name: globalize.translate('OptionDateAdded'), + id: 'DateCreated,SortName' + }, { + name: globalize.translate('OptionDatePlayed'), + id: 'DatePlayed,SortName' + }, { + name: globalize.translate('OptionParentalRating'), + id: 'OfficialRating,SortName' + }, { + name: globalize.translate('OptionPlayCount'), + id: 'PlayCount,SortName' + }, { + name: globalize.translate('OptionReleaseDate'), + id: 'PremiereDate,SortName' + }]; + }, []); return ( -
-
- - - - - -
- - - - - -
- -
-
+ ); };