1
0
Fork 0
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:
grafixeyehero 2022-10-21 21:47:54 +03:00
parent f40c565e4a
commit 6341a71fec
15 changed files with 485 additions and 508 deletions

View file

@ -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();
//} //}
} }

View file

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

View file

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

View file

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

View file

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

View file

@ -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'
}; };
} }

View file

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

View file

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

View file

@ -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}>

View file

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

View file

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

View file

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

View file

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

View file

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

View 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];
}