1
0
Fork 0
mirror of https://github.com/jellyfin/jellyfin-web synced 2025-03-30 19:56:21 +00:00

Refactoring duplicates code

This commit is contained in:
grafixeyehero 2022-08-21 03:09:22 +03:00
parent 368a6064c2
commit cf137497a0
20 changed files with 491 additions and 513 deletions

View file

@ -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) {

View file

@ -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);

View file

@ -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.",

View file

@ -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,11 +13,7 @@ const AlphaPickerContainer: FunctionComponent<AlphaPickerProps> = ({ query, relo
alphaPicker?.updateControls(query);
useEffect(() => {
const alphaPickerElement = element.current?.querySelector('.alphaPicker');
if (alphaPickerElement) {
alphaPickerElement.addEventListener('alphavaluechanged', (e) => {
const onAlphaPickerChange = useCallback((e) => {
const newValue = (e as CustomEvent).detail.value;
if (newValue === '#') {
query.NameLessThan = 'A';
@ -28,19 +24,24 @@ const AlphaPickerContainer: FunctionComponent<AlphaPickerProps> = ({ query, relo
}
query.StartIndex = 0;
reloadItems();
});
}, [query, reloadItems]);
useEffect(() => {
const alphaPickerElement = element.current?.querySelector('.alphaPicker');
setAlphaPicker(new AlphaPicker({
element: alphaPickerElement,
valueChangeEvent: 'click'
}));
alphaPickerElement.classList.add('alphaPicker-fixed-right');
if (alphaPickerElement) {
alphaPickerElement.addEventListener('alphavaluechanged', onAlphaPickerChange);
}
}, [query, reloadItems, setAlphaPicker]);
}, [onAlphaPickerChange]);
return (
<div ref={element}>
<div className='alphaPicker alphaPicker-fixed alphaPicker-vertical alphabetPicker-right' />
<div className='alphaPicker alphaPicker-fixed alphaPicker-fixed-right alphaPicker-vertical alphabetPicker-right' />
</div>
);
};

View file

@ -5,17 +5,18 @@ import { IQuery } from './type';
type FilterProps = {
query: IQuery;
getFilterMode: () => string | null;
reloadItems: () => void;
}
const Filter: FunctionComponent<FilterProps> = ({ query, reloadItems }: FilterProps) => {
const Filter: FunctionComponent<FilterProps> = ({ query, getFilterMode, reloadItems }: FilterProps) => {
const element = useRef<HTMLDivElement>(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<FilterProps> = ({ 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]);

View file

@ -126,13 +126,8 @@ const GenresItemsContainer: FunctionComponent<GenresItemsContainerProps> = ({ to
html += '</a>';
html += '</div>';
if (enableScrollX()) {
let scrollXClass = 'scrollX hiddenScrollX';
if (layoutManager.tv) {
scrollXClass += 'smoothScrollX padded-top-focusscale padded-bottom-focusscale';
}
html += '<div is="emby-itemscontainer" class="itemsContainer ' + scrollXClass + ' lazy padded-left padded-right" data-id="' + item.Id + '">';
html += '<div is="emby-scroller" class="padded-top-focusscale padded-bottom-focusscale" data-centerfocus="true">';
html += '<div is="emby-itemscontainer" class="itemsContainer scrollSlider focuscontainer-x lazy" data-id="' + item.Id + '">';
} else {
html += '<div is="emby-itemscontainer" class="itemsContainer vertical-wrap lazy padded-left padded-right" data-id="' + item.Id + '">';
}

View file

@ -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<ItemsContainerProps> = ({ getCurrentViewStyle, query, items = [], noItemsMessage }: ItemsContainerProps) => {
const ItemsContainer: FunctionComponent<ItemsContainerProps> = ({ getCurrentViewStyle, query, getContext, items = [], noItemsMessage }: ItemsContainerProps) => {
const element = useRef<HTMLDivElement>(null);
const viewStyle = getCurrentViewStyle();
@ -28,7 +29,7 @@ const ItemsContainer: FunctionComponent<ItemsContainerProps> = ({ getCurrentView
items: items,
shape: 'backdrop',
preferThumb: true,
context: 'movies',
context: getContext(),
lazy: true,
overlayPlayButton: true,
showTitle: true,
@ -40,7 +41,7 @@ const ItemsContainer: FunctionComponent<ItemsContainerProps> = ({ getCurrentView
items: items,
shape: 'backdrop',
preferThumb: true,
context: 'movies',
context: getContext(),
lazy: true,
cardLayout: true,
showTitle: true,
@ -52,20 +53,20 @@ const ItemsContainer: FunctionComponent<ItemsContainerProps> = ({ 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<ItemsContainerProps> = ({ 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<ItemsContainerProps> = ({ 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 (
<div ref={element}>
<ItemsContainerElement
className={`itemsContainer ${cssClass} centered padded-left padded-right`}
className={`itemsContainer ${cssClass} centered padded-left padded-right padded-right-withalphapicker`}
/>
</div>
);

View file

@ -1,14 +1,11 @@
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<HTMLDivElement>(null);
useEffect(() => {
const btnNewCollection = element.current?.querySelector('.btnNewCollection') as HTMLButtonElement;
if (btnNewCollection) {
btnNewCollection.addEventListener('click', () => {
const showCollectionEditor = useCallback(() => {
import('../../components/collectionEditor/collectionEditor').then(({default: CollectionEditor}) => {
const serverId = window.ApiClient.serverId();
const collectionEditor = new CollectionEditor();
@ -17,10 +14,15 @@ const NewCollection: FunctionComponent = () => {
serverId: serverId
});
});
});
}
}, []);
useEffect(() => {
const btnNewCollection = element.current?.querySelector('.btnNewCollection');
if (btnNewCollection) {
btnNewCollection.addEventListener('click', showCollectionEditor);
}
}, [showCollectionEditor]);
return (
<div ref={element}>
<IconButtonElement

View file

@ -1,8 +1,7 @@
import { BaseItemDtoQueryResult } from '@thornbill/jellyfin-sdk/dist/generated-client';
import React, { FunctionComponent, useEffect, useRef } from 'react';
import libraryBrowser from '../../scripts/libraryBrowser';
import * as userSettings from '../../scripts/settings/userSettings';
import React, { FunctionComponent, useCallback, useEffect, useRef } from 'react';
import IconButtonElement from '../../elements/IconButtonElement';
import globalize from '../../scripts/globalize';
import { IQuery } from './type';
type PaginationProps = {
@ -12,49 +11,72 @@ type PaginationProps = {
}
const Pagination: FunctionComponent<PaginationProps> = ({ 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<HTMLDivElement>(null);
useEffect(() => {
function onNextPageClick() {
if (userSettings.libraryPageSize(undefined) > 0) {
const onNextPageClick = useCallback(() => {
if (query.Limit > 0) {
query.StartIndex += query.Limit;
}
reloadItems();
}
}, [query, reloadItems]);
function onPreviousPageClick() {
if (userSettings.libraryPageSize(undefined) > 0) {
const onPreviousPageClick = useCallback(() => {
if (query.Limit > 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;
}, [query, reloadItems]);
useEffect(() => {
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 (
<div ref={element}>
<div className='paging' />
<div className='paging'>
{showControls && (
<div className='listPaging' style={{ display: 'flex', alignItems: 'center' }}>
<span>
{globalize.translate('ListPaging', (totalRecordCount ? startIndex + 1 : 0), recordsEnd, totalRecordCount)}
</span>
<IconButtonElement
is='paper-icon-button-light'
className='btnPreviousPage autoSize'
icon='material-icons arrow_back'
/>
<IconButtonElement
is='paper-icon-button-light'
className='btnNextPage autoSize'
icon='material-icons arrow_forward'
/>
</div>
)}
</div>
</div>
);
};

View file

@ -8,11 +8,11 @@ import { IQuery } from './type';
type SelectViewProps = {
getCurrentViewStyle: () => string;
query: IQuery;
savedViewKey: string;
getViewSettings: () => string;
reloadItems: () => void;
}
const SelectView: FunctionComponent<SelectViewProps> = ({ getCurrentViewStyle, savedViewKey, query, reloadItems }: SelectViewProps) => {
const SelectView: FunctionComponent<SelectViewProps> = ({ getCurrentViewStyle, getViewSettings, query, reloadItems }: SelectViewProps) => {
const element = useRef<HTMLDivElement>(null);
useEffect(() => {
@ -22,11 +22,11 @@ const SelectView: FunctionComponent<SelectViewProps> = ({ 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 (
<div ref={element}>

View file

@ -22,8 +22,7 @@ const Shuffle: FunctionComponent<ShuffleProps> = ({ 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<ShuffleProps> = ({ itemsResult = {}, topParentI
<div ref={element}>
<IconButtonElement
is='paper-icon-button-light'
className='btnShuffle autoSize hide'
className='btnShuffle autoSize'
title='Shuffle'
icon='material-icons shuffle'
/>

View file

@ -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<SortProps> = ({ sortMenuOptions, query, savedQueryKey, reloadItems }: SortProps) => {
const Sort: FunctionComponent<SortProps> = ({ getSortMenuOptions, query, getSettingsKey, reloadItems }: SortProps) => {
const element = useRef<HTMLDivElement>(null);
useEffect(() => {
@ -20,10 +23,10 @@ const Sort: FunctionComponent<SortProps> = ({ 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<SortProps> = ({ sortMenuOptions, query, savedQuery
});
});
}
}, [sortMenuOptions, query, reloadItems, savedQueryKey]);
}, [getSortMenuOptions, query, reloadItems, getSettingsKey]);
return (
<div ref={element}>

View file

@ -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<IProps> = ({
topParentId,
isBtnShuffleEnabled = false,
isBtnFilterEnabled = true,
isBtnNewCollectionEnabled = false,
isAlphaPickerEnabled = true,
getBasekey,
getFilterMode,
getItemTypes,
getSortMenuOptions,
getNoItemsMessage
}: IProps) => {
const [ itemsResult, setItemsResult ] = useState<BaseItemDtoQueryResult>({});
const element = useRef<HTMLDivElement>(null);
const getSettingsKey = useCallback(() => {
return `${topParentId} - ${getBasekey()}`;
}, [getBasekey, topParentId]);
const getViewSettings = useCallback(() => {
return `${getSettingsKey()} -view`;
}, [getSettingsKey]);
let query = useMemo<IQuery>(() => ({
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 (
<div ref={element}>
<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} />
{isBtnShuffleEnabled && <Shuffle itemsResult={itemsResult} topParentId={topParentId} />}
<SelectView getCurrentViewStyle={getCurrentViewStyle} getViewSettings={getViewSettings} query={query} reloadItems={reloadItems} />
<Sort getSortMenuOptions={getSortMenuOptions} query={query} getSettingsKey={getSettingsKey} reloadItems={reloadItems} />
{isBtnFilterEnabled && <Filter query={query} getFilterMode={getFilterMode} reloadItems={reloadItems} />}
{isBtnNewCollectionEnabled && <NewCollection />}
</div>
{isAlphaPickerEnabled && <AlphaPickerContainer query={query} reloadItems={reloadItems} />}
<ItemsContainer
getCurrentViewStyle={getCurrentViewStyle}
query={query}
getContext={getContext}
items={itemsResult?.Items}
noItemsMessage={getNoItemsMessage()}
/>
<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} />
</div>
</div>
);
};
export default ViewItemsContainer;

View file

@ -5,6 +5,7 @@ export type IQuery = {
Recursive?: boolean;
Fields?: string;
ImageTypeLimit?: number;
EnableTotalRecordCount?: boolean;
EnableImageTypes?: string;
StartIndex: number;
ParentId?: string | null;

View file

@ -1,19 +1,30 @@
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';
import ViewItemsContainer from '../components/ViewItemsContainer';
const SortMenuOptions = () => {
type IProps = {
topParentId: string | null;
}
const CollectionsView: FunctionComponent<IProps> = ({ topParentId }: IProps) => {
const getBasekey = useCallback(() => {
return 'collections';
}, []);
const getFilterMode = useCallback(() => {
return 'movies';
}, []);
const getItemTypes = useCallback(() => {
return 'BoxSet';
}, []);
const getNoItemsMessage = useCallback(() => {
return 'MessageNoCollectionsAvailable';
}, []);
const getSortMenuOptions = useCallback(() => {
return [{
name: globalize.translate('Name'),
id: 'SortName'
@ -21,79 +32,20 @@ const SortMenuOptions = () => {
name: globalize.translate('OptionDateAdded'),
id: 'DateCreated,SortName'
}];
};
type IProps = {
topParentId: string | null;
}
const CollectionsView: FunctionComponent<IProps> = ({ topParentId }: IProps) => {
const savedQueryKey = topParentId + '-moviecollections';
const savedViewKey = savedQueryKey + '-view';
const [ itemsResult, setItemsResult ] = useState<BaseItemDtoQueryResult>({});
const element = useRef<HTMLDivElement>(null);
const query = useMemo<IQuery>(() => ({
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]);
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();
}, [reloadItems]);
}, []);
return (
<div ref={element}>
<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} />
<SelectView getCurrentViewStyle={getCurrentViewStyle} savedViewKey={savedViewKey} query={query} reloadItems={reloadItems} />
<Sort sortMenuOptions={SortMenuOptions} query={query} savedQueryKey={savedQueryKey} reloadItems={reloadItems} />
<NewCollection />
</div>
<ItemsContainer getCurrentViewStyle={getCurrentViewStyle} query={query} items={itemsResult?.Items} noItemsMessage= 'MessageNoCollectionsAvailable' />
<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} />
</div>
</div>
<ViewItemsContainer
topParentId={topParentId}
isBtnFilterEnabled={false}
isBtnNewCollectionEnabled={true}
isAlphaPickerEnabled={false}
getBasekey={getBasekey}
getFilterMode={getFilterMode}
getItemTypes={getItemTypes}
getSortMenuOptions={getSortMenuOptions}
getNoItemsMessage={getNoItemsMessage}
/>
);
};

View file

@ -1,22 +1,30 @@
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 = () => {
const FavoritesView: FunctionComponent<IProps> = ({ topParentId }: IProps) => {
const getBasekey = useCallback(() => {
return 'favorites';
}, []);
const getFilterMode = useCallback(() => {
return 'movies';
}, []);
const getItemTypes = useCallback(() => {
return 'Movie';
}, []);
const getNoItemsMessage = useCallback(() => {
return 'MessageNoFavoritesAvailable';
}, []);
const getSortMenuOptions = useCallback(() => {
return [{
name: globalize.translate('Name'),
id: 'SortName,ProductionYear'
@ -48,77 +56,17 @@ const SortMenuOptions = () => {
name: globalize.translate('Runtime'),
id: 'Runtime,SortName,ProductionYear'
}];
};
const FavoritesView: FunctionComponent<IProps> = ({ topParentId }: IProps) => {
const savedQueryKey = topParentId + '-favorites';
const savedViewKey = savedQueryKey + '-view';
const [ itemsResult, setItemsResult ] = useState<BaseItemDtoQueryResult>({});
const element = useRef<HTMLDivElement>(null);
const query = useMemo<IQuery>(() => ({
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]);
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]);
}, []);
return (
<div ref={element}>
<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} />
<SelectView getCurrentViewStyle={getCurrentViewStyle} savedViewKey={savedViewKey} query={query} reloadItems={reloadItems} />
<Sort sortMenuOptions={SortMenuOptions} query={query} savedQueryKey={savedQueryKey} reloadItems={reloadItems} />
<Filter query={query} reloadItems={reloadItems} />
</div>
<AlphaPickerContainer query={query} reloadItems={reloadItems} />
<ItemsContainer getCurrentViewStyle={getCurrentViewStyle} query={query} items={itemsResult?.Items} noItemsMessage= 'MessageNoFavoritesAvailable' />
<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} />
</div>
</div>
<ViewItemsContainer
topParentId={topParentId}
getBasekey={getBasekey}
getFilterMode={getFilterMode}
getItemTypes={getItemTypes}
getSortMenuOptions={getSortMenuOptions}
getNoItemsMessage={getNoItemsMessage}
/>
);
};

View file

@ -11,13 +11,18 @@ type IProps = {
}
const GenresView: FunctionComponent<IProps> = ({ topParentId }: IProps) => {
const savedQueryKey = topParentId + '-moviegenres';
const savedViewKey = savedQueryKey + '-view';
const [ itemsResult, setItemsResult ] = useState<BaseItemDtoQueryResult>({});
const element = useRef<HTMLDivElement>(null);
const query = useMemo<IQuery>(() => ({
const getSettingsKey = useCallback(() => {
return topParentId + '-genres';
}, [topParentId]);
const getViewSettings = useCallback(() => {
return getSettingsKey() + '-view';
}, [getSettingsKey]);
let query = useMemo<IQuery>(() => ({
SortBy: 'SortName',
SortOrder: 'Ascending',
IncludeItemTypes: 'Movie',
@ -27,20 +32,13 @@ const GenresView: FunctionComponent<IProps> = ({ 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);

View file

@ -1,23 +1,30 @@
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 = () => {
const MoviesView: FunctionComponent<IProps> = ({ topParentId }: IProps) => {
const getBasekey = useCallback(() => {
return 'movies';
}, []);
const getFilterMode = useCallback(() => {
return 'movies';
}, []);
const getItemTypes = useCallback(() => {
return 'Movie';
}, []);
const getNoItemsMessage = useCallback(() => {
return 'MessageNoItemsAvailable';
}, []);
const getSortMenuOptions = useCallback(() => {
return [{
name: globalize.translate('Name'),
id: 'SortName,ProductionYear'
@ -49,79 +56,18 @@ const SortMenuOptions = () => {
name: globalize.translate('Runtime'),
id: 'Runtime,SortName,ProductionYear'
}];
};
const MoviesView: FunctionComponent<IProps> = ({ topParentId }: IProps) => {
const savedQueryKey = topParentId + '-movies';
const savedViewKey = savedQueryKey + '-view';
const [ itemsResult, setItemsResult ] = useState<BaseItemDtoQueryResult>();
const element = useRef<HTMLDivElement>(null);
const query = useMemo<IQuery>(() => ({
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]);
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]);
}, []);
return (
<div ref={element}>
<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} />
<Shuffle itemsResult= {itemsResult} topParentId={topParentId} />
<SelectView getCurrentViewStyle={getCurrentViewStyle} savedViewKey={savedViewKey} query={query} reloadItems={reloadItems} />
<Sort sortMenuOptions={SortMenuOptions} query={query} savedQueryKey={savedQueryKey} reloadItems={reloadItems} />
<Filter query={query} reloadItems={reloadItems} />
</div>
<AlphaPickerContainer query={query} reloadItems={reloadItems} />
<ItemsContainer getCurrentViewStyle={getCurrentViewStyle} query={query} items={itemsResult?.Items} noItemsMessage= 'NoCreatedLibraries' />
<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} />
</div>
</div>
<ViewItemsContainer
topParentId={topParentId}
isBtnShuffleEnabled={true}
getBasekey={getBasekey}
getFilterMode={getFilterMode}
getItemTypes={getItemTypes}
getSortMenuOptions={getSortMenuOptions}
getNoItemsMessage={getNoItemsMessage}
/>
);
};

View file

@ -57,7 +57,7 @@ const SuggestionsView: FunctionComponent<IProps> = (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<IProps> = (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;

View file

@ -1,19 +1,31 @@
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';
import ViewItemsContainer from '../components/ViewItemsContainer';
const SortMenuOptions = () => {
type IProps = {
topParentId: string | null;
}
const TrailersView: FunctionComponent<IProps> = ({ topParentId }: IProps) => {
const getBasekey = useCallback(() => {
return 'trailers';
}, []);
const getFilterMode = useCallback(() => {
return 'movies';
}, []);
const getItemTypes = useCallback(() => {
return 'Trailer';
}, []);
const getNoItemsMessage = useCallback(() => {
return 'MessageNoTrailersFound';
}, []);
const getSortMenuOptions = useCallback(() => {
return [{
name: globalize.translate('Name'),
id: 'SortName'
@ -36,75 +48,17 @@ const SortMenuOptions = () => {
name: globalize.translate('OptionReleaseDate'),
id: 'PremiereDate,SortName'
}];
};
type IProps = {
topParentId: string | null;
}
const TrailersView: FunctionComponent<IProps> = ({ topParentId }: IProps) => {
const savedQueryKey = topParentId + '-trailers';
const savedViewKey = savedQueryKey + '-view';
const [ itemsResult, setItemsResult ] = useState<BaseItemDtoQueryResult>();
const element = useRef<HTMLDivElement>(null);
const query = useMemo<IQuery>(() => ({
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]);
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();
});
}, [query]);
useEffect(() => {
reloadItems();
}, [query, reloadItems]);
}, []);
return (
<div ref={element}>
<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} />
<Filter query={query} reloadItems={reloadItems} />
</div>
<AlphaPickerContainer query={query} reloadItems={reloadItems} />
<ItemsContainer getCurrentViewStyle={getCurrentViewStyle} query={query} items={itemsResult?.Items} noItemsMessage= 'MessageNoTrailersFound' />
<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} />
</div>
</div>
<ViewItemsContainer
topParentId={topParentId}
getBasekey={getBasekey}
getFilterMode={getFilterMode}
getItemTypes={getItemTypes}
getSortMenuOptions={getSortMenuOptions}
getNoItemsMessage={getNoItemsMessage}
/>
);
};