diff --git a/overseerr-api.yml b/overseerr-api.yml index c48b6575f7..167d3e9762 100644 --- a/overseerr-api.yml +++ b/overseerr-api.yml @@ -4263,6 +4263,11 @@ paths: schema: type: string example: 18 + - in: query + name: filterGenre + schema: + type: string + example: 28 - in: query name: studio schema: @@ -4552,6 +4557,11 @@ paths: schema: type: string example: 18 + - in: query + name: filterGenre + schema: + type: string + example: 28 - in: query name: network schema: diff --git a/server/api/themoviedb/index.ts b/server/api/themoviedb/index.ts index 6bf090b0ff..ab61b19dbd 100644 --- a/server/api/themoviedb/index.ts +++ b/server/api/themoviedb/index.ts @@ -69,6 +69,7 @@ interface DiscoverMovieOptions { voteCountLte?: string; originalLanguage?: string; genre?: string; + filterGenre?: string; studio?: string; keywords?: string; sortBy?: SortOptions; @@ -90,6 +91,7 @@ interface DiscoverTvOptions { includeEmptyReleaseDate?: boolean; originalLanguage?: string; genre?: string; + filterGenre?: string; network?: number; keywords?: string; sortBy?: SortOptions; @@ -460,6 +462,7 @@ class TheMovieDb extends ExternalAPI { primaryReleaseDateLte, originalLanguage, genre, + filterGenre, studio, keywords, withRuntimeGte, @@ -506,6 +509,7 @@ class TheMovieDb extends ExternalAPI { ? defaultFutureDate : primaryReleaseDateLte, with_genres: genre, + without_genres: filterGenre, with_companies: studio, with_keywords: keywords, 'with_runtime.gte': withRuntimeGte, @@ -534,6 +538,7 @@ class TheMovieDb extends ExternalAPI { includeEmptyReleaseDate = false, originalLanguage, genre, + filterGenre, network, keywords, withRuntimeGte, @@ -580,6 +585,7 @@ class TheMovieDb extends ExternalAPI { : this.originalLanguage, include_null_first_air_dates: includeEmptyReleaseDate, with_genres: genre, + without_genres: filterGenre, with_networks: network, with_keywords: keywords, 'with_runtime.gte': withRuntimeGte, diff --git a/server/entity/UserSettings.ts b/server/entity/UserSettings.ts index ea4a7d33bf..a4394a9887 100644 --- a/server/entity/UserSettings.ts +++ b/server/entity/UserSettings.ts @@ -36,6 +36,12 @@ export class UserSettings { @Column({ nullable: true }) public originalLanguage?: string; + @Column({ nullable: true }) + public filterTvGenresDefault?: string; + + @Column({ nullable: true }) + public filterMovieGenresDefault?: string; + @Column({ nullable: true }) public pgpKey?: string; diff --git a/server/interfaces/api/settingsInterfaces.ts b/server/interfaces/api/settingsInterfaces.ts index 0cd2f171ae..b02e466894 100644 --- a/server/interfaces/api/settingsInterfaces.ts +++ b/server/interfaces/api/settingsInterfaces.ts @@ -30,6 +30,8 @@ export interface PublicSettingsResponse { series4kEnabled: boolean; region: string; originalLanguage: string; + filterMovieGenresDefault: string; + filterTvGenresDefault: string; partialRequestsEnabled: boolean; cacheImages: boolean; vapidPublic: string; diff --git a/server/interfaces/api/userSettingsInterfaces.ts b/server/interfaces/api/userSettingsInterfaces.ts index fb0767b211..daa28ad1b7 100644 --- a/server/interfaces/api/userSettingsInterfaces.ts +++ b/server/interfaces/api/userSettingsInterfaces.ts @@ -6,6 +6,8 @@ export interface UserSettingsGeneralResponse { locale?: string; region?: string; originalLanguage?: string; + filterMovieGenresDefault?: string; + filterTvGenresDefault?: string; movieQuotaLimit?: number; movieQuotaDays?: number; tvQuotaLimit?: number; diff --git a/server/lib/settings.ts b/server/lib/settings.ts index 63daf17f36..616b3da54f 100644 --- a/server/lib/settings.ts +++ b/server/lib/settings.ts @@ -101,6 +101,8 @@ export interface MainSettings { newPlexLogin: boolean; region: string; originalLanguage: string; + filterTvGenresDefault: string; + filterMovieGenresDefault: string; trustProxy: boolean; partialRequestsEnabled: boolean; locale: string; @@ -298,6 +300,8 @@ class Settings { newPlexLogin: true, region: '', originalLanguage: '', + filterTvGenresDefault: '', + filterMovieGenresDefault: '', trustProxy: false, partialRequestsEnabled: true, locale: 'en', diff --git a/server/routes/discover.ts b/server/routes/discover.ts index b35306446f..c23883a7e7 100644 --- a/server/routes/discover.ts +++ b/server/routes/discover.ts @@ -59,6 +59,7 @@ const QueryFilterOptions = z.object({ firstAirDateLte: z.coerce.string().optional(), studio: z.coerce.string().optional(), genre: z.coerce.string().optional(), + filterGenre: z.coerce.string().optional(), keywords: z.coerce.string().optional(), language: z.coerce.string().optional(), withRuntimeGte: z.coerce.string().optional(), @@ -80,12 +81,36 @@ discoverRoutes.get('/movies', async (req, res, next) => { try { const query = QueryFilterOptions.parse(req.query); const keywords = query.keywords; + + // Handle user default excluded genres, resolve genres + let filterGenre = query.filterGenre; + if (filterGenre === 'none') { + filterGenre = undefined; + } else if ( + filterGenre === undefined && + req.user?.settings?.filterMovieGenresDefault + ) { + filterGenre = req.user.settings.filterMovieGenresDefault; + } + if (query.genre && filterGenre) { + const explicitGenres = query.genre.split(','); + const excludedGenres = filterGenre.split(','); + const resolvedExclusions = excludedGenres.filter( + (id) => !explicitGenres.includes(id) + ); + filterGenre = + resolvedExclusions.length > 0 + ? resolvedExclusions.join(',') + : undefined; + } + const data = await tmdb.getDiscoverMovies({ page: Number(query.page), sortBy: query.sortBy as SortOptions, language: req.locale ?? query.language, originalLanguage: query.language, genre: query.genre, + filterGenre, studio: query.studio, primaryReleaseDateLte: query.primaryReleaseDateLte ? new Date(query.primaryReleaseDateLte).toISOString().split('T')[0] @@ -357,11 +382,35 @@ discoverRoutes.get('/tv', async (req, res, next) => { try { const query = QueryFilterOptions.parse(req.query); const keywords = query.keywords; + + // Handle user default excluded genres, resolve genres + let filterGenre = query.filterGenre; + if (filterGenre === 'none') { + filterGenre = undefined; + } else if ( + filterGenre === undefined && + req.user?.settings?.filterTvGenresDefault + ) { + filterGenre = req.user.settings.filterTvGenresDefault; + } + if (query.genre && filterGenre) { + const explicitGenres = query.genre.split(','); + const excludedGenres = filterGenre.split(','); + const resolvedExclusions = excludedGenres.filter( + (id) => !explicitGenres.includes(id) + ); + filterGenre = + resolvedExclusions.length > 0 + ? resolvedExclusions.join(',') + : undefined; + } + const data = await tmdb.getDiscoverTv({ page: Number(query.page), sortBy: query.sortBy as SortOptions, language: req.locale ?? query.language, genre: query.genre, + filterGenre, network: query.network ? Number(query.network) : undefined, firstAirDateLte: query.firstAirDateLte ? new Date(query.firstAirDateLte).toISOString().split('T')[0] diff --git a/server/routes/user/usersettings.ts b/server/routes/user/usersettings.ts index c8b3f50bd2..9c782e47e2 100644 --- a/server/routes/user/usersettings.ts +++ b/server/routes/user/usersettings.ts @@ -55,6 +55,8 @@ userSettingsRoutes.get<{ id: string }, UserSettingsGeneralResponse>( locale: user.settings?.locale, region: user.settings?.region, originalLanguage: user.settings?.originalLanguage, + filterTvGenresDefault: user.settings?.filterTvGenresDefault, + filterMovieGenresDefault: user.settings?.filterMovieGenresDefault, movieQuotaLimit: user.movieQuotaLimit, movieQuotaDays: user.movieQuotaDays, tvQuotaLimit: user.tvQuotaLimit, @@ -116,6 +118,8 @@ userSettingsRoutes.post< locale: req.body.locale, region: req.body.region, originalLanguage: req.body.originalLanguage, + filterMovieGenresDefault: req.body.filterMovieGenresDefault, + filterTvGenresDefault: req.body.filterTvGenresDefault, watchlistSyncMovies: req.body.watchlistSyncMovies, watchlistSyncTv: req.body.watchlistSyncTv, }); @@ -124,6 +128,9 @@ userSettingsRoutes.post< user.settings.locale = req.body.locale; user.settings.region = req.body.region; user.settings.originalLanguage = req.body.originalLanguage; + user.settings.filterMovieGenresDefault = + req.body.filterMovieGenresDefault; + user.settings.filterTvGenresDefault = req.body.filterTvGenresDefault; user.settings.watchlistSyncMovies = req.body.watchlistSyncMovies; user.settings.watchlistSyncTv = req.body.watchlistSyncTv; } @@ -136,6 +143,8 @@ userSettingsRoutes.post< locale: user.settings.locale, region: user.settings.region, originalLanguage: user.settings.originalLanguage, + filterMovieGenresDefault: user.settings.filterMovieGenresDefault, + filterTvGenresDefault: user.settings.filterTvGenresDefault, watchlistSyncMovies: user.settings.watchlistSyncMovies, watchlistSyncTv: user.settings.watchlistSyncTv, }); diff --git a/src/components/Discover/DiscoverMovies/index.tsx b/src/components/Discover/DiscoverMovies/index.tsx index 2cc5117707..37d097de7c 100644 --- a/src/components/Discover/DiscoverMovies/index.tsx +++ b/src/components/Discover/DiscoverMovies/index.tsx @@ -10,6 +10,7 @@ import { import FilterSlideover from '@app/components/Discover/FilterSlideover'; import useDiscover from '@app/hooks/useDiscover'; import { useUpdateQueryParams } from '@app/hooks/useUpdateQueryParams'; +import { useUser } from '@app/hooks/useUser'; import Error from '@app/pages/_error'; import { BarsArrowDownIcon, FunnelIcon } from '@heroicons/react/24/solid'; import type { SortOptions as TMDBSortOptions } from '@server/api/themoviedb'; @@ -47,6 +48,7 @@ const DiscoverMovies = () => { const intl = useIntl(); const router = useRouter(); const updateQueryParams = useUpdateQueryParams({}); + const { user } = useUser(); const preparedFilters = prepareFilterValues(router.query); @@ -124,7 +126,10 @@ const DiscoverMovies = () => { {intl.formatMessage(messages.activefilters, { - count: countActiveFilters(preparedFilters), + count: countActiveFilters( + preparedFilters, + !!user?.settings?.filterMovieGenresDefault + ), })} diff --git a/src/components/Discover/DiscoverTv/index.tsx b/src/components/Discover/DiscoverTv/index.tsx index 527393676b..bca28ec969 100644 --- a/src/components/Discover/DiscoverTv/index.tsx +++ b/src/components/Discover/DiscoverTv/index.tsx @@ -10,6 +10,7 @@ import { import FilterSlideover from '@app/components/Discover/FilterSlideover'; import useDiscover from '@app/hooks/useDiscover'; import { useUpdateQueryParams } from '@app/hooks/useUpdateQueryParams'; +import { useUser } from '@app/hooks/useUser'; import Error from '@app/pages/_error'; import { BarsArrowDownIcon, FunnelIcon } from '@heroicons/react/24/solid'; import type { SortOptions as TMDBSortOptions } from '@server/api/themoviedb'; @@ -46,6 +47,7 @@ const SortOptions: Record = { const DiscoverTv = () => { const intl = useIntl(); const router = useRouter(); + const { user } = useUser(); const [showFilters, setShowFilters] = useState(false); const preparedFilters = prepareFilterValues(router.query); const updateQueryParams = useUpdateQueryParams({}); @@ -122,7 +124,10 @@ const DiscoverTv = () => { {intl.formatMessage(messages.activefilters, { - count: countActiveFilters(preparedFilters), + count: countActiveFilters( + preparedFilters, + !!user?.settings?.filterTvGenresDefault + ), })} diff --git a/src/components/Discover/FilterSlideover/index.tsx b/src/components/Discover/FilterSlideover/index.tsx index 83d5a2e49a..af0018735c 100644 --- a/src/components/Discover/FilterSlideover/index.tsx +++ b/src/components/Discover/FilterSlideover/index.tsx @@ -15,6 +15,8 @@ import { useBatchUpdateQueryParams, useUpdateQueryParams, } from '@app/hooks/useUpdateQueryParams'; +import { useUser } from '@app/hooks/useUser'; +import { resolveGenreConflicts } from '@app/utils/genreHelpers'; import { XCircleIcon } from '@heroicons/react/24/outline'; import { defineMessages, useIntl } from 'react-intl'; import Datepicker from 'react-tailwindcss-datepicker-sct'; @@ -29,6 +31,7 @@ const messages = defineMessages({ to: 'To', studio: 'Studio', genres: 'Genres', + filterGenres: 'Exclude genres', keywords: 'Keywords', originalLanguage: 'Original Language', runtimeText: '{minValue}-{maxValue} minute runtime', @@ -56,6 +59,7 @@ const FilterSlideover = ({ }: FilterSlideoverProps) => { const intl = useIntl(); const { currentSettings } = useSettings(); + const { user } = useUser(); const updateQueryParams = useUpdateQueryParams({}); const batchUpdateQueryParams = useBatchUpdateQueryParams({}); @@ -64,12 +68,24 @@ const FilterSlideover = ({ const dateLte = type === 'movie' ? 'primaryReleaseDateLte' : 'firstAirDateLte'; + const userDefaultfilterGenres = + type === 'movie' + ? user?.settings?.filterMovieGenresDefault + : user?.settings?.filterTvGenresDefault; + + const filterGenresValue = + currentFilters.filterGenre !== undefined + ? currentFilters.filterGenre === 'none' + ? '' + : currentFilters.filterGenre + : userDefaultfilterGenres; + return ( onClose()} > @@ -146,7 +162,44 @@ const FilterSlideover = ({ defaultValue={currentFilters.genre} isMulti onChange={(value) => { - updateQueryParams('genre', value?.map((v) => v.value).join(',')); + const selectedGenres = value?.map((v) => v.value.toString()) || []; + const result = resolveGenreConflicts( + selectedGenres, + filterGenresValue + ); + + if (result.hasConflicts) { + batchUpdateQueryParams({ + genre: result.changingList, + filterGenre: result.otherList || 'none', + }); + } else { + updateQueryParams('genre', result.changingList); + } + }} + /> + + {intl.formatMessage(messages.filterGenres)} + + { + const filterGenres = value?.map((v) => v.value.toString()) || []; + const result = resolveGenreConflicts( + filterGenres, + currentFilters.genre + ); + + if (result.hasConflicts) { + batchUpdateQueryParams({ + genre: result.otherList, + filterGenre: result.changingList || 'none', + }); + } else { + updateQueryParams('filterGenre', result.changingList || 'none'); + } }} /> diff --git a/src/components/Discover/constants.ts b/src/components/Discover/constants.ts index 0571f1fc70..73ed3eea6a 100644 --- a/src/components/Discover/constants.ts +++ b/src/components/Discover/constants.ts @@ -98,6 +98,7 @@ export const QueryFilterOptions = z.object({ firstAirDateLte: z.string().optional(), studio: z.string().optional(), genre: z.string().optional(), + filterGenre: z.string().optional(), keywords: z.string().optional(), language: z.string().optional(), withRuntimeGte: z.string().optional(), @@ -147,6 +148,10 @@ export const prepareFilterValues = ( filterValues.genre = values.genre; } + if (values.filterGenre) { + filterValues.filterGenre = values.filterGenre; + } + if (values.keywords) { filterValues.keywords = values.keywords; } @@ -190,10 +195,19 @@ export const prepareFilterValues = ( return filterValues; }; -export const countActiveFilters = (filterValues: FilterOptions): number => { +export const countActiveFilters = ( + filterValues: FilterOptions, + userDefaultExcludedGenres?: boolean +): number => { let totalCount = 0; const clonedFilters = Object.assign({}, filterValues); + if (clonedFilters.filterGenre === 'none') { + delete clonedFilters.filterGenre; + } else if (!clonedFilters.filterGenre && userDefaultExcludedGenres) { + totalCount += 1; + } + if (clonedFilters.voteAverageGte || filterValues.voteAverageLte) { totalCount += 1; delete clonedFilters.voteAverageGte; diff --git a/src/components/Selector/index.tsx b/src/components/Selector/index.tsx index 7b21658723..6b9e5772e3 100644 --- a/src/components/Selector/index.tsx +++ b/src/components/Selector/index.tsx @@ -148,6 +148,7 @@ export const GenreSelector = ({ useEffect(() => { const loadDefaultGenre = async (): Promise => { if (!defaultValue) { + setDefaultDataValue([]); return; } @@ -184,21 +185,30 @@ export const GenreSelector = ({ ); }; + const handleChange = ( + value: MultiValue | SingleValue | null + ) => { + if (isMulti) { + setDefaultDataValue(value ? [...(value as MultiValue)] : []); + } else { + setDefaultDataValue(value ? [value as SingleVal] : []); + } + + // eslint-disable-next-line @typescript-eslint/no-explicit-any + onChange(value as any); + }; + return ( { - // eslint-disable-next-line @typescript-eslint/no-explicit-any - onChange(value as any); - }} + onChange={handleChange} /> ); }; diff --git a/src/components/Settings/SettingsMain/index.tsx b/src/components/Settings/SettingsMain/index.tsx index 62f26d49a2..fc2478906b 100644 --- a/src/components/Settings/SettingsMain/index.tsx +++ b/src/components/Settings/SettingsMain/index.tsx @@ -35,6 +35,8 @@ const messages = defineMessages({ regionTip: 'Filter content by regional availability', originallanguage: 'Discover Language', originallanguageTip: 'Filter content by original language', + filterMovieGenresDefault: 'Exclude movie genres by default', + filterTvGenresDefault: 'Exclude TV genres by default', toastApiKeySuccess: 'New API key generated successfully!', toastApiKeyFailure: 'Something went wrong while generating a new API key.', toastSettingsSuccess: 'Settings saved successfully!', diff --git a/src/components/UserProfile/UserSettings/UserGeneralSettings/index.tsx b/src/components/UserProfile/UserSettings/UserGeneralSettings/index.tsx index 842ea7af26..a3cb76de5f 100644 --- a/src/components/UserProfile/UserSettings/UserGeneralSettings/index.tsx +++ b/src/components/UserProfile/UserSettings/UserGeneralSettings/index.tsx @@ -5,6 +5,7 @@ import PageTitle from '@app/components/Common/PageTitle'; import LanguageSelector from '@app/components/LanguageSelector'; import QuotaSelector from '@app/components/QuotaSelector'; import RegionSelector from '@app/components/RegionSelector'; +import { GenreSelector } from '@app/components/Selector'; import type { AvailableLocale } from '@app/context/LanguageContext'; import { availableLanguages } from '@app/context/LanguageContext'; import useLocale from '@app/hooks/useLocale'; @@ -40,6 +41,8 @@ const messages = defineMessages({ regionTip: 'Filter content by regional availability', originallanguage: 'Discover Language', originallanguageTip: 'Filter content by original language', + filterMovieGenresDefault: 'Exclude movie genres by default', + filterTvGenresDefault: 'Exclude TV genres by default', movierequestlimit: 'Movie Request Limit', seriesrequestlimit: 'Series Request Limit', enableOverride: 'Override Global Limit', @@ -124,6 +127,8 @@ const UserGeneralSettings = () => { locale: data?.locale, region: data?.region, originalLanguage: data?.originalLanguage, + filterMovieGenresDefault: data?.filterMovieGenresDefault, + filterTvGenresDefault: data?.filterTvGenresDefault, movieQuotaLimit: data?.movieQuotaLimit, movieQuotaDays: data?.movieQuotaDays, tvQuotaLimit: data?.tvQuotaLimit, @@ -141,6 +146,8 @@ const UserGeneralSettings = () => { locale: values.locale, region: values.region, originalLanguage: values.originalLanguage, + filterMovieGenresDefault: values.filterMovieGenresDefault, + filterTvGenresDefault: values.filterTvGenresDefault, movieQuotaLimit: movieQuotaEnabled ? values.movieQuotaLimit : null, @@ -334,6 +341,51 @@ const UserGeneralSettings = () => { +
+ +
+
+ { + const genreIds = + value?.map((v) => v.value).join(',') || ''; + setFieldValue('filterMovieGenresDefault', genreIds); + }} + /> +
+
+
+
+ +
+
+ { + const genreIds = + value?.map((v) => v.value).join(',') || ''; + setFieldValue('filterTvGenresDefault', genreIds); + }} + /> +
+
+
{currentHasPermission(Permission.MANAGE_USERS) && !hasPermission(Permission.MANAGE_USERS) && ( <> diff --git a/src/context/SettingsContext.tsx b/src/context/SettingsContext.tsx index d50add4db7..fad347fccd 100644 --- a/src/context/SettingsContext.tsx +++ b/src/context/SettingsContext.tsx @@ -17,6 +17,8 @@ const defaultSettings = { series4kEnabled: false, region: '', originalLanguage: '', + filterMovieGenresDefault: '', + filterTvGenresDefault: '', partialRequestsEnabled: true, cacheImages: false, vapidPublic: '', diff --git a/src/hooks/useUser.ts b/src/hooks/useUser.ts index 192b3fe9da..ed8e4355f8 100644 --- a/src/hooks/useUser.ts +++ b/src/hooks/useUser.ts @@ -30,6 +30,8 @@ export interface UserSettings { region?: string; originalLanguage?: string; locale?: string; + filterMovieGenresDefault?: string; + filterTvGenresDefault?: string; notificationTypes: Partial; watchlistSyncMovies?: boolean; watchlistSyncTv?: boolean; diff --git a/src/i18n/locale/en.json b/src/i18n/locale/en.json index e9f3b41187..97f14920d9 100644 --- a/src/i18n/locale/en.json +++ b/src/i18n/locale/en.json @@ -63,6 +63,7 @@ "components.Discover.DiscoverWatchlist.watchlist": "Plex Watchlist", "components.Discover.FilterSlideover.activefilters": "{count, plural, one {# Active Filter} other {# Active Filters}}", "components.Discover.FilterSlideover.clearfilters": "Clear Active Filters", + "components.Discover.FilterSlideover.filterGenres": "Exclude genres", "components.Discover.FilterSlideover.filters": "Filters", "components.Discover.FilterSlideover.firstAirDate": "First Air Date", "components.Discover.FilterSlideover.from": "From", @@ -800,6 +801,8 @@ "components.Settings.SettingsMain.csrfProtection": "Enable CSRF Protection", "components.Settings.SettingsMain.csrfProtectionHoverTip": "Do NOT enable this setting unless you understand what you are doing!", "components.Settings.SettingsMain.csrfProtectionTip": "Set external API access to read-only (requires HTTPS)", + "components.Settings.SettingsMain.filterMovieGenresDefault": "Exclude movie genres by default", + "components.Settings.SettingsMain.filterTvGenresDefault": "Exclude TV genres by default", "components.Settings.SettingsMain.general": "General", "components.Settings.SettingsMain.generalsettings": "General Settings", "components.Settings.SettingsMain.generalsettingsDescription": "Configure global and default settings for Overseerr.", @@ -1085,6 +1088,8 @@ "components.UserProfile.UserSettings.UserGeneralSettings.discordIdTip": "The multi-digit ID number associated with your Discord user account", "components.UserProfile.UserSettings.UserGeneralSettings.displayName": "Display Name", "components.UserProfile.UserSettings.UserGeneralSettings.enableOverride": "Override Global Limit", + "components.UserProfile.UserSettings.UserGeneralSettings.filterMovieGenresDefault": "Exclude movie genres by default", + "components.UserProfile.UserSettings.UserGeneralSettings.filterTvGenresDefault": "Exclude TV genres by default", "components.UserProfile.UserSettings.UserGeneralSettings.general": "General", "components.UserProfile.UserSettings.UserGeneralSettings.generalsettings": "General Settings", "components.UserProfile.UserSettings.UserGeneralSettings.languageDefault": "Default ({language})", diff --git a/src/i18n/locale/nl.json b/src/i18n/locale/nl.json index f081b400a2..fc3a1483a1 100644 --- a/src/i18n/locale/nl.json +++ b/src/i18n/locale/nl.json @@ -1157,6 +1157,7 @@ "components.Discover.DiscoverTv.sortTitleDesc": "Titel (Z-A) aflopend", "components.Discover.FilterSlideover.activefilters": "{count, plural, one {# filter actief} other {# filters actief}}", "components.Discover.FilterSlideover.clearfilters": "Actieve filters wissen", + "components.Discover.FilterSlideover.filterGenres": "Filter genres", "components.Discover.FilterSlideover.filters": "Filters", "components.Discover.FilterSlideover.firstAirDate": "Eerste uitzenddatum", "components.Discover.FilterSlideover.from": "Van", diff --git a/src/pages/_app.tsx b/src/pages/_app.tsx index 091bb03c7d..5f192e8d6e 100644 --- a/src/pages/_app.tsx +++ b/src/pages/_app.tsx @@ -235,6 +235,8 @@ CoreApp.getInitialProps = async (initialProps) => { localLogin: true, region: '', originalLanguage: '', + filterMovieGenresDefault: '', + filterTvGenresDefault: '', partialRequestsEnabled: true, cacheImages: false, vapidPublic: '', diff --git a/src/utils/genreHelpers.ts b/src/utils/genreHelpers.ts new file mode 100644 index 0000000000..03dc1fe4a1 --- /dev/null +++ b/src/utils/genreHelpers.ts @@ -0,0 +1,26 @@ +export function resolveGenreConflicts( + changingList: string[], + otherList?: string +): { + changingList: string | undefined; + otherList: string | undefined; + hasConflicts: boolean; +} { + const hasConflicts = otherList + ? changingList.some((genre) => otherList.includes(genre)) + : false; + + const cleanedOtherList = + hasConflicts && otherList + ? otherList + .split(',') + .filter((id) => !changingList.includes(id)) + .join(',') || undefined + : otherList; + + return { + changingList: changingList.length > 0 ? changingList.join(',') : undefined, + otherList: cleanedOtherList, + hasConflicts, + }; +}