use viewSettings instead of libraryBrowser.showLayoutMenu

This commit is contained in:
grafixeyehero 2022-10-05 02:44:28 +03:00
parent 0dc9ad8904
commit 1ac97c878a
15 changed files with 287 additions and 168 deletions

View file

@ -57,7 +57,7 @@ function showIfAllowed(context, selector, visible) {
class ViewSettings { class ViewSettings {
show(options) { show(options) {
return new Promise(function (resolve, reject) { return new Promise(function (resolve) {
const dialogOptions = { const dialogOptions = {
removeOnClose: true, removeOnClose: true,
scrollY: false scrollY: false
@ -99,8 +99,9 @@ class ViewSettings {
initEditor(dlg, options.settings); initEditor(dlg, options.settings);
dlg.querySelector('.selectImageType').addEventListener('change', function () { dlg.querySelector('.selectImageType').addEventListener('change', function () {
showIfAllowed(dlg, '.chkTitleContainer', this.value !== 'list'); showIfAllowed(dlg, '.chkTitleContainer', this.value !== 'list' && this.value !== 'banner');
showIfAllowed(dlg, '.chkYearContainer', this.value !== 'list'); showIfAllowed(dlg, '.chkYearContainer', this.value !== 'list' && this.value !== 'banner');
showIfAllowed(dlg, '.chkCardLayoutContainer', this.value !== 'list' && this.value !== 'banner');
}); });
dlg.querySelector('.btnCancel').addEventListener('click', function () { dlg.querySelector('.btnCancel').addEventListener('click', function () {
@ -126,11 +127,10 @@ class ViewSettings {
if (submitted) { if (submitted) {
saveValues(dlg, options.settings, options.settingsKey); saveValues(dlg, options.settings, options.settingsKey);
resolve(); return resolve();
return;
} }
reject(); return resolve();
}); });
}); });
} }

View file

@ -35,6 +35,13 @@
<span>${GroupBySeries}</span> <span>${GroupBySeries}</span>
</label> </label>
</div> </div>
<div class="checkboxContainer viewSetting viewSetting-checkboxContainer hide chkCardLayoutContainer" data-settingname="cardLayout">
<label>
<input is="emby-checkbox" type="checkbox" />
<span>${EnableCardLayout}</span>
</label>
</div>
</div> </div>
</form> </form>
</div> </div>

View file

@ -236,6 +236,7 @@
"EnableNextVideoInfoOverlayHelp": "At the end of a video, display info about the next video coming up in the current playlist.", "EnableNextVideoInfoOverlayHelp": "At the end of a video, display info about the next video coming up in the current playlist.",
"EnablePhotos": "Display the photos", "EnablePhotos": "Display the photos",
"EnablePhotosHelp": "Images will be detected and displayed alongside other media files.", "EnablePhotosHelp": "Images will be detected and displayed alongside other media files.",
"EnableCardLayout": "Display visual CardBox",
"EnableRewatchingNextUp": "Enable Rewatching in Next Up", "EnableRewatchingNextUp": "Enable Rewatching in Next Up",
"EnableRewatchingNextUpHelp": "Enable showing already watched episodes in 'Next Up' sections.", "EnableRewatchingNextUpHelp": "Enable showing already watched episodes in 'Next Up' sections.",
"EnableQuickConnect": "Enable Quick Connect on this server", "EnableQuickConnect": "Enable Quick Connect on this server",

View file

@ -1,33 +1,34 @@
import React, { FunctionComponent, useCallback, useEffect, useRef, useState } from 'react'; import React, { FC, useCallback, useEffect, useRef, useState } from 'react';
import AlphaPicker from '../../components/alphaPicker/alphaPicker'; import AlphaPicker from '../../components/alphaPicker/alphaPicker';
import { IQuery } from './type'; import { AlphaPickerValueI, QueryI } from './interface';
type AlphaPickerProps = { interface AlphaPickerContainerI {
query: IQuery; getQuery: () => QueryI
reloadItems: () => void; setAlphaPickerValue: React.Dispatch<AlphaPickerValueI>;
}; setStartIndex: React.Dispatch<React.SetStateAction<number>>;
}
const AlphaPickerContainer: FunctionComponent<AlphaPickerProps> = ({ query, reloadItems }: AlphaPickerProps) => { const AlphaPickerContainer: FC<AlphaPickerContainerI> = ({ getQuery, setAlphaPickerValue, setStartIndex }) => {
const [ alphaPicker, setAlphaPicker ] = useState<AlphaPicker>(); const [ alphaPicker, setAlphaPicker ] = useState<AlphaPicker>();
const element = useRef<HTMLDivElement>(null); const element = useRef<HTMLDivElement>(null);
const query = getQuery();
alphaPicker?.updateControls(query); alphaPicker?.updateControls(query);
const onAlphaPickerChange = useCallback((e) => { const onAlphaPickerChange = useCallback((e) => {
const newValue = (e as CustomEvent).detail.value; const newValue = (e as CustomEvent).detail.value;
let updatedValue;
if (newValue === '#') { if (newValue === '#') {
query.NameLessThan = 'A'; updatedValue = {NameLessThan: 'A'};
delete query.NameStartsWith;
} else { } else {
query.NameStartsWith = newValue; updatedValue = {NameStartsWith: newValue};
delete query.NameLessThan;
} }
query.StartIndex = 0; setAlphaPickerValue(updatedValue);
reloadItems(); setStartIndex(0);
}, [query, reloadItems]); }, [setStartIndex, setAlphaPickerValue]);
useEffect(() => { useEffect(() => {
const alphaPickerElement = element.current?.querySelector('.alphaPicker'); const alphaPickerElement = element.current;
setAlphaPicker(new AlphaPicker({ setAlphaPicker(new AlphaPicker({
element: alphaPickerElement, element: alphaPickerElement,
@ -37,12 +38,14 @@ const AlphaPickerContainer: FunctionComponent<AlphaPickerProps> = ({ query, relo
if (alphaPickerElement) { if (alphaPickerElement) {
alphaPickerElement.addEventListener('alphavaluechanged', onAlphaPickerChange); alphaPickerElement.addEventListener('alphavaluechanged', onAlphaPickerChange);
} }
return () => {
alphaPickerElement?.removeEventListener('alphavaluechanged', onAlphaPickerChange);
};
}, [onAlphaPickerChange]); }, [onAlphaPickerChange]);
return ( return (
<div ref={element}> <div ref={element} className='alphaPicker alphaPicker-fixed alphaPicker-fixed-right alphaPicker-vertical alphabetPicker-right' />
<div className='alphaPicker alphaPicker-fixed alphaPicker-fixed-right alphaPicker-vertical alphabetPicker-right' />
</div>
); );
}; };

View file

@ -1,7 +1,7 @@
import '../../elements/emby-button/emby-button'; import '../../elements/emby-button/emby-button';
import '../../elements/emby-itemscontainer/emby-itemscontainer'; import '../../elements/emby-itemscontainer/emby-itemscontainer';
import { BaseItemDtoQueryResult } from '@thornbill/jellyfin-sdk/dist/generated-client'; import type { BaseItemDtoQueryResult } from '@jellyfin/sdk/lib/generated-client';
import escapeHTML from 'escape-html'; import escapeHTML from 'escape-html';
import React, { FC, useCallback, useEffect, useRef } from 'react'; import React, { FC, useCallback, useEffect, useRef } from 'react';

View file

@ -1,5 +1,5 @@
import { BaseItemDto } from '@thornbill/jellyfin-sdk/dist/generated-client'; import type { BaseItemDto } from '@jellyfin/sdk/lib/generated-client';
import React, { FC, useEffect, useRef } from 'react'; import React, { FC, useCallback, useEffect, useRef } from 'react';
import ItemsContainerElement from '../../elements/ItemsContainerElement'; import ItemsContainerElement from '../../elements/ItemsContainerElement';
import cardBuilder from '../../components/cardbuilder/cardBuilder'; import cardBuilder from '../../components/cardbuilder/cardBuilder';
@ -7,82 +7,78 @@ import listview from '../../components/listview/listview';
import globalize from '../../scripts/globalize'; import globalize from '../../scripts/globalize';
import imageLoader from '../../components/images/imageLoader'; import imageLoader from '../../components/images/imageLoader';
import '../../elements/emby-itemscontainer/emby-itemscontainer'; import '../../elements/emby-itemscontainer/emby-itemscontainer';
import { QueryI } from './interface'; import { CardOptionsI } from './interface';
interface ItemsContainerI { interface ItemsContainerI {
getCurrentViewStyle: () => string; getViewSettings: () => {
query: QueryI; showTitle: string | boolean;
cardLayout: string | boolean;
showYear: string | boolean;
imageType: string;
viewType: string;
};
getContext: () => string | null; getContext: () => string | null;
items?: BaseItemDto[] | null; items?: BaseItemDto[] | null;
noItemsMessage?: string; noItemsMessage?: string;
} }
const ItemsContainer: FC<ItemsContainerI> = ({ getCurrentViewStyle, query, getContext, items = [], noItemsMessage }) => { const ItemsContainer: FC<ItemsContainerI> = ({ getViewSettings, getContext, items = [], noItemsMessage }) => {
const element = useRef<HTMLDivElement>(null); const element = useRef<HTMLDivElement>(null);
const viewStyle = getCurrentViewStyle(); const viewsettings = getViewSettings();
useEffect(() => { const getCardOptions = useCallback(() => {
let html; let shape;
let preferThumb;
let preferDisc;
let preferLogo;
if (viewStyle == 'Thumb') { if (viewsettings.imageType === 'banner') {
html = cardBuilder.getCardsHtml(items, { shape = 'banner';
items: items, } else if (viewsettings.imageType === 'disc') {
shape: 'backdrop', shape = 'square';
preferThumb: true, preferDisc = true;
} else if (viewsettings.imageType === 'logo') {
shape = 'backdrop';
preferLogo = true;
} else if (viewsettings.imageType === 'thumb') {
shape = 'backdrop';
preferThumb = true;
} else {
shape = 'autoVertical';
}
const cardOptions: CardOptionsI = {
shape: shape,
showTitle: viewsettings.showTitle,
showYear: viewsettings.showTitle,
cardLayout: viewsettings.cardLayout,
centerText: true,
context: getContext(), context: getContext(),
lazy: true, coverImage: true,
overlayPlayButton: true, preferThumb: preferThumb,
showTitle: true, preferDisc: preferDisc,
showYear: true, preferLogo: preferLogo,
centerText: true overlayPlayButton: false,
}); overlayMoreButton: true,
} else if (viewStyle == 'ThumbCard') { overlayText: !viewsettings.showTitle
html = cardBuilder.getCardsHtml(items, { };
items: items,
shape: 'backdrop', cardOptions.items = items;
preferThumb: true,
context: getContext(), return cardOptions;
lazy: true, }, [getContext, items, viewsettings.cardLayout, viewsettings.imageType, viewsettings.showTitle]);
cardLayout: true,
showTitle: true, const getItemsHtml = useCallback(() => {
showYear: true, const settings = getViewSettings();
centerText: true
}); let html = '';
} else if (viewStyle == 'Banner') {
html = cardBuilder.getCardsHtml(items, { if (settings.imageType === 'list') {
items: items,
shape: 'banner',
preferBanner: true,
context: getContext(),
lazy: true
});
} else if (viewStyle == 'List') {
html = listview.getListViewHtml({ html = listview.getListViewHtml({
items: items, items: items,
context: getContext(), context: getContext()});
sortBy: query.SortBy
});
} else if (viewStyle == 'PosterCard') {
html = cardBuilder.getCardsHtml(items, {
items: items,
shape: 'portrait',
context: getContext(),
showTitle: true,
showYear: true,
centerText: true,
lazy: true,
cardLayout: true
});
} else { } else {
html = cardBuilder.getCardsHtml(items, { html = cardBuilder.getCardsHtml(items, getCardOptions());
items: items,
shape: 'portrait',
context: getContext(),
overlayPlayButton: true,
showTitle: true,
showYear: true,
centerText: true
});
} }
if (!items?.length) { if (!items?.length) {
@ -94,12 +90,16 @@ const ItemsContainer: FC<ItemsContainerI> = ({ getCurrentViewStyle, query, getCo
html += '</div>'; html += '</div>';
} }
const itemsContainer = element.current?.querySelector('.itemsContainer') as HTMLDivElement; return html;
itemsContainer.innerHTML = html; }, [getCardOptions, getContext, getViewSettings, items, noItemsMessage]);
imageLoader.lazyChildren(itemsContainer);
}, [query.SortBy, items, noItemsMessage, viewStyle, getContext]);
const cssClass = viewStyle == 'List' ? 'vertical-list' : 'vertical-wrap'; useEffect(() => {
const itemsContainer = element.current?.querySelector('.itemsContainer') as HTMLDivElement;
itemsContainer.innerHTML = getItemsHtml();
imageLoader.lazyChildren(itemsContainer);
}, [getItemsHtml]);
const cssClass = viewsettings.imageType == 'List' ? 'vertical-list' : 'vertical-wrap';
return ( return (
<div ref={element}> <div ref={element}>

View file

@ -1,36 +1,35 @@
import { BaseItemDtoQueryResult } from '@thornbill/jellyfin-sdk/dist/generated-client'; import type { BaseItemDtoQueryResult } from '@jellyfin/sdk/lib/generated-client';
import React, { FC, useCallback, useEffect, useRef } from 'react'; import React, { FC, useCallback, useEffect, useRef } from 'react';
import IconButtonElement from '../../elements/IconButtonElement'; import IconButtonElement from '../../elements/IconButtonElement';
import globalize from '../../scripts/globalize'; import globalize from '../../scripts/globalize';
import { QueryI } from './interface'; import * as userSettings from '../../scripts/settings/userSettings';
interface PaginationI { interface PaginationI {
query: QueryI; startIndex: number
setStartIndex: React.Dispatch<React.SetStateAction<number>>;
itemsResult?: BaseItemDtoQueryResult; itemsResult?: BaseItemDtoQueryResult;
reloadItems: () => void;
} }
const Pagination: FC<PaginationI> = ({ query, itemsResult = {}, reloadItems }) => { const Pagination: FC<PaginationI> = ({ startIndex, setStartIndex, itemsResult = {} }) => {
const startIndex = query.StartIndex; const limit = userSettings.libraryPageSize(undefined);
const limit = query.Limit;
const totalRecordCount = itemsResult.TotalRecordCount || 0; const totalRecordCount = itemsResult.TotalRecordCount || 0;
const recordsEnd = Math.min(startIndex + limit, totalRecordCount); const recordsEnd = Math.min(startIndex + limit, totalRecordCount);
const showControls = limit < totalRecordCount; const showControls = limit < totalRecordCount;
const element = useRef<HTMLDivElement>(null); const element = useRef<HTMLDivElement>(null);
const onNextPageClick = useCallback(() => { const onNextPageClick = useCallback(() => {
if (query.Limit > 0) { if (limit > 0) {
query.StartIndex += query.Limit; const newIndex = startIndex + limit;
setStartIndex(newIndex);
} }
reloadItems(); }, [limit, setStartIndex, startIndex]);
}, [query, reloadItems]);
const onPreviousPageClick = useCallback(() => { const onPreviousPageClick = useCallback(() => {
if (query.Limit > 0) { if (limit > 0) {
query.StartIndex = Math.max(0, query.StartIndex - query.Limit); const newIndex = Math.max(0, startIndex - limit);
setStartIndex(newIndex);
} }
reloadItems(); }, [limit, setStartIndex, startIndex]);
}, [query, reloadItems]);
useEffect(() => { useEffect(() => {
const btnNextPage = element.current?.querySelector('.btnNextPage') as HTMLButtonElement; const btnNextPage = element.current?.querySelector('.btnNextPage') as HTMLButtonElement;
@ -52,6 +51,11 @@ const Pagination: FC<PaginationI> = ({ query, itemsResult = {}, reloadItems }) =
} }
btnPreviousPage.addEventListener('click', onPreviousPageClick); btnPreviousPage.addEventListener('click', onPreviousPageClick);
} }
return () => {
btnNextPage?.removeEventListener('click', onNextPageClick);
btnPreviousPage?.removeEventListener('click', onPreviousPageClick);
};
}, [totalRecordCount, onNextPageClick, onPreviousPageClick, limit, startIndex]); }, [totalRecordCount, onNextPageClick, onPreviousPageClick, limit, startIndex]);
return ( return (

View file

@ -1,4 +1,4 @@
import { RecommendationDto } from '@thornbill/jellyfin-sdk/dist/generated-client'; import type { RecommendationDto } from '@jellyfin/sdk/lib/generated-client';
import React, { FC } from 'react'; import React, { FC } from 'react';
import globalize from '../../scripts/globalize'; import globalize from '../../scripts/globalize';

View file

@ -1,6 +1,6 @@
import '../../elements/emby-itemscontainer/emby-itemscontainer'; import '../../elements/emby-itemscontainer/emby-itemscontainer';
import { BaseItemDto } from '@thornbill/jellyfin-sdk/dist/generated-client'; import type { BaseItemDto } from '@jellyfin/sdk/lib/generated-client';
import React, { FC, useEffect, useRef } from 'react'; import React, { FC, useEffect, useRef } from 'react';
import cardBuilder from '../../components/cardbuilder/cardBuilder'; import cardBuilder from '../../components/cardbuilder/cardBuilder';

View file

@ -1,32 +1,43 @@
import React, { FC, useEffect, useRef } from 'react'; import React, { FC, useCallback, useEffect, useRef } from 'react';
import IconButtonElement from '../../elements/IconButtonElement'; import IconButtonElement from '../../elements/IconButtonElement';
import libraryBrowser from '../../scripts/libraryBrowser';
import * as userSettings from '../../scripts/settings/userSettings';
import { QueryI } from './interface';
interface SelectViewI { interface SelectViewI {
getCurrentViewStyle: () => string; getSettingsKey: () => string;
query: QueryI; getVisibleViewSettings: () => string[];
getViewSettings: () => string; getViewSettings: () => {
showTitle: string | boolean;
cardLayout: string | boolean;
showYear: string | boolean;
imageType: string;
viewType: string;
};
reloadItems: () => void; reloadItems: () => void;
} }
const SelectView: FC<SelectViewI> = ({ getCurrentViewStyle, getViewSettings, query, reloadItems }) => { const SelectView: FC<SelectViewI> = ({ getSettingsKey, getVisibleViewSettings, getViewSettings, reloadItems }) => {
const element = useRef<HTMLDivElement>(null); const element = useRef<HTMLDivElement>(null);
const showViewSettingsMenu = useCallback(() => {
import('../../components/viewSettings/viewSettings').then(({default: ViewSettings}) => {
const viewSettings = new ViewSettings();
viewSettings.show({
settingsKey: getSettingsKey(),
settings: getViewSettings(),
visibleSettings: getVisibleViewSettings()
}).then(() => {
reloadItems();
});
});
}, [getSettingsKey, getViewSettings, getVisibleViewSettings, reloadItems]);
useEffect(() => { useEffect(() => {
const btnSelectView = element.current?.querySelector('.btnSelectView') as HTMLButtonElement; const btnSelectView = element.current?.querySelector('.btnSelectView') as HTMLButtonElement;
btnSelectView.addEventListener('click', (e) => { btnSelectView?.addEventListener('click', showViewSettingsMenu);
libraryBrowser.showLayoutMenu(e.target, getCurrentViewStyle(), 'Banner,List,Poster,PosterCard,Thumb,ThumbCard'.split(','));
}); return () => {
btnSelectView.addEventListener('layoutchange', (e) => { btnSelectView?.removeEventListener('click', showViewSettingsMenu);
const viewStyle = (e as CustomEvent).detail.viewStyle; };
userSettings.set(getViewSettings(), viewStyle, false); }, [showViewSettingsMenu]);
query.StartIndex = 0;
reloadItems();
});
}, [getCurrentViewStyle, query, reloadItems, getViewSettings]);
return ( return (
<div ref={element}> <div ref={element}>

View file

@ -1,4 +1,4 @@
import { BaseItemDtoQueryResult } from '@thornbill/jellyfin-sdk/dist/generated-client'; import type { BaseItemDtoQueryResult } from '@jellyfin/sdk/lib/generated-client';
import React, { FC, useCallback, useEffect, useRef } from 'react'; import React, { FC, useCallback, useEffect, useRef } from 'react';
import { playbackManager } from '../../components/playback/playbackmanager'; import { playbackManager } from '../../components/playback/playbackmanager';

View file

@ -1,5 +1,5 @@
import { BaseItemDtoQueryResult } from '@thornbill/jellyfin-sdk/dist/generated-client'; import type { BaseItemDtoQueryResult } from '@jellyfin/sdk/lib/generated-client';
import React, { FC, useCallback, useEffect, useRef, useState } from 'react'; import React, { FC, useCallback, useEffect, useMemo, useRef, useState } from 'react';
import loading from '../../components/loading/loading'; import loading from '../../components/loading/loading';
import * as userSettings from '../../scripts/settings/userSettings'; import * as userSettings from '../../scripts/settings/userSettings';
@ -10,9 +10,10 @@ import Pagination from './Pagination';
import SelectView from './SelectView'; import SelectView from './SelectView';
import Shuffle from './Shuffle'; import Shuffle from './Shuffle';
import Sort from './Sort'; import Sort from './Sort';
import { QueryI } from './interface';
import NewCollection from './NewCollection'; import NewCollection from './NewCollection';
import globalize from '../../scripts/globalize'; import globalize from '../../scripts/globalize';
import layoutManager from '../../components/layoutManager';
import { AlphaPickerValueI, QueryI } from './interface';
interface ViewItemsContainerI { interface ViewItemsContainerI {
topParentId: string | null; topParentId: string | null;
@ -36,15 +37,37 @@ const ViewItemsContainer: FC<ViewItemsContainerI> = ({
getNoItemsMessage getNoItemsMessage
}) => { }) => {
const [ itemsResult, setItemsResult ] = useState<BaseItemDtoQueryResult>({}); const [ itemsResult, setItemsResult ] = useState<BaseItemDtoQueryResult>({});
const [ startIndex, setStartIndex ] = useState<number>(0);
const [ alphaPickerValue, setAlphaPickerValue ] = useState<AlphaPickerValueI>({});
const element = useRef<HTMLDivElement>(null); const element = useRef<HTMLDivElement>(null);
const queryAlphaPickerValue = useMemo(() => ({
...alphaPickerValue
}), [alphaPickerValue]);
const getSettingsKey = useCallback(() => { const getSettingsKey = useCallback(() => {
return `${topParentId} - ${getBasekey()}`; return `${topParentId} - ${getBasekey()}`;
}, [getBasekey, topParentId]); }, [getBasekey, topParentId]);
const getVisibleViewSettings = useCallback(() => {
return [
'showTitle',
'showYear',
'imageType',
'cardLayout'
];
}, []);
const getViewSettings = useCallback(() => { const getViewSettings = useCallback(() => {
return `${getSettingsKey()} -view`; const basekey = getSettingsKey();
return {
showTitle: userSettings.get(basekey + '-showTitle', false) !== 'false',
showYear: userSettings.get(basekey + '-showYear', false) !== 'false',
imageType: userSettings.get(basekey + '-imageType', false) || 'primary',
viewType: userSettings.get(basekey + '-viewType', false) || 'images',
cardLayout: userSettings.get(basekey + '-cardLayout', false) !== 'false'
};
}, [getSettingsKey]); }, [getSettingsKey]);
const getDefaultSortBy = useCallback(() => { const getDefaultSortBy = useCallback(() => {
@ -102,16 +125,26 @@ const ViewItemsContainer: FC<ViewItemsContainerI> = ({
}, []); }, []);
const getQuery = useCallback(() => { const getQuery = useCallback(() => {
let fields = 'BasicSyncInfo,MediaSourceCount';
const viewsettings = getViewSettings();
if (viewsettings.imageType === 'primary') {
fields += ',PrimaryImageAspectRatio';
}
if (viewsettings.showYear) {
fields += ',ProductionYear';
}
const query: QueryI = { const query: QueryI = {
SortBy: getSortValues().sortBy, SortBy: getSortValues().sortBy,
SortOrder: getSortValues().sortOrder, SortOrder: getSortValues().sortOrder,
IncludeItemTypes: getItemTypes().join(','), IncludeItemTypes: getItemTypes().join(','),
Recursive: true, Recursive: true,
Fields: 'PrimaryImageAspectRatio,MediaSourceCount,BasicSyncInfo', Fields: fields,
ImageTypeLimit: 1, ImageTypeLimit: 1,
EnableImageTypes: 'Primary,Backdrop,Banner,Thumb', EnableImageTypes: 'Primary,Backdrop,Banner,Thumb,Disc,Logo',
Limit: userSettings.libraryPageSize(undefined), Limit: userSettings.libraryPageSize(undefined),
StartIndex: 0, StartIndex: startIndex,
ParentId: topParentId ParentId: topParentId
}; };
@ -119,13 +152,13 @@ const ViewItemsContainer: FC<ViewItemsContainerI> = ({
query.IsFavorite = true; query.IsFavorite = true;
} }
userSettings.loadQuerySettings(getSettingsKey(), query); const queryInfo: QueryI = Object.assign(query, queryAlphaPickerValue || {});
return query;
}, [getSortValues, getItemTypes, topParentId, getBasekey, getSettingsKey]); return queryInfo;
}, [getViewSettings, getSortValues, getItemTypes, startIndex, topParentId, getBasekey, queryAlphaPickerValue]);
const getQueryWithFilters = useCallback(() => { const getQueryWithFilters = useCallback(() => {
const query = getQuery(); const query = getQuery();
const queryFilters = []; const queryFilters = [];
let hasFilters; let hasFilters;
@ -248,10 +281,6 @@ const ViewItemsContainer: FC<ViewItemsContainerI> = ({
}]; }];
}, []); }, []);
const getCurrentViewStyle = useCallback(() => {
return userSettings.get(getViewSettings(), false) || 'Poster';
}, [getViewSettings]);
const getContext = useCallback(() => { const getContext = useCallback(() => {
const itemType = getItemTypes().join(','); const itemType = getItemTypes().join(',');
if (itemType === 'Movie' || itemType === 'BoxSet') { if (itemType === 'Movie' || itemType === 'BoxSet') {
@ -269,8 +298,8 @@ const ViewItemsContainer: FC<ViewItemsContainerI> = ({
return; return;
} }
loading.show(); loading.show();
const querywithfilters = getQueryWithFilters().query; const query = getQueryWithFilters().query;
window.ApiClient.getItems(window.ApiClient.getCurrentUserId(), querywithfilters).then((result) => { window.ApiClient.getItems(window.ApiClient.getCurrentUserId(), query).then((result) => {
setItemsResult(result); setItemsResult(result);
window.scrollTo(0, 0); window.scrollTo(0, 0);
@ -289,10 +318,20 @@ const ViewItemsContainer: FC<ViewItemsContainerI> = ({
return ( return (
<div ref={element}> <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'> <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={getQuery()} reloadItems={reloadItems} /> <Pagination
itemsResult= {itemsResult}
startIndex={startIndex}
setStartIndex={setStartIndex}
/>
{isBtnShuffleEnabled && <Shuffle itemsResult={itemsResult} topParentId={topParentId} />} {isBtnShuffleEnabled && <Shuffle itemsResult={itemsResult} topParentId={topParentId} />}
<SelectView getCurrentViewStyle={getCurrentViewStyle} getViewSettings={getViewSettings} query={getQuery()} reloadItems={reloadItems} />
{<SelectView
getSettingsKey={getSettingsKey}
getVisibleViewSettings={getVisibleViewSettings}
getViewSettings={getViewSettings}
reloadItems={reloadItems}
/>}
<Sort <Sort
getSortMenuOptions={getSortMenuOptions} getSortMenuOptions={getSortMenuOptions}
@ -315,18 +354,25 @@ const ViewItemsContainer: FC<ViewItemsContainerI> = ({
</div> </div>
{isAlphaPickerEnabled && <AlphaPickerContainer query={getQuery()} reloadItems={reloadItems} />} {isAlphaPickerEnabled && <AlphaPickerContainer
getQuery={getQuery}
setAlphaPickerValue={setAlphaPickerValue}
setStartIndex={setStartIndex}
/>}
<ItemsContainer <ItemsContainer
getCurrentViewStyle={getCurrentViewStyle} getViewSettings={getViewSettings}
query={getQuery()}
getContext={getContext} getContext={getContext}
items={itemsResult?.Items} items={itemsResult?.Items}
noItemsMessage={getNoItemsMessage()} noItemsMessage={getNoItemsMessage()}
/> />
<div className='flex align-items-center justify-content-center flex-wrap-wrap padded-top padded-left padded-right padded-bottom focuscontainer-x'> <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={getQuery()} reloadItems={reloadItems} /> <Pagination
itemsResult= {itemsResult}
startIndex={startIndex}
setStartIndex={setStartIndex}
/>
</div> </div>
</div> </div>
); );

View file

@ -1,3 +1,9 @@
import type { BaseItemDto } from '@jellyfin/sdk/lib/generated-client';
export interface AlphaPickerValueI {
NameLessThan?: string;
NameStartsWith?: string | null;
}
export interface QueryI { export interface QueryI {
SortBy?: string; SortBy?: string;
SortOrder?: string; SortOrder?: string;
@ -7,14 +13,14 @@ export interface QueryI {
ImageTypeLimit?: number; ImageTypeLimit?: number;
EnableTotalRecordCount?: boolean; EnableTotalRecordCount?: boolean;
EnableImageTypes?: string; EnableImageTypes?: string;
StartIndex: number; StartIndex?: number;
ParentId?: string | null; ParentId?: string | null;
IsFavorite?: boolean; IsFavorite?: boolean;
IsMissing?: boolean; IsMissing?: boolean;
Limit:number; Limit:number;
NameStartsWithOrGreater?: string; NameStartsWithOrGreater?: string;
NameLessThan?: string; NameLessThan?: string;
NameStartsWith?: string; NameStartsWith?: string | null;
VideoTypes?: string; VideoTypes?: string;
GenreIds?: string; GenreIds?: string;
Is4K?: boolean; Is4K?: boolean;
@ -48,8 +54,9 @@ export interface FiltersI {
} }
export interface CardOptionsI { export interface CardOptionsI {
itemsContainer?: HTMLElement; itemsContainer?: HTMLElement | null;
parentContainer?: HTMLElement; parentContainer?: HTMLElement | null;
items?: BaseItemDto[] | null;
allowBottomPadding?: boolean; allowBottomPadding?: boolean;
centerText?: boolean; centerText?: boolean;
coverImage?: boolean; coverImage?: boolean;
@ -58,17 +65,19 @@ export interface CardOptionsI {
overlayPlayButton?: boolean; overlayPlayButton?: boolean;
overlayText?: boolean; overlayText?: boolean;
preferThumb?: boolean; preferThumb?: boolean;
preferDisc?: boolean;
preferLogo?: boolean;
scalable?: boolean; scalable?: boolean;
shape?: string; shape?: string | null;
lazy?: boolean; lazy?: boolean;
cardLayout?: boolean; cardLayout?: boolean | string;
showParentTitle?: boolean; showParentTitle?: boolean;
showParentTitleOrTitle?: boolean; showParentTitleOrTitle?: boolean;
showAirTime?: boolean; showAirTime?: boolean;
showAirDateTime?: boolean; showAirDateTime?: boolean;
showChannelName?: boolean; showChannelName?: boolean;
showTitle?: boolean; showTitle?: boolean | string;
showYear?: boolean; showYear?: boolean | string;
showDetailsMenu?: boolean; showDetailsMenu?: boolean;
missingIndicator?: boolean; missingIndicator?: boolean;
showLocationTypeIndicator?: boolean; showLocationTypeIndicator?: boolean;
@ -76,5 +85,43 @@ export interface CardOptionsI {
showUnplayedIndicator?: boolean; showUnplayedIndicator?: boolean;
showChildCountIndicator?: boolean; showChildCountIndicator?: boolean;
lines?: number; lines?: number;
context?: string; context?: string | null;
action?: string | null;
defaultShape?: string;
indexBy?: string;
parentId?: string | null;
showMenu?: boolean;
cardCssClass?: string | null;
cardClass?: string | null;
centerPlayButton?: boolean;
overlayInfoButton?: boolean;
autoUpdate?: boolean;
cardFooterAside?: string;
includeParentInfoInTitle?: boolean;
maxLines?: number;
overlayMarkPlayedButton?: boolean;
overlayRateButton?: boolean;
showAirEndTime?: boolean;
showCurrentProgram?: boolean;
showCurrentProgramTime?: boolean;
showItemCounts?: boolean;
showPersonRoleOrType?: boolean;
showProgressBar?: boolean;
showPremiereDate?: boolean;
showRuntime?: boolean;
showSeriesTimerTime?: boolean;
showSeriesTimerChannel?: boolean;
showSongCount?: boolean;
width?: number;
showChannelLogo?: boolean;
showLogo?: boolean;
serverId?: string;
collectionId?: string | null;
playlistId?: string | null;
defaultCardImageIcon?: string;
disableHoverMenu?: boolean;
disableIndicators?: boolean;
showGroupCount?: boolean;
containerClass?: string;
noItemsMessage?: string;
} }

View file

@ -1,4 +1,4 @@
import { BaseItemDtoQueryResult } from '@thornbill/jellyfin-sdk/dist/generated-client'; import type { BaseItemDtoQueryResult } from '@jellyfin/sdk/lib/generated-client';
import React, { FC, useCallback, useEffect, useMemo, useRef, useState } from 'react'; import React, { FC, useCallback, useEffect, useMemo, useRef, useState } from 'react';
import loading from '../../components/loading/loading'; import loading from '../../components/loading/loading';

View file

@ -1,5 +1,5 @@
import { BaseItemDto, BaseItemDtoQueryResult, RecommendationDto } from '@thornbill/jellyfin-sdk/dist/generated-client'; import type { BaseItemDto, BaseItemDtoQueryResult, RecommendationDto } from '@jellyfin/sdk/lib/generated-client';
import React, { FunctionComponent, useCallback, useEffect, useRef, useState } from 'react'; import React, { FC, useCallback, useEffect, useRef, useState } from 'react';
import layoutManager from '../../components/layoutManager'; import layoutManager from '../../components/layoutManager';
import loading from '../../components/loading/loading'; import loading from '../../components/loading/loading';
@ -12,7 +12,7 @@ interface SuggestionsViewI {
topParentId: string | null; topParentId: string | null;
} }
const SuggestionsView: FunctionComponent<SuggestionsViewI> = ({topParentId}) => { const SuggestionsView: FC<SuggestionsViewI> = ({topParentId}) => {
const [ latestItems, setLatestItems ] = useState<BaseItemDto[]>([]); const [ latestItems, setLatestItems ] = useState<BaseItemDto[]>([]);
const [ resumeResult, setResumeResult ] = useState<BaseItemDtoQueryResult>({}); const [ resumeResult, setResumeResult ] = useState<BaseItemDtoQueryResult>({});
const [ recommendations, setRecommendations ] = useState<RecommendationDto[]>([]); const [ recommendations, setRecommendations ] = useState<RecommendationDto[]>([]);