mirror of
https://github.com/jellyfin/jellyfin-web
synced 2025-03-30 19:56:21 +00:00
use single state and local storage
This commit is contained in:
parent
f40c565e4a
commit
6341a71fec
15 changed files with 485 additions and 508 deletions
|
@ -102,15 +102,8 @@ function onInputCommand(e) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
function saveValues(context, settings, settingsKey) {
|
function saveValues(context, settings, settingsKey, setfilters) {
|
||||||
let elems = context.querySelectorAll('.simpleFilter');
|
let elems = context.querySelectorAll('.simpleFilter');
|
||||||
for (let i = 0, length = elems.length; i < length; i++) {
|
|
||||||
if (elems[i].tagName === 'INPUT') {
|
|
||||||
setBasicFilter(context, settingsKey + '-filter-' + elems[i].getAttribute('data-settingname'), elems[i]);
|
|
||||||
} else {
|
|
||||||
setBasicFilter(context, settingsKey + '-filter-' + elems[i].getAttribute('data-settingname'), elems[i].querySelector('input'));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Video type
|
// Video type
|
||||||
const videoTypes = [];
|
const videoTypes = [];
|
||||||
|
@ -121,7 +114,6 @@ function saveValues(context, settings, settingsKey) {
|
||||||
videoTypes.push(elems[i].getAttribute('data-filter'));
|
videoTypes.push(elems[i].getAttribute('data-filter'));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
userSettings.setFilter(settingsKey + '-filter-VideoTypes', videoTypes.join(','));
|
|
||||||
|
|
||||||
// Series status
|
// Series status
|
||||||
const seriesStatuses = [];
|
const seriesStatuses = [];
|
||||||
|
@ -132,7 +124,6 @@ function saveValues(context, settings, settingsKey) {
|
||||||
seriesStatuses.push(elems[i].getAttribute('data-filter'));
|
seriesStatuses.push(elems[i].getAttribute('data-filter'));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
userSettings.setFilter(`${settingsKey}-filter-SeriesStatus`, seriesStatuses.join(','));
|
|
||||||
|
|
||||||
// Genres
|
// Genres
|
||||||
const genres = [];
|
const genres = [];
|
||||||
|
@ -143,8 +134,40 @@ function saveValues(context, settings, settingsKey) {
|
||||||
genres.push(elems[i].getAttribute('data-filter'));
|
genres.push(elems[i].getAttribute('data-filter'));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (setfilters) {
|
||||||
|
setfilters((prevState) => ({
|
||||||
|
...prevState,
|
||||||
|
StartIndex: 0,
|
||||||
|
IsPlayed: context.querySelector('.chkPlayed').checked,
|
||||||
|
IsUnplayed: context.querySelector('.chkUnplayed').checked,
|
||||||
|
IsFavorite: context.querySelector('.chkFavorite').checked,
|
||||||
|
IsResumable: context.querySelector('.chkResumable').checked,
|
||||||
|
Is4K: context.querySelector('.chk4KFilter').checked,
|
||||||
|
IsHD: context.querySelector('.chkHDFilter').checked,
|
||||||
|
IsSD: context.querySelector('.chkSDFilter').checked,
|
||||||
|
Is3D: context.querySelector('.chk3DFilter').checked,
|
||||||
|
VideoTypes: videoTypes.join(','),
|
||||||
|
SeriesStatus: seriesStatuses.join(','),
|
||||||
|
HasSubtitles: context.querySelector('.chkSubtitle').checked,
|
||||||
|
HasTrailer: context.querySelector('.chkTrailer').checked,
|
||||||
|
HasSpecialFeature: context.querySelector('.chkSpecialFeature').checked,
|
||||||
|
HasThemeSong: context.querySelector('.chkThemeSong').checked,
|
||||||
|
HasThemeVideo: context.querySelector('.chkThemeVideo').checked,
|
||||||
|
GenreIds: genres.join(',')
|
||||||
|
}));
|
||||||
|
} else {
|
||||||
|
for (let i = 0, length = elems.length; i < length; i++) {
|
||||||
|
if (elems[i].tagName === 'INPUT') {
|
||||||
|
setBasicFilter(context, settingsKey + '-filter-' + elems[i].getAttribute('data-settingname'), elems[i]);
|
||||||
|
} else {
|
||||||
|
setBasicFilter(context, settingsKey + '-filter-' + elems[i].getAttribute('data-settingname'), elems[i].querySelector('input'));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
userSettings.setFilter(settingsKey + '-filter-GenreIds', genres.join(','));
|
userSettings.setFilter(settingsKey + '-filter-GenreIds', genres.join(','));
|
||||||
}
|
}
|
||||||
|
}
|
||||||
function bindCheckboxInput(context, on) {
|
function bindCheckboxInput(context, on) {
|
||||||
const elems = context.querySelectorAll('.checkboxList-verticalwrap');
|
const elems = context.querySelectorAll('.checkboxList-verticalwrap');
|
||||||
for (let i = 0, length = elems.length; i < length; i++) {
|
for (let i = 0, length = elems.length; i < length; i++) {
|
||||||
|
@ -275,7 +298,7 @@ class FilterMenu {
|
||||||
|
|
||||||
if (submitted) {
|
if (submitted) {
|
||||||
//if (!options.onChange) {
|
//if (!options.onChange) {
|
||||||
saveValues(dlg, options.settings, options.settingsKey);
|
saveValues(dlg, options.settings, options.settingsKey, options.setfilters);
|
||||||
return resolve();
|
return resolve();
|
||||||
//}
|
//}
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,19 +5,19 @@
|
||||||
<div class="verticalSection verticalSection-extrabottompadding basicFilterSection focuscontainer-x" style="margin-top:2em;">
|
<div class="verticalSection verticalSection-extrabottompadding basicFilterSection focuscontainer-x" style="margin-top:2em;">
|
||||||
<div class="checkboxList checkboxList-verticalwrap">
|
<div class="checkboxList checkboxList-verticalwrap">
|
||||||
<label class="viewSetting simpleFilter" data-settingname="IsUnplayed">
|
<label class="viewSetting simpleFilter" data-settingname="IsUnplayed">
|
||||||
<input type="checkbox" is="emby-checkbox" />
|
<input type="checkbox" is="emby-checkbox" class="chkUnplayed" />
|
||||||
<span>${Unplayed}</span>
|
<span>${Unplayed}</span>
|
||||||
</label>
|
</label>
|
||||||
<label class="viewSetting simpleFilter" data-settingname="IsPlayed">
|
<label class="viewSetting simpleFilter" data-settingname="IsPlayed">
|
||||||
<input type="checkbox" is="emby-checkbox" />
|
<input type="checkbox" is="emby-checkbox" class="chkPlayed" />
|
||||||
<span>${Played}</span>
|
<span>${Played}</span>
|
||||||
</label>
|
</label>
|
||||||
<label class="viewSetting simpleFilter" data-settingname="IsFavorite">
|
<label class="viewSetting simpleFilter" data-settingname="IsFavorite">
|
||||||
<input type="checkbox" is="emby-checkbox" />
|
<input type="checkbox" is="emby-checkbox" class="chkFavorite" />
|
||||||
<span>${Favorite}</span>
|
<span>${Favorite}</span>
|
||||||
</label>
|
</label>
|
||||||
<label class="viewSetting simpleFilter" data-settingname="IsResumable">
|
<label class="viewSetting simpleFilter" data-settingname="IsResumable">
|
||||||
<input type="checkbox" is="emby-checkbox" />
|
<input type="checkbox" is="emby-checkbox" class="chkResumable" />
|
||||||
<span>${ContinueWatching}</span>
|
<span>${ContinueWatching}</span>
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
|
@ -49,22 +49,22 @@
|
||||||
<div class="checkboxList checkboxList-verticalwrap">
|
<div class="checkboxList checkboxList-verticalwrap">
|
||||||
|
|
||||||
<label>
|
<label>
|
||||||
<input type="checkbox" is="emby-checkbox" class="simpleFilter" data-settingname="IsHD" />
|
<input type="checkbox" is="emby-checkbox" class="simpleFilter chkHDFilter" data-settingname="IsHD" />
|
||||||
<span>HD</span>
|
<span>HD</span>
|
||||||
</label>
|
</label>
|
||||||
|
|
||||||
<label>
|
<label>
|
||||||
<input type="checkbox" is="emby-checkbox" class="simpleFilter" data-settingname="Is4K" />
|
<input type="checkbox" is="emby-checkbox" class="simpleFilter chk4KFilter" data-settingname="Is4K" />
|
||||||
<span>4K</span>
|
<span>4K</span>
|
||||||
</label>
|
</label>
|
||||||
|
|
||||||
<label>
|
<label>
|
||||||
<input type="checkbox" is="emby-checkbox" class="simpleFilter" data-settingname="IsSD" />
|
<input type="checkbox" is="emby-checkbox" class="simpleFilter chkSDFilter" data-settingname="IsSD" />
|
||||||
<span>SD</span>
|
<span>SD</span>
|
||||||
</label>
|
</label>
|
||||||
|
|
||||||
<label>
|
<label>
|
||||||
<input type="checkbox" is="emby-checkbox" class="simpleFilter" data-settingname="Is3D" />
|
<input type="checkbox" is="emby-checkbox" class="simpleFilter chk3DFilter" data-settingname="Is3D" />
|
||||||
<span>3D</span>
|
<span>3D</span>
|
||||||
</label>
|
</label>
|
||||||
<label>
|
<label>
|
||||||
|
@ -82,23 +82,23 @@
|
||||||
<h2 class="checkboxListLabel">${Features}</h2>
|
<h2 class="checkboxListLabel">${Features}</h2>
|
||||||
<div class="checkboxList checkboxList-verticalwrap">
|
<div class="checkboxList checkboxList-verticalwrap">
|
||||||
<label class="viewSetting simpleFilter" data-settingname="HasSubtitles">
|
<label class="viewSetting simpleFilter" data-settingname="HasSubtitles">
|
||||||
<input type="checkbox" is="emby-checkbox" class="chkFeatureFilter" />
|
<input type="checkbox" is="emby-checkbox" class="chkFeatureFilter chkSubtitle" />
|
||||||
<span>${Subtitles}</span>
|
<span>${Subtitles}</span>
|
||||||
</label>
|
</label>
|
||||||
<label class="viewSetting simpleFilter" data-settingname="HasTrailer">
|
<label class="viewSetting simpleFilter" data-settingname="HasTrailer">
|
||||||
<input type="checkbox" is="emby-checkbox" class="chkFeatureFilter" />
|
<input type="checkbox" is="emby-checkbox" class="chkFeatureFilter chkTrailer" />
|
||||||
<span>${Trailers}</span>
|
<span>${Trailers}</span>
|
||||||
</label>
|
</label>
|
||||||
<label class="viewSetting simpleFilter" data-settingname="HasSpecialFeature">
|
<label class="viewSetting simpleFilter" data-settingname="HasSpecialFeature">
|
||||||
<input type="checkbox" is="emby-checkbox" class="chkFeatureFilter" />
|
<input type="checkbox" is="emby-checkbox" class="chkFeatureFilter chkSpecialFeature" />
|
||||||
<span>${Extras}</span>
|
<span>${Extras}</span>
|
||||||
</label>
|
</label>
|
||||||
<label class="viewSetting simpleFilter" data-settingname="HasThemeSong">
|
<label class="viewSetting simpleFilter" data-settingname="HasThemeSong">
|
||||||
<input type="checkbox" is="emby-checkbox" class="chkFeatureFilter" />
|
<input type="checkbox" is="emby-checkbox" class="chkFeatureFilter chkThemeSong" />
|
||||||
<span>${ThemeSongs}</span>
|
<span>${ThemeSongs}</span>
|
||||||
</label>
|
</label>
|
||||||
<label class="viewSetting simpleFilter" data-settingname="HasThemeVideo">
|
<label class="viewSetting simpleFilter" data-settingname="HasThemeVideo">
|
||||||
<input type="checkbox" is="emby-checkbox" class="chkFeatureFilter" />
|
<input type="checkbox" is="emby-checkbox" class="chkFeatureFilter chkThemeVideo" />
|
||||||
<span>${ThemeVideos}</span>
|
<span>${ThemeVideos}</span>
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -18,8 +18,8 @@ function onSubmit(e) {
|
||||||
function initEditor(context, settings) {
|
function initEditor(context, settings) {
|
||||||
context.querySelector('form').addEventListener('submit', onSubmit);
|
context.querySelector('form').addEventListener('submit', onSubmit);
|
||||||
|
|
||||||
context.querySelector('.selectSortOrder').value = settings.sortOrder;
|
context.querySelector('.selectSortOrder').value = settings.SortOrder;
|
||||||
context.querySelector('.selectSortBy').value = settings.sortBy;
|
context.querySelector('.selectSortBy').value = settings.SortBy;
|
||||||
}
|
}
|
||||||
|
|
||||||
function centerFocus(elem, horiz, on) {
|
function centerFocus(elem, horiz, on) {
|
||||||
|
@ -37,10 +37,19 @@ function fillSortBy(context, options) {
|
||||||
}).join('');
|
}).join('');
|
||||||
}
|
}
|
||||||
|
|
||||||
function saveValues(context, settingsKey) {
|
function saveValues(context, settingsKey, setSortValues) {
|
||||||
|
if (setSortValues) {
|
||||||
|
setSortValues((prevState) => ({
|
||||||
|
...prevState,
|
||||||
|
StartIndex: 0,
|
||||||
|
SortBy: context.querySelector('.selectSortBy').value,
|
||||||
|
SortOrder: context.querySelector('.selectSortOrder').value
|
||||||
|
}));
|
||||||
|
} else {
|
||||||
userSettings.setFilter(settingsKey + '-sortorder', context.querySelector('.selectSortOrder').value);
|
userSettings.setFilter(settingsKey + '-sortorder', context.querySelector('.selectSortOrder').value);
|
||||||
userSettings.setFilter(settingsKey + '-sortby', context.querySelector('.selectSortBy').value);
|
userSettings.setFilter(settingsKey + '-sortby', context.querySelector('.selectSortBy').value);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
class SortMenu {
|
class SortMenu {
|
||||||
show(options) {
|
show(options) {
|
||||||
|
@ -95,7 +104,7 @@ class SortMenu {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (submitted) {
|
if (submitted) {
|
||||||
saveValues(dlg, options.settingsKey);
|
saveValues(dlg, options.settingsKey, options.setSortValues);
|
||||||
resolve();
|
resolve();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,7 +29,17 @@ function initEditor(context, settings) {
|
||||||
context.querySelector('.selectImageType').value = settings.imageType || 'primary';
|
context.querySelector('.selectImageType').value = settings.imageType || 'primary';
|
||||||
}
|
}
|
||||||
|
|
||||||
function saveValues(context, settings, settingsKey) {
|
function saveValues(context, settings, settingsKey, setviewsettings) {
|
||||||
|
if (setviewsettings) {
|
||||||
|
setviewsettings((prevState) => ({
|
||||||
|
...prevState,
|
||||||
|
StartIndex: 0,
|
||||||
|
imageType: context.querySelector('.selectImageType').value,
|
||||||
|
showTitle: context.querySelector('.chkShowTitle').checked || false,
|
||||||
|
showYear: context.querySelector('.chkShowYear').checked || false,
|
||||||
|
cardLayout: context.querySelector('.chkEnableCardLayout').checked || false
|
||||||
|
}));
|
||||||
|
} else {
|
||||||
const elems = context.querySelectorAll('.viewSetting-checkboxContainer');
|
const elems = context.querySelectorAll('.viewSetting-checkboxContainer');
|
||||||
for (const elem of elems) {
|
for (const elem of elems) {
|
||||||
userSettings.set(settingsKey + '-' + elem.getAttribute('data-settingname'), elem.querySelector('input').checked);
|
userSettings.set(settingsKey + '-' + elem.getAttribute('data-settingname'), elem.querySelector('input').checked);
|
||||||
|
@ -37,6 +47,7 @@ function saveValues(context, settings, settingsKey) {
|
||||||
|
|
||||||
userSettings.set(settingsKey + '-imageType', context.querySelector('.selectImageType').value);
|
userSettings.set(settingsKey + '-imageType', context.querySelector('.selectImageType').value);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function centerFocus(elem, horiz, on) {
|
function centerFocus(elem, horiz, on) {
|
||||||
import('../../scripts/scrollHelper').then((scrollHelper) => {
|
import('../../scripts/scrollHelper').then((scrollHelper) => {
|
||||||
|
@ -126,7 +137,7 @@ class ViewSettings {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (submitted) {
|
if (submitted) {
|
||||||
saveValues(dlg, options.settings, options.settingsKey);
|
saveValues(dlg, options.settings, options.settingsKey, options.setviewsettings);
|
||||||
return resolve();
|
return resolve();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -17,28 +17,28 @@
|
||||||
|
|
||||||
<div class="checkboxContainer viewSetting viewSetting-checkboxContainer hide chkTitleContainer" data-settingname="showTitle">
|
<div class="checkboxContainer viewSetting viewSetting-checkboxContainer hide chkTitleContainer" data-settingname="showTitle">
|
||||||
<label>
|
<label>
|
||||||
<input is="emby-checkbox" type="checkbox" />
|
<input is="emby-checkbox" type="checkbox" class="chkShowTitle" />
|
||||||
<span>${ShowTitle}</span>
|
<span>${ShowTitle}</span>
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="checkboxContainer viewSetting viewSetting-checkboxContainer hide chkYearContainer" data-settingname="showYear">
|
<div class="checkboxContainer viewSetting viewSetting-checkboxContainer hide chkYearContainer" data-settingname="showYear">
|
||||||
<label>
|
<label>
|
||||||
<input is="emby-checkbox" type="checkbox" />
|
<input is="emby-checkbox" type="checkbox" class="chkShowYear" />
|
||||||
<span>${ShowYear}</span>
|
<span>${ShowYear}</span>
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="checkboxContainer viewSetting viewSetting-checkboxContainer hide" data-settingname="groupBySeries">
|
<div class="checkboxContainer viewSetting viewSetting-checkboxContainer hide" data-settingname="groupBySeries">
|
||||||
<label>
|
<label>
|
||||||
<input is="emby-checkbox" type="checkbox" />
|
<input is="emby-checkbox" type="checkbox" class="chkGroupBySeries" />
|
||||||
<span>${GroupBySeries}</span>
|
<span>${GroupBySeries}</span>
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="checkboxContainer viewSetting viewSetting-checkboxContainer hide chkCardLayoutContainer" data-settingname="cardLayout">
|
<div class="checkboxContainer viewSetting viewSetting-checkboxContainer hide chkCardLayoutContainer" data-settingname="cardLayout">
|
||||||
<label>
|
<label>
|
||||||
<input is="emby-checkbox" type="checkbox" />
|
<input is="emby-checkbox" type="checkbox" class="chkEnableCardLayout" />
|
||||||
<span>${EnableCardLayout}</span>
|
<span>${EnableCardLayout}</span>
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -76,8 +76,8 @@ import LibraryMenu from '../scripts/libraryMenu';
|
||||||
const sortValues = instance.getSortValues();
|
const sortValues = instance.getSortValues();
|
||||||
|
|
||||||
if (!query.SortBy) {
|
if (!query.SortBy) {
|
||||||
query.SortBy = sortValues.sortBy;
|
query.SortBy = sortValues.SortBy;
|
||||||
query.SortOrder = sortValues.sortOrder;
|
query.SortOrder = sortValues.SortOrder;
|
||||||
}
|
}
|
||||||
|
|
||||||
query.Fields = query.Fields ? query.Fields + ',PrimaryImageAspectRatio' : 'PrimaryImageAspectRatio';
|
query.Fields = query.Fields ? query.Fields + ',PrimaryImageAspectRatio' : 'PrimaryImageAspectRatio';
|
||||||
|
@ -202,7 +202,7 @@ import LibraryMenu from '../scripts/libraryMenu';
|
||||||
const btnSortIcon = instance.btnSortIcon;
|
const btnSortIcon = instance.btnSortIcon;
|
||||||
|
|
||||||
if (btnSortIcon) {
|
if (btnSortIcon) {
|
||||||
setSortButtonIcon(btnSortIcon, values.sortOrder === 'Descending' ? 'arrow_downward' : 'arrow_upward');
|
setSortButtonIcon(btnSortIcon, values.SortOrder === 'Descending' ? 'arrow_downward' : 'arrow_upward');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -963,8 +963,8 @@ class ItemsView {
|
||||||
getSortValues() {
|
getSortValues() {
|
||||||
const basekey = this.getSettingsKey();
|
const basekey = this.getSettingsKey();
|
||||||
return {
|
return {
|
||||||
sortBy: userSettings.getFilter(basekey + '-sortby') || this.getDefaultSortBy(),
|
SortBy: userSettings.getFilter(basekey + '-sortby') || this.getDefaultSortBy(),
|
||||||
sortOrder: userSettings.getFilter(basekey + '-sortorder') === 'Descending' ? 'Descending' : 'Ascending'
|
SortOrder: userSettings.getFilter(basekey + '-sortorder') === 'Descending' ? 'Descending' : 'Ascending'
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,30 +1,32 @@
|
||||||
import React, { FC, 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 { QueryI } from './interface';
|
import { ViewSettingsI } from './interface';
|
||||||
|
|
||||||
interface AlphaPickerContainerProps {
|
interface AlphaPickerContainerProps {
|
||||||
getQuery: () => QueryI;
|
viewSettings: ViewSettingsI
|
||||||
setQuery: React.Dispatch<React.SetStateAction<QueryI>>;
|
setViewSettings: React.Dispatch<React.SetStateAction<ViewSettingsI>>;
|
||||||
}
|
}
|
||||||
|
|
||||||
const AlphaPickerContainer: FC<AlphaPickerContainerProps> = ({ getQuery, setQuery }) => {
|
const AlphaPickerContainer: FC<AlphaPickerContainerProps> = ({ viewSettings, setViewSettings }) => {
|
||||||
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(viewSettings);
|
||||||
|
|
||||||
const onAlphaPickerChange = useCallback((e) => {
|
const onAlphaPickerChange = useCallback((e) => {
|
||||||
const newValue = (e as CustomEvent).detail.value;
|
const newValue = (e as CustomEvent).detail.value;
|
||||||
let updatedValue;
|
let updatedValue: React.SetStateAction<ViewSettingsI>;
|
||||||
if (newValue === '#') {
|
if (newValue === '#') {
|
||||||
updatedValue = {NameLessThan: 'A'};
|
updatedValue = {NameLessThan: 'A'};
|
||||||
} else {
|
} else {
|
||||||
updatedValue = {NameStartsWith: newValue};
|
updatedValue = {NameStartsWith: newValue};
|
||||||
}
|
}
|
||||||
|
setViewSettings((prevState) => ({
|
||||||
setQuery({StartIndex: 0, ...updatedValue});
|
...prevState,
|
||||||
}, [setQuery]);
|
StartIndex: 0,
|
||||||
|
...updatedValue
|
||||||
|
}));
|
||||||
|
}, [setViewSettings]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const alphaPickerElement = element.current;
|
const alphaPickerElement = element.current;
|
||||||
|
|
|
@ -1,27 +1,23 @@
|
||||||
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 { FiltersI, QueryI } from './interface';
|
import { ViewSettingsI } from './interface';
|
||||||
|
|
||||||
interface FilterProps {
|
interface FilterProps {
|
||||||
topParentId?: string | null;
|
topParentId?: string | null;
|
||||||
getItemTypes: () => string[];
|
getItemTypes: () => string[];
|
||||||
getFilters: () => FiltersI;
|
|
||||||
getSettingsKey: () => string;
|
|
||||||
getFilterMenuOptions: () => Record<string, never>;
|
getFilterMenuOptions: () => Record<string, never>;
|
||||||
getVisibleFilters: () => string[];
|
getVisibleFilters: () => string[];
|
||||||
setQuery: React.Dispatch<React.SetStateAction<QueryI>>;
|
viewSettings: ViewSettingsI;
|
||||||
reloadItems: () => void;
|
setViewSettings: React.Dispatch<React.SetStateAction<ViewSettingsI>>;
|
||||||
}
|
}
|
||||||
|
|
||||||
const Filter: FC<FilterProps> = ({
|
const Filter: FC<FilterProps> = ({
|
||||||
topParentId,
|
topParentId,
|
||||||
getItemTypes,
|
getItemTypes,
|
||||||
getSettingsKey,
|
|
||||||
getFilters,
|
|
||||||
getVisibleFilters,
|
getVisibleFilters,
|
||||||
getFilterMenuOptions,
|
getFilterMenuOptions,
|
||||||
setQuery,
|
viewSettings,
|
||||||
reloadItems
|
setViewSettings
|
||||||
}) => {
|
}) => {
|
||||||
const element = useRef<HTMLDivElement>(null);
|
const element = useRef<HTMLDivElement>(null);
|
||||||
|
|
||||||
|
@ -29,19 +25,16 @@ const Filter: FC<FilterProps> = ({
|
||||||
import('../../components/filtermenu/filtermenu').then(({default: FilterMenu}) => {
|
import('../../components/filtermenu/filtermenu').then(({default: FilterMenu}) => {
|
||||||
const filterMenu = new FilterMenu();
|
const filterMenu = new FilterMenu();
|
||||||
filterMenu.show({
|
filterMenu.show({
|
||||||
settingsKey: getSettingsKey(),
|
settings: viewSettings,
|
||||||
settings: getFilters(),
|
|
||||||
visibleSettings: getVisibleFilters(),
|
visibleSettings: getVisibleFilters(),
|
||||||
parentId: topParentId,
|
parentId: topParentId,
|
||||||
itemTypes: getItemTypes(),
|
itemTypes: getItemTypes(),
|
||||||
serverId: window.ApiClient.serverId(),
|
serverId: window.ApiClient.serverId(),
|
||||||
filterMenuOptions: getFilterMenuOptions()
|
filterMenuOptions: getFilterMenuOptions(),
|
||||||
}).then(() => {
|
setfilters: setViewSettings
|
||||||
setQuery({StartIndex: 0});
|
|
||||||
reloadItems();
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}, [getSettingsKey, getFilters, getVisibleFilters, topParentId, getItemTypes, getFilterMenuOptions, setQuery, reloadItems]);
|
}, [viewSettings, getVisibleFilters, topParentId, getItemTypes, getFilterMenuOptions, setViewSettings]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const btnFilter = element.current?.querySelector('.btnFilter');
|
const btnFilter = element.current?.querySelector('.btnFilter');
|
||||||
|
|
|
@ -1,95 +1,17 @@
|
||||||
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 listview from '../../components/listview/listview';
|
|
||||||
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 { CardOptionsI } from './interface';
|
import { ViewSettingsI } from './interface';
|
||||||
|
|
||||||
interface ItemsContainerI {
|
interface ItemsContainerI {
|
||||||
getViewSettings: () => {
|
viewSettings: ViewSettingsI;
|
||||||
showTitle: string | boolean;
|
getItemsHtml: () => string
|
||||||
cardLayout: string | boolean;
|
|
||||||
showYear: string | boolean;
|
|
||||||
imageType: string;
|
|
||||||
viewType: string;
|
|
||||||
};
|
|
||||||
getContext: () => string | null;
|
|
||||||
items?: BaseItemDto[] | null;
|
|
||||||
noItemsMessage?: string;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const ItemsContainer: FC<ItemsContainerI> = ({ getViewSettings, getContext, items = [], noItemsMessage }) => {
|
const ItemsContainer: FC<ItemsContainerI> = ({ viewSettings, getItemsHtml }) => {
|
||||||
const element = useRef<HTMLDivElement>(null);
|
const element = useRef<HTMLDivElement>(null);
|
||||||
const viewsettings = getViewSettings();
|
|
||||||
|
|
||||||
const getCardOptions = useCallback(() => {
|
|
||||||
let shape;
|
|
||||||
let preferThumb;
|
|
||||||
let preferDisc;
|
|
||||||
let preferLogo;
|
|
||||||
|
|
||||||
if (viewsettings.imageType === 'banner') {
|
|
||||||
shape = 'banner';
|
|
||||||
} else if (viewsettings.imageType === 'disc') {
|
|
||||||
shape = 'square';
|
|
||||||
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.showYear,
|
|
||||||
cardLayout: viewsettings.cardLayout,
|
|
||||||
centerText: true,
|
|
||||||
context: getContext(),
|
|
||||||
coverImage: true,
|
|
||||||
preferThumb: preferThumb,
|
|
||||||
preferDisc: preferDisc,
|
|
||||||
preferLogo: preferLogo,
|
|
||||||
overlayPlayButton: false,
|
|
||||||
overlayMoreButton: true,
|
|
||||||
overlayText: !viewsettings.showTitle
|
|
||||||
};
|
|
||||||
|
|
||||||
cardOptions.items = items;
|
|
||||||
|
|
||||||
return cardOptions;
|
|
||||||
}, [getContext, items, viewsettings.cardLayout, viewsettings.imageType, viewsettings.showTitle, viewsettings.showYear]);
|
|
||||||
|
|
||||||
const getItemsHtml = useCallback(() => {
|
|
||||||
const settings = getViewSettings();
|
|
||||||
|
|
||||||
let html = '';
|
|
||||||
|
|
||||||
if (settings.imageType === 'list') {
|
|
||||||
html = listview.getListViewHtml({
|
|
||||||
items: items,
|
|
||||||
context: getContext()});
|
|
||||||
} else {
|
|
||||||
html = cardBuilder.getCardsHtml(items, getCardOptions());
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!items?.length) {
|
|
||||||
html += '<div class="noItemsMessage centerMessage">';
|
|
||||||
html += '<h1>' + globalize.translate('MessageNothingHere') + '</h1>';
|
|
||||||
html += '<p>' + globalize.translate(noItemsMessage) + '</p>';
|
|
||||||
html += '</div>';
|
|
||||||
}
|
|
||||||
|
|
||||||
return html;
|
|
||||||
}, [getCardOptions, getContext, getViewSettings, items, noItemsMessage]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const itemsContainer = element.current?.querySelector('.itemsContainer') as HTMLDivElement;
|
const itemsContainer = element.current?.querySelector('.itemsContainer') as HTMLDivElement;
|
||||||
|
@ -97,7 +19,7 @@ const ItemsContainer: FC<ItemsContainerI> = ({ getViewSettings, getContext, item
|
||||||
imageLoader.lazyChildren(itemsContainer);
|
imageLoader.lazyChildren(itemsContainer);
|
||||||
}, [getItemsHtml]);
|
}, [getItemsHtml]);
|
||||||
|
|
||||||
const cssClass = viewsettings.imageType == 'List' ? 'vertical-list' : 'vertical-wrap';
|
const cssClass = viewSettings.imageType == 'list' ? 'vertical-list' : 'vertical-wrap';
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div ref={element}>
|
<div ref={element}>
|
||||||
|
|
|
@ -3,18 +3,18 @@ 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 * as userSettings from '../../scripts/settings/userSettings';
|
import * as userSettings from '../../scripts/settings/userSettings';
|
||||||
import { QueryI } from './interface';
|
import { ViewSettingsI } from './interface';
|
||||||
|
|
||||||
interface PaginationProps {
|
interface PaginationProps {
|
||||||
query: QueryI;
|
viewSettings: ViewSettingsI;
|
||||||
setQuery: React.Dispatch<React.SetStateAction<QueryI>>;
|
setViewSettings: React.Dispatch<React.SetStateAction<ViewSettingsI>>;
|
||||||
itemsResult?: BaseItemDtoQueryResult;
|
itemsResult?: BaseItemDtoQueryResult;
|
||||||
}
|
}
|
||||||
|
|
||||||
const Pagination: FC<PaginationProps> = ({ query, setQuery, itemsResult = {} }) => {
|
const Pagination: FC<PaginationProps> = ({ viewSettings, setViewSettings, itemsResult = {} }) => {
|
||||||
const limit = userSettings.libraryPageSize(undefined);
|
const limit = userSettings.libraryPageSize(undefined);
|
||||||
const totalRecordCount = itemsResult.TotalRecordCount || 0;
|
const totalRecordCount = itemsResult.TotalRecordCount || 0;
|
||||||
const startIndex = query.StartIndex || 0;
|
const startIndex = viewSettings.StartIndex || 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);
|
||||||
|
@ -22,16 +22,22 @@ const Pagination: FC<PaginationProps> = ({ query, setQuery, itemsResult = {} })
|
||||||
const onNextPageClick = useCallback(() => {
|
const onNextPageClick = useCallback(() => {
|
||||||
if (limit > 0) {
|
if (limit > 0) {
|
||||||
const newIndex = startIndex + limit;
|
const newIndex = startIndex + limit;
|
||||||
setQuery({StartIndex: newIndex});
|
setViewSettings((prevState) => ({
|
||||||
|
...prevState,
|
||||||
|
StartIndex: newIndex
|
||||||
|
}));
|
||||||
}
|
}
|
||||||
}, [limit, setQuery, startIndex]);
|
}, [limit, setViewSettings, startIndex]);
|
||||||
|
|
||||||
const onPreviousPageClick = useCallback(() => {
|
const onPreviousPageClick = useCallback(() => {
|
||||||
if (limit > 0) {
|
if (limit > 0) {
|
||||||
const newIndex = Math.max(0, startIndex - limit);
|
const newIndex = Math.max(0, startIndex - limit);
|
||||||
setQuery({StartIndex: newIndex});
|
setViewSettings((prevState) => ({
|
||||||
|
...prevState,
|
||||||
|
StartIndex: newIndex
|
||||||
|
}));
|
||||||
}
|
}
|
||||||
}, [limit, setQuery, startIndex]);
|
}, [limit, setViewSettings, startIndex]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const btnNextPage = element.current?.querySelector('.btnNextPage') as HTMLButtonElement;
|
const btnNextPage = element.current?.querySelector('.btnNextPage') as HTMLButtonElement;
|
||||||
|
|
|
@ -1,37 +1,30 @@
|
||||||
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 { QueryI } from './interface';
|
import { ViewSettingsI } from './interface';
|
||||||
|
|
||||||
interface SelectViewProps {
|
interface SelectViewProps {
|
||||||
getSettingsKey: () => string;
|
|
||||||
getVisibleViewSettings: () => string[];
|
getVisibleViewSettings: () => string[];
|
||||||
getViewSettings: () => {
|
viewSettings: ViewSettingsI
|
||||||
showTitle: string | boolean;
|
setViewSettings: React.Dispatch<React.SetStateAction<ViewSettingsI>>;
|
||||||
cardLayout: string | boolean;
|
|
||||||
showYear: string | boolean;
|
|
||||||
imageType: string;
|
|
||||||
viewType: string;
|
|
||||||
};
|
|
||||||
setQuery: React.Dispatch<React.SetStateAction<QueryI>>;
|
|
||||||
reloadItems: () => void;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const SelectView: FC<SelectViewProps> = ({ setQuery, getSettingsKey, getVisibleViewSettings, getViewSettings, reloadItems }) => {
|
const SelectView: FC<SelectViewProps> = ({
|
||||||
|
getVisibleViewSettings,
|
||||||
|
viewSettings,
|
||||||
|
setViewSettings
|
||||||
|
}) => {
|
||||||
const element = useRef<HTMLDivElement>(null);
|
const element = useRef<HTMLDivElement>(null);
|
||||||
|
|
||||||
const showViewSettingsMenu = useCallback(() => {
|
const showViewSettingsMenu = useCallback(() => {
|
||||||
import('../../components/viewSettings/viewSettings').then(({default: ViewSettings}) => {
|
import('../../components/viewSettings/viewSettings').then(({default: ViewSettings}) => {
|
||||||
const viewSettings = new ViewSettings();
|
const viewsettings = new ViewSettings();
|
||||||
viewSettings.show({
|
viewsettings.show({
|
||||||
settingsKey: getSettingsKey(),
|
settings: viewSettings,
|
||||||
settings: getViewSettings(),
|
visibleSettings: getVisibleViewSettings(),
|
||||||
visibleSettings: getVisibleViewSettings()
|
setviewsettings: setViewSettings
|
||||||
}).then(() => {
|
|
||||||
setQuery({StartIndex: 0});
|
|
||||||
reloadItems();
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}, [getSettingsKey, getViewSettings, getVisibleViewSettings, reloadItems, setQuery]);
|
}, [getVisibleViewSettings, viewSettings, setViewSettings]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const btnSelectView = element.current?.querySelector('.btnSelectView') as HTMLButtonElement;
|
const btnSelectView = element.current?.querySelector('.btnSelectView') as HTMLButtonElement;
|
||||||
|
|
|
@ -1,37 +1,34 @@
|
||||||
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 { QueryI } from './interface';
|
import { ViewSettingsI } from './interface';
|
||||||
|
|
||||||
interface SortProps {
|
interface SortProps {
|
||||||
getSortMenuOptions: () => {
|
getSortMenuOptions: () => {
|
||||||
name: string;
|
name: string;
|
||||||
value: string;
|
value: string;
|
||||||
}[]
|
}[];
|
||||||
getSortValues: () => {
|
viewSettings: ViewSettingsI
|
||||||
sortBy: string;
|
setViewSettings: React.Dispatch<React.SetStateAction<ViewSettingsI>>;
|
||||||
sortOrder: string;
|
|
||||||
}
|
|
||||||
getSettingsKey: () => string;
|
|
||||||
setQuery: React.Dispatch<React.SetStateAction<QueryI>>;
|
|
||||||
reloadItems: () => void;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const Sort: FC<SortProps> = ({ getSortMenuOptions, getSortValues, getSettingsKey, setQuery, reloadItems }) => {
|
const Sort: FC<SortProps> = ({
|
||||||
|
getSortMenuOptions,
|
||||||
|
viewSettings,
|
||||||
|
//setSortValues,
|
||||||
|
setViewSettings
|
||||||
|
}) => {
|
||||||
const element = useRef<HTMLDivElement>(null);
|
const element = useRef<HTMLDivElement>(null);
|
||||||
|
|
||||||
const showSortMenu = useCallback(() => {
|
const showSortMenu = useCallback(() => {
|
||||||
import('../../components/sortmenu/sortmenu').then(({default: SortMenu}) => {
|
import('../../components/sortmenu/sortmenu').then(({default: SortMenu}) => {
|
||||||
const sortMenu = new SortMenu();
|
const sortMenu = new SortMenu();
|
||||||
sortMenu.show({
|
sortMenu.show({
|
||||||
settingsKey: getSettingsKey(),
|
settings: viewSettings,
|
||||||
settings: getSortValues(),
|
sortOptions: getSortMenuOptions(),
|
||||||
sortOptions: getSortMenuOptions()
|
setSortValues: setViewSettings
|
||||||
}).then(() => {
|
|
||||||
setQuery({StartIndex: 0});
|
|
||||||
reloadItems();
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}, [getSettingsKey, getSortMenuOptions, getSortValues, reloadItems, setQuery]);
|
}, [getSortMenuOptions, viewSettings, setViewSettings]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const btnSort = element.current?.querySelector('.btnSort');
|
const btnSort = element.current?.querySelector('.btnSort');
|
||||||
|
|
|
@ -12,7 +12,11 @@ import Shuffle from './Shuffle';
|
||||||
import Sort from './Sort';
|
import Sort from './Sort';
|
||||||
import NewCollection from './NewCollection';
|
import NewCollection from './NewCollection';
|
||||||
import globalize from '../../scripts/globalize';
|
import globalize from '../../scripts/globalize';
|
||||||
import { QueryI } from './interface';
|
import { CardOptionsI, QueryI, ViewSettingsI } from './interface';
|
||||||
|
import ServerConnections from '../../components/ServerConnections';
|
||||||
|
import { useLocalStorage } from '../hook/useLocalStorage';
|
||||||
|
import listview from '../../components/listview/listview';
|
||||||
|
import cardBuilder from '../../components/cardbuilder/cardBuilder';
|
||||||
|
|
||||||
interface ViewItemsContainerProps {
|
interface ViewItemsContainerProps {
|
||||||
topParentId: string | null;
|
topParentId: string | null;
|
||||||
|
@ -25,87 +29,24 @@ interface ViewItemsContainerProps {
|
||||||
getNoItemsMessage: () => string;
|
getNoItemsMessage: () => string;
|
||||||
}
|
}
|
||||||
|
|
||||||
const ViewItemsContainer: FC<ViewItemsContainerProps> = ({
|
const getDefaultSortBy = () => {
|
||||||
topParentId,
|
return 'SortName';
|
||||||
isBtnShuffleEnabled = false,
|
};
|
||||||
isBtnFilterEnabled = true,
|
|
||||||
isBtnNewCollectionEnabled = false,
|
|
||||||
isAlphaPickerEnabled = true,
|
|
||||||
getBasekey,
|
|
||||||
getItemTypes,
|
|
||||||
getNoItemsMessage
|
|
||||||
}) => {
|
|
||||||
const [ itemsResult, setItemsResult ] = useState<BaseItemDtoQueryResult>({});
|
|
||||||
const [ query, setQuery ] = useState<QueryI>({
|
|
||||||
StartIndex: 0
|
|
||||||
});
|
|
||||||
|
|
||||||
const element = useRef<HTMLDivElement>(null);
|
const getVisibleViewSettings = () => {
|
||||||
|
|
||||||
const getSettingsKey = useCallback(() => {
|
|
||||||
return `${topParentId} - ${getBasekey()}`;
|
|
||||||
}, [getBasekey, topParentId]);
|
|
||||||
|
|
||||||
const getVisibleViewSettings = useCallback(() => {
|
|
||||||
return [
|
return [
|
||||||
'showTitle',
|
'showTitle',
|
||||||
'showYear',
|
'showYear',
|
||||||
'imageType',
|
'imageType',
|
||||||
'cardLayout'
|
'cardLayout'
|
||||||
];
|
];
|
||||||
}, []);
|
|
||||||
|
|
||||||
const getViewSettings = useCallback(() => {
|
|
||||||
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]);
|
|
||||||
|
|
||||||
const getDefaultSortBy = useCallback(() => {
|
const getFilterMenuOptions = () => {
|
||||||
return 'SortName';
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
const getSortValues = useCallback(() => {
|
|
||||||
const basekey = getSettingsKey();
|
|
||||||
|
|
||||||
return {
|
|
||||||
sortBy: userSettings.getFilter(basekey + '-sortby') || getDefaultSortBy(),
|
|
||||||
sortOrder: userSettings.getFilter(basekey + '-sortorder') === 'Descending' ? 'Descending' : 'Ascending'
|
|
||||||
};
|
|
||||||
}, [getDefaultSortBy, getSettingsKey]);
|
|
||||||
|
|
||||||
const getFilters = useCallback(() => {
|
|
||||||
const basekey = getSettingsKey();
|
|
||||||
return {
|
|
||||||
IsPlayed: userSettings.getFilter(basekey + '-filter-IsPlayed') === 'true',
|
|
||||||
IsUnplayed: userSettings.getFilter(basekey + '-filter-IsUnplayed') === 'true',
|
|
||||||
IsFavorite: userSettings.getFilter(basekey + '-filter-IsFavorite') === 'true',
|
|
||||||
IsResumable: userSettings.getFilter(basekey + '-filter-IsResumable') === 'true',
|
|
||||||
Is4K: userSettings.getFilter(basekey + '-filter-Is4K') === 'true',
|
|
||||||
IsHD: userSettings.getFilter(basekey + '-filter-IsHD') === 'true',
|
|
||||||
IsSD: userSettings.getFilter(basekey + '-filter-IsSD') === 'true',
|
|
||||||
Is3D: userSettings.getFilter(basekey + '-filter-Is3D') === 'true',
|
|
||||||
VideoTypes: userSettings.getFilter(basekey + '-filter-VideoTypes'),
|
|
||||||
SeriesStatus: userSettings.getFilter(basekey + '-filter-SeriesStatus'),
|
|
||||||
HasSubtitles: userSettings.getFilter(basekey + '-filter-HasSubtitles'),
|
|
||||||
HasTrailer: userSettings.getFilter(basekey + '-filter-HasTrailer'),
|
|
||||||
HasSpecialFeature: userSettings.getFilter(basekey + '-filter-HasSpecialFeature'),
|
|
||||||
HasThemeSong: userSettings.getFilter(basekey + '-filter-HasThemeSong'),
|
|
||||||
HasThemeVideo: userSettings.getFilter(basekey + '-filter-HasThemeVideo'),
|
|
||||||
GenreIds: userSettings.getFilter(basekey + '-filter-GenreIds')
|
|
||||||
};
|
|
||||||
}, [getSettingsKey]);
|
|
||||||
|
|
||||||
const getFilterMenuOptions = useCallback(() => {
|
|
||||||
return {};
|
return {};
|
||||||
}, []);
|
};
|
||||||
|
|
||||||
const getVisibleFilters = useCallback(() => {
|
const getVisibleFilters = () => {
|
||||||
return [
|
return [
|
||||||
'IsUnplayed',
|
'IsUnplayed',
|
||||||
'IsPlayed',
|
'IsPlayed',
|
||||||
|
@ -118,132 +59,9 @@ const ViewItemsContainer: FC<ViewItemsContainerProps> = ({
|
||||||
'HasThemeSong',
|
'HasThemeSong',
|
||||||
'HasThemeVideo'
|
'HasThemeVideo'
|
||||||
];
|
];
|
||||||
}, []);
|
|
||||||
|
|
||||||
const getQuery = useCallback(() => {
|
|
||||||
let fields = 'BasicSyncInfo,MediaSourceCount';
|
|
||||||
const viewsettings = getViewSettings();
|
|
||||||
if (viewsettings.imageType === 'primary') {
|
|
||||||
fields += ',PrimaryImageAspectRatio';
|
|
||||||
}
|
|
||||||
|
|
||||||
if (viewsettings.showYear) {
|
|
||||||
fields += ',ProductionYear';
|
|
||||||
}
|
|
||||||
|
|
||||||
const options: QueryI = {
|
|
||||||
SortBy: getSortValues().sortBy,
|
|
||||||
SortOrder: getSortValues().sortOrder,
|
|
||||||
IncludeItemTypes: getItemTypes().join(','),
|
|
||||||
Recursive: true,
|
|
||||||
Fields: fields,
|
|
||||||
ImageTypeLimit: 1,
|
|
||||||
EnableImageTypes: 'Primary,Backdrop,Banner,Thumb,Disc,Logo',
|
|
||||||
Limit: userSettings.libraryPageSize(undefined),
|
|
||||||
StartIndex: query.StartIndex,
|
|
||||||
NameLessThan: query.NameLessThan,
|
|
||||||
NameStartsWith: query.NameStartsWith,
|
|
||||||
ParentId: topParentId
|
|
||||||
};
|
};
|
||||||
|
|
||||||
if (getBasekey() === 'favorites') {
|
const getSortMenuOptions = () => {
|
||||||
options.IsFavorite = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return options;
|
|
||||||
}, [getViewSettings, getSortValues, getItemTypes, query.StartIndex, query.NameLessThan, query.NameStartsWith, topParentId, getBasekey]);
|
|
||||||
|
|
||||||
const getQueryWithFilters = useCallback(() => {
|
|
||||||
const query = getQuery();
|
|
||||||
const queryFilters = [];
|
|
||||||
let hasFilters;
|
|
||||||
|
|
||||||
const filters = getFilters();
|
|
||||||
|
|
||||||
if (filters.IsPlayed) {
|
|
||||||
queryFilters.push('IsPlayed');
|
|
||||||
hasFilters = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (filters.IsUnplayed) {
|
|
||||||
queryFilters.push('IsUnplayed');
|
|
||||||
hasFilters = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (filters.IsFavorite) {
|
|
||||||
queryFilters.push('IsFavorite');
|
|
||||||
hasFilters = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (filters.IsResumable) {
|
|
||||||
queryFilters.push('IsResumable');
|
|
||||||
hasFilters = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (filters.VideoTypes) {
|
|
||||||
hasFilters = true;
|
|
||||||
query.VideoTypes = filters.VideoTypes;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (filters.GenreIds) {
|
|
||||||
hasFilters = true;
|
|
||||||
query.GenreIds = filters.GenreIds;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (filters.Is4K) {
|
|
||||||
query.Is4K = true;
|
|
||||||
hasFilters = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (filters.IsHD) {
|
|
||||||
query.IsHD = true;
|
|
||||||
hasFilters = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (filters.IsSD) {
|
|
||||||
query.IsHD = false;
|
|
||||||
hasFilters = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (filters.Is3D) {
|
|
||||||
query.Is3D = true;
|
|
||||||
hasFilters = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (filters.HasSubtitles) {
|
|
||||||
query.HasSubtitles = true;
|
|
||||||
hasFilters = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (filters.HasTrailer) {
|
|
||||||
query.HasTrailer = true;
|
|
||||||
hasFilters = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (filters.HasSpecialFeature) {
|
|
||||||
query.HasSpecialFeature = true;
|
|
||||||
hasFilters = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (filters.HasThemeSong) {
|
|
||||||
query.HasThemeSong = true;
|
|
||||||
hasFilters = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (filters.HasThemeVideo) {
|
|
||||||
query.HasThemeVideo = true;
|
|
||||||
hasFilters = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
query.Filters = queryFilters.length ? queryFilters.join(',') : null;
|
|
||||||
|
|
||||||
return {
|
|
||||||
query: query,
|
|
||||||
hasFilters: hasFilters
|
|
||||||
};
|
|
||||||
}, [getQuery, getFilters]);
|
|
||||||
|
|
||||||
const getSortMenuOptions = useCallback(() => {
|
|
||||||
return [{
|
return [{
|
||||||
name: globalize.translate('Name'),
|
name: globalize.translate('Name'),
|
||||||
value: 'SortName,ProductionYear'
|
value: 'SortName,ProductionYear'
|
||||||
|
@ -275,7 +93,59 @@ const ViewItemsContainer: FC<ViewItemsContainerProps> = ({
|
||||||
name: globalize.translate('Runtime'),
|
name: globalize.translate('Runtime'),
|
||||||
value: 'Runtime,SortName,ProductionYear'
|
value: 'Runtime,SortName,ProductionYear'
|
||||||
}];
|
}];
|
||||||
}, []);
|
};
|
||||||
|
|
||||||
|
const defaultViewSettingsValue: ViewSettingsI = {
|
||||||
|
showTitle: true,
|
||||||
|
showYear: true,
|
||||||
|
imageType: 'primary',
|
||||||
|
viewType: '',
|
||||||
|
cardLayout: true,
|
||||||
|
SortBy: getDefaultSortBy(),
|
||||||
|
SortOrder: 'Ascending',
|
||||||
|
IsPlayed: false,
|
||||||
|
IsUnplayed: false,
|
||||||
|
IsFavorite: false,
|
||||||
|
IsResumable: false,
|
||||||
|
Is4K: null,
|
||||||
|
IsHD: null,
|
||||||
|
IsSD: null,
|
||||||
|
Is3D: null,
|
||||||
|
VideoTypes: '',
|
||||||
|
SeriesStatus: '',
|
||||||
|
HasSubtitles: null,
|
||||||
|
HasTrailer: null,
|
||||||
|
HasSpecialFeature: null,
|
||||||
|
HasThemeSong: null,
|
||||||
|
HasThemeVideo: null,
|
||||||
|
GenreIds: '',
|
||||||
|
StartIndex: 0
|
||||||
|
};
|
||||||
|
|
||||||
|
const ViewItemsContainer: FC<ViewItemsContainerProps> = ({
|
||||||
|
topParentId,
|
||||||
|
isBtnShuffleEnabled = false,
|
||||||
|
isBtnFilterEnabled = true,
|
||||||
|
isBtnNewCollectionEnabled = false,
|
||||||
|
isAlphaPickerEnabled = true,
|
||||||
|
getBasekey,
|
||||||
|
getItemTypes,
|
||||||
|
getNoItemsMessage
|
||||||
|
}) => {
|
||||||
|
const getSettingsKey = useCallback(() => {
|
||||||
|
return `${topParentId} - ${getBasekey()}`;
|
||||||
|
}, [getBasekey, topParentId]);
|
||||||
|
|
||||||
|
const [isLoading, setisLoading] = useState(false);
|
||||||
|
|
||||||
|
const [viewSettings, setViewSettings] = useLocalStorage<ViewSettingsI>(
|
||||||
|
getSettingsKey() + 'viewSettings',
|
||||||
|
defaultViewSettingsValue
|
||||||
|
);
|
||||||
|
|
||||||
|
const [ itemsResult, setItemsResult ] = useState<BaseItemDtoQueryResult>({});
|
||||||
|
|
||||||
|
const element = useRef<HTMLDivElement>(null);
|
||||||
|
|
||||||
const getContext = useCallback(() => {
|
const getContext = useCallback(() => {
|
||||||
const itemType = getItemTypes().join(',');
|
const itemType = getItemTypes().join(',');
|
||||||
|
@ -286,6 +156,151 @@ const ViewItemsContainer: FC<ViewItemsContainerProps> = ({
|
||||||
return null;
|
return null;
|
||||||
}, [getItemTypes]);
|
}, [getItemTypes]);
|
||||||
|
|
||||||
|
const getCardOptions = useCallback(() => {
|
||||||
|
let shape;
|
||||||
|
let preferThumb;
|
||||||
|
let preferDisc;
|
||||||
|
let preferLogo;
|
||||||
|
|
||||||
|
if (viewSettings.imageType === 'banner') {
|
||||||
|
shape = 'banner';
|
||||||
|
} else if (viewSettings.imageType === 'disc') {
|
||||||
|
shape = 'square';
|
||||||
|
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.showYear,
|
||||||
|
cardLayout: viewSettings.cardLayout,
|
||||||
|
centerText: true,
|
||||||
|
context: getContext(),
|
||||||
|
coverImage: true,
|
||||||
|
preferThumb: preferThumb,
|
||||||
|
preferDisc: preferDisc,
|
||||||
|
preferLogo: preferLogo,
|
||||||
|
overlayPlayButton: false,
|
||||||
|
overlayMoreButton: true,
|
||||||
|
overlayText: !viewSettings.showTitle
|
||||||
|
};
|
||||||
|
|
||||||
|
cardOptions.items = itemsResult.Items || [];
|
||||||
|
console.log('cardOptions', cardOptions);
|
||||||
|
return cardOptions;
|
||||||
|
}, [getContext, itemsResult.Items, viewSettings.cardLayout, viewSettings.imageType, viewSettings.showTitle, viewSettings.showYear]);
|
||||||
|
|
||||||
|
const getItemsHtml = useCallback(() => {
|
||||||
|
let html = '';
|
||||||
|
|
||||||
|
if (viewSettings.imageType === 'list') {
|
||||||
|
html = listview.getListViewHtml({
|
||||||
|
items: itemsResult.Items || [],
|
||||||
|
context: getContext()
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
html = cardBuilder.getCardsHtml(itemsResult.Items || [], getCardOptions());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!itemsResult.Items?.length) {
|
||||||
|
html += '<div class="noItemsMessage centerMessage">';
|
||||||
|
html += '<h1>' + globalize.translate('MessageNothingHere') + '</h1>';
|
||||||
|
html += '<p>' + globalize.translate(getNoItemsMessage()) + '</p>';
|
||||||
|
html += '</div>';
|
||||||
|
}
|
||||||
|
|
||||||
|
return html;
|
||||||
|
}, [getCardOptions, getContext, itemsResult.Items, getNoItemsMessage, viewSettings.imageType]);
|
||||||
|
|
||||||
|
const getQuery = useCallback(() => {
|
||||||
|
let fields = 'BasicSyncInfo,MediaSourceCount';
|
||||||
|
|
||||||
|
if (viewSettings.imageType === 'primary') {
|
||||||
|
fields += ',PrimaryImageAspectRatio';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (viewSettings.showYear) {
|
||||||
|
fields += ',ProductionYear';
|
||||||
|
}
|
||||||
|
|
||||||
|
const queryFilters: string[] = [];
|
||||||
|
|
||||||
|
if (viewSettings.IsPlayed) {
|
||||||
|
queryFilters.push('IsPlayed');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (viewSettings.IsUnplayed) {
|
||||||
|
queryFilters.push('IsUnplayed');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (viewSettings.IsFavorite) {
|
||||||
|
queryFilters.push('IsFavorite');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (viewSettings.IsResumable) {
|
||||||
|
queryFilters.push('IsResumable');
|
||||||
|
}
|
||||||
|
|
||||||
|
let queryIsHD;
|
||||||
|
|
||||||
|
if (viewSettings.IsHD) {
|
||||||
|
queryIsHD = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (viewSettings.IsSD) {
|
||||||
|
queryIsHD = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const options: QueryI = {
|
||||||
|
SortBy: viewSettings.SortBy,
|
||||||
|
SortOrder: viewSettings.SortOrder,
|
||||||
|
IncludeItemTypes: getItemTypes().join(','),
|
||||||
|
Recursive: true,
|
||||||
|
Fields: fields,
|
||||||
|
ImageTypeLimit: 1,
|
||||||
|
EnableImageTypes: 'Primary,Backdrop,Banner,Thumb,Disc,Logo',
|
||||||
|
Limit: userSettings.libraryPageSize(undefined),
|
||||||
|
IsFavorite: getBasekey() === 'favorites' ? true : null,
|
||||||
|
VideoTypes: viewSettings.VideoTypes,
|
||||||
|
GenreIds: viewSettings.GenreIds,
|
||||||
|
Is4K: viewSettings.Is4K ? true : null,
|
||||||
|
IsHD: queryIsHD,
|
||||||
|
Is3D: viewSettings.Is3D ? true : null,
|
||||||
|
HasSubtitles: viewSettings.HasSubtitles ? true : null,
|
||||||
|
HasTrailer: viewSettings.HasTrailer ? true : null,
|
||||||
|
HasSpecialFeature: viewSettings.HasSpecialFeature ? true : null,
|
||||||
|
HasThemeSong: viewSettings.HasThemeSong ? true : null,
|
||||||
|
HasThemeVideo: viewSettings.HasThemeVideo ? true : null,
|
||||||
|
Filters: queryFilters.length ? queryFilters.join(',') : null,
|
||||||
|
StartIndex: viewSettings.StartIndex,
|
||||||
|
NameLessThan: viewSettings.NameLessThan,
|
||||||
|
NameStartsWith: viewSettings.NameStartsWith,
|
||||||
|
ParentId: topParentId
|
||||||
|
};
|
||||||
|
console.log('options', options);
|
||||||
|
return options;
|
||||||
|
}, [viewSettings.imageType, viewSettings.showYear, viewSettings.IsPlayed, viewSettings.IsUnplayed, viewSettings.IsFavorite, viewSettings.IsResumable, viewSettings.IsHD, viewSettings.IsSD, viewSettings.SortBy, viewSettings.SortOrder, viewSettings.VideoTypes, viewSettings.GenreIds, viewSettings.Is4K, viewSettings.Is3D, viewSettings.HasSubtitles, viewSettings.HasTrailer, viewSettings.HasSpecialFeature, viewSettings.HasThemeSong, viewSettings.HasThemeVideo, viewSettings.StartIndex, viewSettings.NameLessThan, viewSettings.NameStartsWith, getItemTypes, getBasekey, topParentId]);
|
||||||
|
|
||||||
|
const fetchData = useCallback(() => {
|
||||||
|
loading.show();
|
||||||
|
|
||||||
|
const apiClient = ServerConnections.getApiClient(window.ApiClient.serverId());
|
||||||
|
return apiClient.getItems(
|
||||||
|
apiClient.getCurrentUserId(),
|
||||||
|
{
|
||||||
|
...getQuery()
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}, [getQuery]);
|
||||||
|
|
||||||
const reloadItems = useCallback(() => {
|
const reloadItems = useCallback(() => {
|
||||||
const page = element.current;
|
const page = element.current;
|
||||||
|
|
||||||
|
@ -293,19 +308,19 @@ const ViewItemsContainer: FC<ViewItemsContainerProps> = ({
|
||||||
console.error('Unexpected null reference');
|
console.error('Unexpected null reference');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
loading.show();
|
setisLoading(false);
|
||||||
const query = getQueryWithFilters().query;
|
fetchData().then((result) => {
|
||||||
window.ApiClient.getItems(window.ApiClient.getCurrentUserId(), query).then((result) => {
|
|
||||||
setItemsResult(result);
|
setItemsResult(result);
|
||||||
window.scrollTo(0, 0);
|
|
||||||
|
|
||||||
loading.hide();
|
window.scrollTo(0, 0);
|
||||||
|
|
||||||
import('../../components/autoFocuser').then(({ default: autoFocuser }) => {
|
import('../../components/autoFocuser').then(({ default: autoFocuser }) => {
|
||||||
autoFocuser.autoFocus(page);
|
autoFocuser.autoFocus(page);
|
||||||
});
|
});
|
||||||
|
loading.hide();
|
||||||
|
setisLoading(true);
|
||||||
});
|
});
|
||||||
}, [getQueryWithFilters]);
|
}, [fetchData]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
reloadItems();
|
reloadItems();
|
||||||
|
@ -316,37 +331,31 @@ const ViewItemsContainer: FC<ViewItemsContainerProps> = ({
|
||||||
<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
|
<Pagination
|
||||||
itemsResult= {itemsResult}
|
itemsResult= {itemsResult}
|
||||||
query={query}
|
viewSettings={viewSettings}
|
||||||
setQuery={setQuery}
|
setViewSettings={setViewSettings}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{isBtnShuffleEnabled && <Shuffle itemsResult={itemsResult} topParentId={topParentId} />}
|
{isBtnShuffleEnabled && <Shuffle itemsResult={itemsResult} topParentId={topParentId} />}
|
||||||
|
|
||||||
{<SelectView
|
<SelectView
|
||||||
getSettingsKey={getSettingsKey}
|
|
||||||
getVisibleViewSettings={getVisibleViewSettings}
|
getVisibleViewSettings={getVisibleViewSettings}
|
||||||
getViewSettings={getViewSettings}
|
viewSettings={viewSettings}
|
||||||
setQuery={setQuery}
|
setViewSettings={setViewSettings}
|
||||||
reloadItems={reloadItems}
|
/>
|
||||||
/>}
|
|
||||||
|
|
||||||
<Sort
|
<Sort
|
||||||
getSortMenuOptions={getSortMenuOptions}
|
getSortMenuOptions={getSortMenuOptions}
|
||||||
getSortValues={getSortValues}
|
viewSettings={viewSettings}
|
||||||
getSettingsKey={getSettingsKey}
|
setViewSettings={setViewSettings}
|
||||||
setQuery={setQuery}
|
|
||||||
reloadItems={reloadItems}
|
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{isBtnFilterEnabled && <Filter
|
{isBtnFilterEnabled && <Filter
|
||||||
topParentId={topParentId}
|
topParentId={topParentId}
|
||||||
getFilters={getFilters}
|
|
||||||
getSettingsKey={getSettingsKey}
|
|
||||||
getItemTypes={getItemTypes}
|
getItemTypes={getItemTypes}
|
||||||
getVisibleFilters={getVisibleFilters}
|
getVisibleFilters={getVisibleFilters}
|
||||||
getFilterMenuOptions={getFilterMenuOptions}
|
getFilterMenuOptions={getFilterMenuOptions}
|
||||||
setQuery={setQuery}
|
viewSettings={viewSettings}
|
||||||
reloadItems={reloadItems}
|
setViewSettings={setViewSettings}
|
||||||
/>}
|
/>}
|
||||||
|
|
||||||
{isBtnNewCollectionEnabled && <NewCollection />}
|
{isBtnNewCollectionEnabled && <NewCollection />}
|
||||||
|
@ -354,22 +363,20 @@ const ViewItemsContainer: FC<ViewItemsContainerProps> = ({
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{isAlphaPickerEnabled && <AlphaPickerContainer
|
{isAlphaPickerEnabled && <AlphaPickerContainer
|
||||||
getQuery={getQuery}
|
viewSettings={viewSettings}
|
||||||
setQuery={setQuery}
|
setViewSettings={setViewSettings}
|
||||||
/>}
|
/>}
|
||||||
|
|
||||||
<ItemsContainer
|
{isLoading && <ItemsContainer
|
||||||
getViewSettings={getViewSettings}
|
viewSettings={viewSettings}
|
||||||
getContext={getContext}
|
getItemsHtml={getItemsHtml}
|
||||||
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'>
|
<div className='flex align-items-center justify-content-center flex-wrap-wrap padded-top padded-left padded-right padded-bottom focuscontainer-x'>
|
||||||
<Pagination
|
<Pagination
|
||||||
itemsResult= {itemsResult}
|
itemsResult= {itemsResult}
|
||||||
query={query}
|
viewSettings={viewSettings}
|
||||||
setQuery={setQuery}
|
setViewSettings={setViewSettings}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,54 +1,48 @@
|
||||||
import type { BaseItemDto } from '@jellyfin/sdk/lib/generated-client';
|
import type { BaseItemDto } from '@jellyfin/sdk/lib/generated-client';
|
||||||
|
|
||||||
export interface QueryI {
|
export interface QueryI extends ViewSettingsI {
|
||||||
SortBy?: string;
|
|
||||||
SortOrder?: string;
|
|
||||||
IncludeItemTypes?: string;
|
IncludeItemTypes?: string;
|
||||||
Recursive?: boolean;
|
Recursive?: boolean;
|
||||||
Fields?: string;
|
Fields?: string | null;
|
||||||
ImageTypeLimit?: number;
|
ImageTypeLimit?: number;
|
||||||
EnableTotalRecordCount?: boolean;
|
EnableTotalRecordCount?: boolean;
|
||||||
EnableImageTypes?: string;
|
EnableImageTypes?: string;
|
||||||
StartIndex?: number;
|
StartIndex?: number;
|
||||||
ParentId?: string | null;
|
ParentId?: string | null;
|
||||||
IsFavorite?: boolean;
|
IsMissing?: boolean | null;
|
||||||
IsMissing?: boolean;
|
|
||||||
Limit?:number;
|
Limit?:number;
|
||||||
NameStartsWithOrGreater?: string;
|
|
||||||
NameLessThan?: string;
|
|
||||||
NameStartsWith?: string | null;
|
|
||||||
VideoTypes?: string;
|
|
||||||
GenreIds?: string;
|
|
||||||
Is4K?: boolean;
|
|
||||||
IsHD?: boolean;
|
|
||||||
Is3D?: boolean;
|
|
||||||
HasSubtitles?: boolean;
|
|
||||||
HasTrailer?: boolean;
|
|
||||||
HasSpecialFeature?: boolean;
|
|
||||||
HasThemeSong?: boolean;
|
|
||||||
HasThemeVideo?: boolean;
|
|
||||||
Filters?: string | null;
|
Filters?: string | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface FiltersI {
|
export interface ViewSettingsI {
|
||||||
IsPlayed: boolean;
|
showTitle?: boolean;
|
||||||
IsUnplayed: boolean;
|
showYear?: boolean;
|
||||||
IsFavorite: boolean;
|
imageType?: string;
|
||||||
IsResumable: boolean;
|
viewType?: string;
|
||||||
Is4K: boolean;
|
cardLayout?: boolean;
|
||||||
IsHD: boolean;
|
SortBy?: string | null;
|
||||||
IsSD: boolean;
|
SortOrder?: string | null;
|
||||||
Is3D: boolean;
|
IsPlayed?: boolean | null;
|
||||||
VideoTypes: string;
|
IsUnplayed?: boolean | null;
|
||||||
SeriesStatus: string;
|
IsFavorite?: boolean | null;
|
||||||
HasSubtitles: string;
|
IsResumable?: boolean | null;
|
||||||
HasTrailer: string;
|
Is4K?: boolean | null;
|
||||||
HasSpecialFeature: string;
|
IsHD?: boolean | null;
|
||||||
HasThemeSong: string;
|
IsSD?: boolean | null;
|
||||||
HasThemeVideo: string;
|
Is3D?: boolean | null;
|
||||||
GenreIds: string;
|
VideoTypes?: string | null;
|
||||||
|
SeriesStatus?: string | null;
|
||||||
|
HasSubtitles?: boolean | null;
|
||||||
|
HasTrailer?: boolean | null;
|
||||||
|
HasSpecialFeature?: boolean | null;
|
||||||
|
ParentIndexNumber?: boolean | null;
|
||||||
|
HasThemeSong?: boolean | null;
|
||||||
|
HasThemeVideo?: boolean | null;
|
||||||
|
GenreIds?: string | null;
|
||||||
|
NameLessThan?: string | null;
|
||||||
|
NameStartsWith?: string | null;
|
||||||
|
StartIndex?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface CardOptionsI {
|
export interface CardOptionsI {
|
||||||
itemsContainer?: HTMLElement | null;
|
itemsContainer?: HTMLElement | null;
|
||||||
parentContainer?: HTMLElement | null;
|
parentContainer?: HTMLElement | null;
|
||||||
|
|
20
src/view/hook/useLocalStorage.tsx
Normal file
20
src/view/hook/useLocalStorage.tsx
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
import { useEffect, useState } from 'react';
|
||||||
|
|
||||||
|
export function useLocalStorage<T>(key: string, initialValue: T | (() => T)) {
|
||||||
|
const [value, setValue] = useState<T>(() => {
|
||||||
|
const storedValues = localStorage.getItem(key);
|
||||||
|
if (storedValues != null) return JSON.parse(storedValues);
|
||||||
|
|
||||||
|
if (typeof initialValue === 'function') {
|
||||||
|
return (initialValue as () => T)();
|
||||||
|
} else {
|
||||||
|
return initialValue;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
localStorage.setItem(key, JSON.stringify(value));
|
||||||
|
}, [key, value]);
|
||||||
|
|
||||||
|
return [value, setValue] as [typeof value, typeof setValue];
|
||||||
|
}
|
Loading…
Add table
Add a link
Reference in a new issue