use viewSettings instead of libraryBrowser.showLayoutMenu
This commit is contained in:
parent
0dc9ad8904
commit
1ac97c878a
15 changed files with 287 additions and 168 deletions
|
@ -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();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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",
|
||||||
|
|
|
@ -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>
|
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -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';
|
||||||
|
|
||||||
|
|
|
@ -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}>
|
||||||
|
|
|
@ -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 (
|
||||||
|
|
|
@ -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';
|
||||||
|
|
|
@ -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';
|
||||||
|
|
|
@ -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}>
|
||||||
|
|
|
@ -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';
|
||||||
|
|
|
@ -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>
|
||||||
);
|
);
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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';
|
||||||
|
|
|
@ -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[]>([]);
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue