mirror of
https://github.com/jellyfin/jellyfin-web
synced 2025-03-30 19:56:21 +00:00
Merge pull request #4776 from grafixeyehero/Add-filters-status-indicator
This commit is contained in:
commit
5fb4b51cfb
12 changed files with 235 additions and 292 deletions
|
@ -10,6 +10,7 @@ import MuiAccordionSummary, {
|
||||||
AccordionSummaryProps
|
AccordionSummaryProps
|
||||||
} from '@mui/material/AccordionSummary';
|
} from '@mui/material/AccordionSummary';
|
||||||
import IconButton from '@mui/material/IconButton';
|
import IconButton from '@mui/material/IconButton';
|
||||||
|
import { Badge } from '@mui/material';
|
||||||
import { styled } from '@mui/material/styles';
|
import { styled } from '@mui/material/styles';
|
||||||
import Typography from '@mui/material/Typography';
|
import Typography from '@mui/material/Typography';
|
||||||
|
|
||||||
|
@ -152,6 +153,9 @@ const FilterButton: FC<FilterButtonProps> = ({
|
||||||
return viewType === LibraryTab.Episodes;
|
return viewType === LibraryTab.Episodes;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const hasFilters =
|
||||||
|
Object.values(libraryViewSettings.Filters || {}).some((filter) => !!filter);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box>
|
<Box>
|
||||||
<IconButton
|
<IconButton
|
||||||
|
@ -161,7 +165,9 @@ const FilterButton: FC<FilterButtonProps> = ({
|
||||||
className='paper-icon-button-light btnShuffle autoSize'
|
className='paper-icon-button-light btnShuffle autoSize'
|
||||||
onClick={handleClick}
|
onClick={handleClick}
|
||||||
>
|
>
|
||||||
<FilterListIcon />
|
<Badge color='info' variant='dot' invisible={!hasFilters}>
|
||||||
|
<FilterListIcon />
|
||||||
|
</Badge>
|
||||||
</IconButton>
|
</IconButton>
|
||||||
<Popover
|
<Popover
|
||||||
id={id}
|
id={id}
|
||||||
|
@ -239,7 +245,9 @@ const FilterButton: FC<FilterButtonProps> = ({
|
||||||
id='filtersEpisodesStatus-header'
|
id='filtersEpisodesStatus-header'
|
||||||
>
|
>
|
||||||
<Typography>
|
<Typography>
|
||||||
{globalize.translate('HeaderEpisodesStatus')}
|
{globalize.translate(
|
||||||
|
'HeaderEpisodesStatus'
|
||||||
|
)}
|
||||||
</Typography>
|
</Typography>
|
||||||
</AccordionSummary>
|
</AccordionSummary>
|
||||||
<AccordionDetails>
|
<AccordionDetails>
|
||||||
|
|
|
@ -3,12 +3,12 @@ import FormGroup from '@mui/material/FormGroup';
|
||||||
import FormControlLabel from '@mui/material/FormControlLabel';
|
import FormControlLabel from '@mui/material/FormControlLabel';
|
||||||
import Checkbox from '@mui/material/Checkbox';
|
import Checkbox from '@mui/material/Checkbox';
|
||||||
import globalize from 'scripts/globalize';
|
import globalize from 'scripts/globalize';
|
||||||
import { LibraryViewSettings } from 'types/library';
|
import { EpisodeFilter, LibraryViewSettings } from 'types/library';
|
||||||
|
|
||||||
const episodesStatusOptions = [
|
const episodeFilterOptions = [
|
||||||
{ label: 'OptionSpecialEpisode', value: 'ParentIndexNumber' },
|
{ label: 'OptionSpecialEpisode', value: EpisodeFilter.ParentIndexNumber },
|
||||||
{ label: 'OptionMissingEpisode', value: 'IsMissing' },
|
{ label: 'OptionMissingEpisode', value: EpisodeFilter.IsMissing },
|
||||||
{ label: 'OptionUnairedEpisode', value: 'IsUnaired' }
|
{ label: 'OptionUnairedEpisode', value: EpisodeFilter.IsUnaired }
|
||||||
];
|
];
|
||||||
|
|
||||||
interface FiltersEpisodesStatusProps {
|
interface FiltersEpisodesStatusProps {
|
||||||
|
@ -23,46 +23,39 @@ const FiltersEpisodesStatus: FC<FiltersEpisodesStatusProps> = ({
|
||||||
const onFiltersEpisodesStatusChange = useCallback(
|
const onFiltersEpisodesStatusChange = useCallback(
|
||||||
(event: React.ChangeEvent<HTMLInputElement>) => {
|
(event: React.ChangeEvent<HTMLInputElement>) => {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
const value = String(event.target.value);
|
const value = event.target.value as EpisodeFilter;
|
||||||
const existingValue = libraryViewSettings?.Filters?.EpisodesStatus;
|
const existingEpisodeFilter = libraryViewSettings?.Filters?.EpisodeFilter ?? [];
|
||||||
|
|
||||||
if (existingValue?.includes(value)) {
|
const updatedEpisodeFilter = existingEpisodeFilter.includes(value) ?
|
||||||
const newValue = existingValue?.filter(
|
existingEpisodeFilter.filter((filter) => filter !== value) :
|
||||||
(prevState: string) => prevState !== value
|
[...existingEpisodeFilter, value];
|
||||||
);
|
|
||||||
setLibraryViewSettings((prevState) => ({
|
setLibraryViewSettings((prevState) => ({
|
||||||
...prevState,
|
...prevState,
|
||||||
StartIndex: 0,
|
StartIndex: 0,
|
||||||
Filters: { ...prevState.Filters, EpisodesStatus: newValue }
|
Filters: {
|
||||||
}));
|
...prevState.Filters,
|
||||||
} else {
|
EpisodeFilter: updatedEpisodeFilter.length ? updatedEpisodeFilter : undefined
|
||||||
setLibraryViewSettings((prevState) => ({
|
}
|
||||||
...prevState,
|
}));
|
||||||
StartIndex: 0,
|
|
||||||
Filters: {
|
|
||||||
...prevState.Filters,
|
|
||||||
EpisodesStatus: [...(existingValue ?? []), value]
|
|
||||||
}
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
[setLibraryViewSettings, libraryViewSettings?.Filters?.EpisodesStatus]
|
[setLibraryViewSettings, libraryViewSettings?.Filters?.EpisodeFilter]
|
||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<FormGroup>
|
<FormGroup>
|
||||||
{episodesStatusOptions.map((filter) => (
|
{episodeFilterOptions.map((filter) => (
|
||||||
<FormControlLabel
|
<FormControlLabel
|
||||||
key={filter.value}
|
key={filter.value}
|
||||||
control={
|
control={
|
||||||
<Checkbox
|
<Checkbox
|
||||||
checked={
|
checked={
|
||||||
!!libraryViewSettings?.Filters?.EpisodesStatus?.includes(
|
!!libraryViewSettings?.Filters?.EpisodeFilter?.includes(
|
||||||
String( filter.value)
|
filter.value
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
onChange={onFiltersEpisodesStatusChange}
|
onChange={onFiltersEpisodesStatusChange}
|
||||||
value={String(filter.value)}
|
value={filter.value}
|
||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
label={globalize.translate(filter.label)}
|
label={globalize.translate(filter.label)}
|
||||||
|
|
|
@ -3,14 +3,14 @@ import FormGroup from '@mui/material/FormGroup';
|
||||||
import FormControlLabel from '@mui/material/FormControlLabel';
|
import FormControlLabel from '@mui/material/FormControlLabel';
|
||||||
import Checkbox from '@mui/material/Checkbox';
|
import Checkbox from '@mui/material/Checkbox';
|
||||||
import globalize from 'scripts/globalize';
|
import globalize from 'scripts/globalize';
|
||||||
import { LibraryViewSettings } from 'types/library';
|
import { FeatureFilters, LibraryViewSettings } from 'types/library';
|
||||||
|
|
||||||
const featuresOptions = [
|
const featuresOptions = [
|
||||||
{ label: 'Subtitles', value: 'HasSubtitles' },
|
{ label: 'Subtitles', value: FeatureFilters.HasSubtitles },
|
||||||
{ label: 'Trailers', value: 'HasTrailer' },
|
{ label: 'Trailers', value: FeatureFilters.HasTrailer },
|
||||||
{ label: 'Extras', value: 'HasSpecialFeature' },
|
{ label: 'Extras', value: FeatureFilters.HasSpecialFeature },
|
||||||
{ label: 'ThemeSongs', value: 'HasThemeSong' },
|
{ label: 'ThemeSongs', value: FeatureFilters.HasThemeSong },
|
||||||
{ label: 'ThemeVideos', value: 'HasThemeVideo' }
|
{ label: 'ThemeVideos', value: FeatureFilters.HasThemeVideo }
|
||||||
];
|
];
|
||||||
|
|
||||||
interface FiltersFeaturesProps {
|
interface FiltersFeaturesProps {
|
||||||
|
@ -27,29 +27,21 @@ const FiltersFeatures: FC<FiltersFeaturesProps> = ({
|
||||||
const onFiltersFeaturesChange = useCallback(
|
const onFiltersFeaturesChange = useCallback(
|
||||||
(event: React.ChangeEvent<HTMLInputElement>) => {
|
(event: React.ChangeEvent<HTMLInputElement>) => {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
const value = String(event.target.value);
|
const value = event.target.value as FeatureFilters;
|
||||||
const existingValue =
|
const existingFeatures = libraryViewSettings?.Filters?.Features ?? [];
|
||||||
libraryViewSettings?.Filters?.Features;
|
|
||||||
|
|
||||||
if (existingValue?.includes(value)) {
|
const updatedFeatures = existingFeatures.includes(value) ?
|
||||||
const newValue = existingValue?.filter(
|
existingFeatures.filter((filter) => filter !== value) :
|
||||||
(prevState: string) => prevState !== value
|
[...existingFeatures, value];
|
||||||
);
|
|
||||||
setLibraryViewSettings((prevState) => ({
|
setLibraryViewSettings((prevState) => ({
|
||||||
...prevState,
|
...prevState,
|
||||||
StartIndex: 0,
|
StartIndex: 0,
|
||||||
Filters: { ...prevState.Filters, Features: newValue }
|
Filters: {
|
||||||
}));
|
...prevState.Filters,
|
||||||
} else {
|
Features: updatedFeatures.length ? updatedFeatures : undefined
|
||||||
setLibraryViewSettings((prevState) => ({
|
}
|
||||||
...prevState,
|
}));
|
||||||
StartIndex: 0,
|
|
||||||
Filters: {
|
|
||||||
...prevState.Filters,
|
|
||||||
Features: [...(existingValue ?? []), value]
|
|
||||||
}
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
[setLibraryViewSettings, libraryViewSettings?.Filters?.Features]
|
[setLibraryViewSettings, libraryViewSettings?.Filters?.Features]
|
||||||
);
|
);
|
||||||
|
@ -64,11 +56,11 @@ const FiltersFeatures: FC<FiltersFeaturesProps> = ({
|
||||||
<Checkbox
|
<Checkbox
|
||||||
checked={
|
checked={
|
||||||
!!libraryViewSettings?.Filters?.Features?.includes(
|
!!libraryViewSettings?.Filters?.Features?.includes(
|
||||||
String(filter.value)
|
filter.value
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
onChange={onFiltersFeaturesChange}
|
onChange={onFiltersFeaturesChange}
|
||||||
value={String(filter.value)}
|
value={filter.value}
|
||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
label={globalize.translate(filter.label)}
|
label={globalize.translate(filter.label)}
|
||||||
|
|
|
@ -19,28 +19,21 @@ const FiltersGenres: FC<FiltersGenresProps> = ({
|
||||||
const onFiltersGenresChange = useCallback(
|
const onFiltersGenresChange = useCallback(
|
||||||
(event: React.ChangeEvent<HTMLInputElement>) => {
|
(event: React.ChangeEvent<HTMLInputElement>) => {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
const value = String(event.target.value);
|
const value = event.target.value;
|
||||||
const existingValue = libraryViewSettings?.Filters?.Genres;
|
const existingGenres = libraryViewSettings?.Filters?.Genres ?? [];
|
||||||
|
|
||||||
if (existingValue?.includes(value)) {
|
const updatedGenres = existingGenres.includes(value) ?
|
||||||
const newValue = existingValue?.filter(
|
existingGenres.filter((filter) => filter !== value) :
|
||||||
(prevState: string) => prevState !== value
|
[...existingGenres, value];
|
||||||
);
|
|
||||||
setLibraryViewSettings((prevState) => ({
|
setLibraryViewSettings((prevState) => ({
|
||||||
...prevState,
|
...prevState,
|
||||||
StartIndex: 0,
|
StartIndex: 0,
|
||||||
Filters: { ...prevState.Filters, Genres: newValue }
|
Filters: {
|
||||||
}));
|
...prevState.Filters,
|
||||||
} else {
|
Genres: updatedGenres.length ? updatedGenres : undefined
|
||||||
setLibraryViewSettings((prevState) => ({
|
}
|
||||||
...prevState,
|
}));
|
||||||
StartIndex: 0,
|
|
||||||
Filters: {
|
|
||||||
...prevState.Filters,
|
|
||||||
Genres: [...(existingValue ?? []), value]
|
|
||||||
}
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
[setLibraryViewSettings, libraryViewSettings?.Filters?.Genres]
|
[setLibraryViewSettings, libraryViewSettings?.Filters?.Genres]
|
||||||
);
|
);
|
||||||
|
|
|
@ -19,28 +19,21 @@ const FiltersOfficialRatings: FC<FiltersOfficialRatingsProps> = ({
|
||||||
const onFiltersOfficialRatingsChange = useCallback(
|
const onFiltersOfficialRatingsChange = useCallback(
|
||||||
(event: React.ChangeEvent<HTMLInputElement>) => {
|
(event: React.ChangeEvent<HTMLInputElement>) => {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
const value = String(event.target.value);
|
const value = event.target.value;
|
||||||
const existingValue = libraryViewSettings?.Filters?.OfficialRatings;
|
const existingOfficialRatings = libraryViewSettings?.Filters?.OfficialRatings ?? [];
|
||||||
|
|
||||||
if (existingValue?.includes(value)) {
|
const updatedOfficialRatings = existingOfficialRatings.includes(value) ?
|
||||||
const newValue = existingValue?.filter(
|
existingOfficialRatings.filter((filter) => filter !== value) :
|
||||||
(prevState: string) => prevState !== value
|
[...existingOfficialRatings, value];
|
||||||
);
|
|
||||||
setLibraryViewSettings((prevState) => ({
|
setLibraryViewSettings((prevState) => ({
|
||||||
...prevState,
|
...prevState,
|
||||||
StartIndex: 0,
|
StartIndex: 0,
|
||||||
Filters: { ...prevState.Filters, OfficialRatings: newValue }
|
Filters: {
|
||||||
}));
|
...prevState.Filters,
|
||||||
} else {
|
OfficialRatings: updatedOfficialRatings.length ? updatedOfficialRatings : undefined
|
||||||
setLibraryViewSettings((prevState) => ({
|
}
|
||||||
...prevState,
|
}));
|
||||||
StartIndex: 0,
|
|
||||||
Filters: {
|
|
||||||
...prevState.Filters,
|
|
||||||
OfficialRatings: [...(existingValue ?? []), value]
|
|
||||||
}
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
[setLibraryViewSettings, libraryViewSettings?.Filters?.OfficialRatings]
|
[setLibraryViewSettings, libraryViewSettings?.Filters?.OfficialRatings]
|
||||||
);
|
);
|
||||||
|
|
|
@ -25,27 +25,20 @@ const FiltersSeriesStatus: FC<FiltersSeriesStatusProps> = ({
|
||||||
(event: React.ChangeEvent<HTMLInputElement>) => {
|
(event: React.ChangeEvent<HTMLInputElement>) => {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
const value = event.target.value as SeriesStatus;
|
const value = event.target.value as SeriesStatus;
|
||||||
const existingValue = libraryViewSettings?.Filters?.SeriesStatus;
|
const existingSeriesStatus = libraryViewSettings?.Filters?.SeriesStatus ?? [];
|
||||||
|
|
||||||
if (existingValue?.includes(value)) {
|
const updatedSeriesStatus = existingSeriesStatus.includes(value) ?
|
||||||
const newValue = existingValue?.filter(
|
existingSeriesStatus.filter((filter) => filter !== value) :
|
||||||
(prevState: SeriesStatus) => prevState !== value
|
[...existingSeriesStatus, value];
|
||||||
);
|
|
||||||
setLibraryViewSettings((prevState) => ({
|
setLibraryViewSettings((prevState) => ({
|
||||||
...prevState,
|
...prevState,
|
||||||
StartIndex: 0,
|
StartIndex: 0,
|
||||||
Filters: { ...prevState.Filters, SeriesStatus: newValue }
|
Filters: {
|
||||||
}));
|
...prevState.Filters,
|
||||||
} else {
|
SeriesStatus: updatedSeriesStatus.length ? updatedSeriesStatus : undefined
|
||||||
setLibraryViewSettings((prevState) => ({
|
}
|
||||||
...prevState,
|
}));
|
||||||
StartIndex: 0,
|
|
||||||
Filters: {
|
|
||||||
...prevState.Filters,
|
|
||||||
SeriesStatus: [...(existingValue ?? []), value]
|
|
||||||
}
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
[setLibraryViewSettings, libraryViewSettings?.Filters?.SeriesStatus]
|
[setLibraryViewSettings, libraryViewSettings?.Filters?.SeriesStatus]
|
||||||
);
|
);
|
||||||
|
|
|
@ -29,27 +29,20 @@ const FiltersStatus: FC<FiltersStatusProps> = ({
|
||||||
(event: React.ChangeEvent<HTMLInputElement>) => {
|
(event: React.ChangeEvent<HTMLInputElement>) => {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
const value = event.target.value as ItemFilter;
|
const value = event.target.value as ItemFilter;
|
||||||
const existingValue = libraryViewSettings?.Filters?.Status;
|
const existingStatus = libraryViewSettings?.Filters?.Status ?? [];
|
||||||
|
|
||||||
if (existingValue?.includes(value)) {
|
const updatedStatus = existingStatus.includes(value) ?
|
||||||
const newValue = existingValue?.filter(
|
existingStatus.filter((filter) => filter !== value) :
|
||||||
(prevState: ItemFilter) => prevState !== value
|
[...existingStatus, value];
|
||||||
);
|
|
||||||
setLibraryViewSettings((prevState) => ({
|
setLibraryViewSettings((prevState) => ({
|
||||||
...prevState,
|
...prevState,
|
||||||
StartIndex: 0,
|
StartIndex: 0,
|
||||||
Filters: { ...prevState.Filters, Status: newValue }
|
Filters: {
|
||||||
}));
|
...prevState.Filters,
|
||||||
} else {
|
Status: updatedStatus.length ? updatedStatus : undefined
|
||||||
setLibraryViewSettings((prevState) => ({
|
}
|
||||||
...prevState,
|
}));
|
||||||
StartIndex: 0,
|
|
||||||
Filters: {
|
|
||||||
...prevState.Filters,
|
|
||||||
Status: [...(existingValue ?? []), value]
|
|
||||||
}
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
[setLibraryViewSettings, libraryViewSettings?.Filters?.Status]
|
[setLibraryViewSettings, libraryViewSettings?.Filters?.Status]
|
||||||
);
|
);
|
||||||
|
|
|
@ -19,30 +19,23 @@ const FiltersStudios: FC<FiltersStudiosProps> = ({
|
||||||
const onFiltersStudiosChange = useCallback(
|
const onFiltersStudiosChange = useCallback(
|
||||||
(event: React.ChangeEvent<HTMLInputElement>) => {
|
(event: React.ChangeEvent<HTMLInputElement>) => {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
const value = String(event.target.value);
|
const value = event.target.value;
|
||||||
const existingValue = libraryViewSettings?.Filters?.StudioIds;
|
const existingStudioIds = libraryViewSettings?.Filters?.StudioIds ?? [];
|
||||||
|
|
||||||
if (existingValue?.includes(value)) {
|
const updatedStudioIds = existingStudioIds.includes(value) ?
|
||||||
const newValue = existingValue?.filter(
|
existingStudioIds.filter((filter) => filter !== value) :
|
||||||
(prevState: string) => prevState !== value
|
[...existingStudioIds, value];
|
||||||
);
|
|
||||||
setLibraryViewSettings((prevState) => ({
|
setLibraryViewSettings((prevState) => ({
|
||||||
...prevState,
|
...prevState,
|
||||||
StartIndex: 0,
|
StartIndex: 0,
|
||||||
Filters: { ...prevState.Filters, StudioIds: newValue }
|
Filters: {
|
||||||
}));
|
...prevState.Filters,
|
||||||
} else {
|
StudioIds: updatedStudioIds.length ? updatedStudioIds : undefined
|
||||||
setLibraryViewSettings((prevState) => ({
|
}
|
||||||
...prevState,
|
}));
|
||||||
StartIndex: 0,
|
|
||||||
Filters: {
|
|
||||||
...prevState.Filters,
|
|
||||||
StudioIds: [...(existingValue ?? []), value]
|
|
||||||
}
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
[setLibraryViewSettings, libraryViewSettings?.Filters?.StudioIds]
|
[setLibraryViewSettings, libraryViewSettings.Filters?.StudioIds]
|
||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|
|
@ -19,30 +19,23 @@ const FiltersTags: FC<FiltersTagsProps> = ({
|
||||||
const onFiltersTagsChange = useCallback(
|
const onFiltersTagsChange = useCallback(
|
||||||
(event: React.ChangeEvent<HTMLInputElement>) => {
|
(event: React.ChangeEvent<HTMLInputElement>) => {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
const value = String(event.target.value);
|
const value = event.target.value;
|
||||||
const existingValue = libraryViewSettings?.Filters?.Tags;
|
const existingTags = libraryViewSettings?.Filters?.Tags ?? [];
|
||||||
|
|
||||||
if (existingValue?.includes(value)) {
|
const updatedTags = existingTags.includes(value) ?
|
||||||
const newValue = existingValue?.filter(
|
existingTags.filter((filter) => filter !== value) :
|
||||||
(prevState: string) => prevState !== value
|
[...existingTags, value];
|
||||||
);
|
|
||||||
setLibraryViewSettings((prevState) => ({
|
setLibraryViewSettings((prevState) => ({
|
||||||
...prevState,
|
...prevState,
|
||||||
StartIndex: 0,
|
StartIndex: 0,
|
||||||
Filters: { ...prevState.Filters, Tags: newValue }
|
Filters: {
|
||||||
}));
|
...prevState.Filters,
|
||||||
} else {
|
Tags: updatedTags.length ? updatedTags : undefined
|
||||||
setLibraryViewSettings((prevState) => ({
|
}
|
||||||
...prevState,
|
}));
|
||||||
StartIndex: 0,
|
|
||||||
Filters: {
|
|
||||||
...prevState.Filters,
|
|
||||||
Tags: [...(existingValue ?? []), value]
|
|
||||||
}
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
[setLibraryViewSettings, libraryViewSettings?.Filters?.Tags]
|
[setLibraryViewSettings, libraryViewSettings.Filters?.Tags]
|
||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|
|
@ -2,9 +2,15 @@ import React, { FC, useCallback } from 'react';
|
||||||
import FormGroup from '@mui/material/FormGroup';
|
import FormGroup from '@mui/material/FormGroup';
|
||||||
import FormControlLabel from '@mui/material/FormControlLabel';
|
import FormControlLabel from '@mui/material/FormControlLabel';
|
||||||
import Checkbox from '@mui/material/Checkbox';
|
import Checkbox from '@mui/material/Checkbox';
|
||||||
import { LibraryViewSettings } from 'types/library';
|
import { LibraryViewSettings, VideoBasicFilter } from 'types/library';
|
||||||
import { VideoType } from '@jellyfin/sdk/lib/generated-client';
|
import { VideoType } from '@jellyfin/sdk/lib/generated-client';
|
||||||
import globalize from 'scripts/globalize';
|
|
||||||
|
const videoBasicFilterOptions = [
|
||||||
|
{ label: 'SD', value: VideoBasicFilter.IsSD },
|
||||||
|
{ label: 'HD', value: VideoBasicFilter.IsHD },
|
||||||
|
{ label: '4K', value: VideoBasicFilter.Is4K },
|
||||||
|
{ label: '3D', value: VideoBasicFilter.Is3D }
|
||||||
|
];
|
||||||
|
|
||||||
const videoTypesOptions = [
|
const videoTypesOptions = [
|
||||||
{ label: 'DVD', value: VideoType.Dvd },
|
{ label: 'DVD', value: VideoType.Dvd },
|
||||||
|
@ -21,93 +27,67 @@ const FiltersVideoTypes: FC<FiltersVideoTypesProps> = ({
|
||||||
libraryViewSettings,
|
libraryViewSettings,
|
||||||
setLibraryViewSettings
|
setLibraryViewSettings
|
||||||
}) => {
|
}) => {
|
||||||
|
const onFiltersvideoStandardChange = useCallback(
|
||||||
|
(event: React.ChangeEvent<HTMLInputElement>) => {
|
||||||
|
event.preventDefault();
|
||||||
|
const value = event.target.value as VideoBasicFilter;
|
||||||
|
const existingVideoBasicFilter = libraryViewSettings?.Filters?.VideoBasicFilter ?? [];
|
||||||
|
|
||||||
|
const updatedVideoBasicFilter = existingVideoBasicFilter.includes(value) ?
|
||||||
|
existingVideoBasicFilter.filter((filter) => filter !== value) :
|
||||||
|
[...existingVideoBasicFilter, value];
|
||||||
|
|
||||||
|
setLibraryViewSettings((prevState) => ({
|
||||||
|
...prevState,
|
||||||
|
Filters: {
|
||||||
|
...prevState.Filters,
|
||||||
|
VideoBasicFilter: updatedVideoBasicFilter.length ? updatedVideoBasicFilter : undefined
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
},
|
||||||
|
[setLibraryViewSettings, libraryViewSettings?.Filters?.VideoBasicFilter]
|
||||||
|
);
|
||||||
|
|
||||||
const onFiltersVideoTypesChange = useCallback(
|
const onFiltersVideoTypesChange = useCallback(
|
||||||
(event: React.ChangeEvent<HTMLInputElement>) => {
|
(event: React.ChangeEvent<HTMLInputElement>) => {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
const value = event.target.value as VideoType;
|
const value = event.target.value as VideoType;
|
||||||
const existingValue = libraryViewSettings?.Filters?.VideoTypes;
|
const existingVideoTypes = libraryViewSettings?.Filters?.VideoTypes ?? [];
|
||||||
|
|
||||||
if (existingValue?.includes(value)) {
|
const updatedVideoTypes = existingVideoTypes.includes(value) ?
|
||||||
const newValue = existingValue?.filter(
|
existingVideoTypes.filter((filter) => filter !== value) :
|
||||||
(prevState: VideoType) => prevState !== value
|
[...existingVideoTypes, value];
|
||||||
);
|
|
||||||
setLibraryViewSettings((prevState) => ({
|
setLibraryViewSettings((prevState) => ({
|
||||||
...prevState,
|
...prevState,
|
||||||
StartIndex: 0,
|
StartIndex: 0,
|
||||||
Filters: { ...prevState.Filters, VideoTypes: newValue }
|
Filters: {
|
||||||
}));
|
...prevState.Filters,
|
||||||
} else {
|
VideoTypes: updatedVideoTypes.length ? updatedVideoTypes : undefined
|
||||||
setLibraryViewSettings((prevState) => ({
|
}
|
||||||
...prevState,
|
}));
|
||||||
StartIndex: 0,
|
|
||||||
Filters: {
|
|
||||||
...prevState.Filters,
|
|
||||||
VideoTypes: [...(existingValue ?? []), value]
|
|
||||||
}
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
[setLibraryViewSettings, libraryViewSettings?.Filters?.VideoTypes]
|
[setLibraryViewSettings, libraryViewSettings?.Filters?.VideoTypes]
|
||||||
);
|
);
|
||||||
|
|
||||||
const handleChange = useCallback(
|
|
||||||
(event: React.ChangeEvent<HTMLInputElement>) => {
|
|
||||||
const name = event.target.name;
|
|
||||||
|
|
||||||
setLibraryViewSettings((prevState) => ({
|
|
||||||
...prevState,
|
|
||||||
[name]: event.target.checked
|
|
||||||
}));
|
|
||||||
},
|
|
||||||
[setLibraryViewSettings]
|
|
||||||
);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<FormGroup>
|
<FormGroup>
|
||||||
<FormControlLabel
|
{videoBasicFilterOptions
|
||||||
control={
|
.map((filter) => (
|
||||||
<Checkbox
|
<FormControlLabel
|
||||||
checked={libraryViewSettings.IsSD}
|
key={filter.value}
|
||||||
onChange={handleChange}
|
control={
|
||||||
name='IsSD'
|
<Checkbox
|
||||||
/>
|
checked={
|
||||||
}
|
!!libraryViewSettings?.Filters?.VideoBasicFilter?.includes(filter.value)
|
||||||
label={globalize.translate('SD')}
|
}
|
||||||
/>
|
onChange={onFiltersvideoStandardChange}
|
||||||
<FormControlLabel
|
value={filter.value}
|
||||||
control={
|
/>
|
||||||
<Checkbox
|
|
||||||
checked={libraryViewSettings.IsHD}
|
|
||||||
onChange={handleChange}
|
|
||||||
name='IsHD'
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
label={globalize.translate('HD')}
|
|
||||||
/>
|
|
||||||
<FormControlLabel
|
|
||||||
control={
|
|
||||||
<Checkbox
|
|
||||||
checked={
|
|
||||||
libraryViewSettings.Is4K
|
|
||||||
}
|
}
|
||||||
onChange={handleChange}
|
label={filter.label}
|
||||||
name='Is4K'
|
|
||||||
/>
|
/>
|
||||||
}
|
))}
|
||||||
label={globalize.translate('4K')}
|
|
||||||
/>
|
|
||||||
<FormControlLabel
|
|
||||||
control={
|
|
||||||
<Checkbox
|
|
||||||
checked={
|
|
||||||
libraryViewSettings.Is3D
|
|
||||||
}
|
|
||||||
onChange={handleChange}
|
|
||||||
name='Is3D'
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
label={globalize.translate('3D')}
|
|
||||||
/>
|
|
||||||
{videoTypesOptions
|
{videoTypesOptions
|
||||||
.map((filter) => (
|
.map((filter) => (
|
||||||
<FormControlLabel
|
<FormControlLabel
|
||||||
|
|
|
@ -20,27 +20,20 @@ const FiltersYears: FC<FiltersYearsProps> = ({
|
||||||
(event: React.ChangeEvent<HTMLInputElement>) => {
|
(event: React.ChangeEvent<HTMLInputElement>) => {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
const value = Number(event.target.value);
|
const value = Number(event.target.value);
|
||||||
const existingValue = libraryViewSettings?.Filters?.Years;
|
const existingYears = libraryViewSettings?.Filters?.Years ?? [];
|
||||||
|
|
||||||
if (existingValue?.includes(value)) {
|
const updatedYears = existingYears.includes(value) ?
|
||||||
const newValue = existingValue?.filter(
|
existingYears.filter((filter) => filter !== value) :
|
||||||
(prevState: number) => prevState !== value
|
[...existingYears, value];
|
||||||
);
|
|
||||||
setLibraryViewSettings((prevState) => ({
|
setLibraryViewSettings((prevState) => ({
|
||||||
...prevState,
|
...prevState,
|
||||||
StartIndex: 0,
|
StartIndex: 0,
|
||||||
Filters: { ...prevState.Filters, Years: newValue }
|
Filters: {
|
||||||
}));
|
...prevState.Filters,
|
||||||
} else {
|
Years: updatedYears.length ? updatedYears : undefined
|
||||||
setLibraryViewSettings((prevState) => ({
|
}
|
||||||
...prevState,
|
}));
|
||||||
StartIndex: 0,
|
|
||||||
Filters: {
|
|
||||||
...prevState.Filters,
|
|
||||||
Years: [...(existingValue ?? []), value]
|
|
||||||
}
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
[setLibraryViewSettings, libraryViewSettings?.Filters?.Years]
|
[setLibraryViewSettings, libraryViewSettings?.Filters?.Years]
|
||||||
);
|
);
|
||||||
|
|
|
@ -2,8 +2,8 @@ import type { ItemFilter } from '@jellyfin/sdk/lib/generated-client/models/item-
|
||||||
import type { VideoType } from '@jellyfin/sdk/lib/generated-client/models/video-type';
|
import type { VideoType } from '@jellyfin/sdk/lib/generated-client/models/video-type';
|
||||||
import type { SortOrder } from '@jellyfin/sdk/lib/generated-client/models/sort-order';
|
import type { SortOrder } from '@jellyfin/sdk/lib/generated-client/models/sort-order';
|
||||||
import type { SeriesStatus } from '@jellyfin/sdk/lib/generated-client/models/series-status';
|
import type { SeriesStatus } from '@jellyfin/sdk/lib/generated-client/models/series-status';
|
||||||
|
import type { ImageType } from '@jellyfin/sdk/lib/generated-client';
|
||||||
import { ItemSortBy } from '@jellyfin/sdk/lib/models/api/item-sort-by';
|
import { ItemSortBy } from '@jellyfin/sdk/lib/models/api/item-sort-by';
|
||||||
import { ImageType } from '@jellyfin/sdk/lib/generated-client';
|
|
||||||
|
|
||||||
export type ParentId = string | null | undefined;
|
export type ParentId = string | null | undefined;
|
||||||
|
|
||||||
|
@ -11,15 +11,38 @@ export interface LibraryViewProps {
|
||||||
parentId: string | null;
|
parentId: string | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface Filters {
|
export enum FeatureFilters {
|
||||||
Features?: string[];
|
HasSubtitles = 'HasSubtitles',
|
||||||
|
HasTrailer = 'HasTrailer',
|
||||||
|
HasSpecialFeature = 'HasSpecialFeature',
|
||||||
|
HasThemeSong = 'HasThemeSong',
|
||||||
|
HasThemeVideo = 'HasThemeVideo',
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum EpisodeFilter {
|
||||||
|
ParentIndexNumber = 'ParentIndexNumber',
|
||||||
|
IsMissing = 'IsMissing',
|
||||||
|
IsUnaired = 'IsUnaired',
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum VideoBasicFilter {
|
||||||
|
IsSD = 'IsSD',
|
||||||
|
IsHD = 'IsHD',
|
||||||
|
Is4K = 'Is4K',
|
||||||
|
Is3D = 'Is3D',
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Filters {
|
||||||
|
Features?: FeatureFilters[];
|
||||||
Genres?: string[];
|
Genres?: string[];
|
||||||
OfficialRatings?: string[];
|
OfficialRatings?: string[];
|
||||||
|
EpisodeFilter?: EpisodeFilter[];
|
||||||
Status?: ItemFilter[];
|
Status?: ItemFilter[];
|
||||||
EpisodesStatus?: string[];
|
EpisodesStatus?: string[];
|
||||||
SeriesStatus?: SeriesStatus[];
|
SeriesStatus?: SeriesStatus[];
|
||||||
StudioIds?: string[];
|
StudioIds?: string[];
|
||||||
Tags?: string[];
|
Tags?: string[];
|
||||||
|
VideoBasicFilter?: VideoBasicFilter[];
|
||||||
VideoTypes?: VideoType[];
|
VideoTypes?: VideoType[];
|
||||||
Years?: number[];
|
Years?: number[];
|
||||||
}
|
}
|
||||||
|
@ -39,10 +62,6 @@ export interface LibraryViewSettings {
|
||||||
ShowTitle: boolean;
|
ShowTitle: boolean;
|
||||||
ShowYear?: boolean;
|
ShowYear?: boolean;
|
||||||
Filters?: Filters;
|
Filters?: Filters;
|
||||||
IsSD?: boolean;
|
|
||||||
IsHD?: boolean;
|
|
||||||
Is4K?: boolean;
|
|
||||||
Is3D?: boolean;
|
|
||||||
NameLessThan?: string | null;
|
NameLessThan?: string | null;
|
||||||
NameStartsWith?: string | null;
|
NameStartsWith?: string | null;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue