feat(blacklist): hide blacklisted items from discover pages for admins (#1601)

* feat(blacklist): add 'Hide Blacklisted Items' setting to general settings and UI

* feat(migration): add HideBlacklistedItems migration for PostgreSQL and SQLite

* feat(settings): add hideBlacklisted option to application settings

* feat(settings): add tooltips for hideAvailable and hideBlacklisted options in settings

* chore(migration): remove HideBlacklistedItems migration files for PostgreSQL and SQLite

* docs(settings): clarify description of 'Hide Blacklisted Items' setting to specify it affects all users

* docs(settings): update tooltip and description for 'Hide Blacklisted Items' to clarify it applies to all users, including administrators

* docs(settings): clarify 'Hide Blacklisted Items' functionality to specify it applies only to administrators with the "Manage Blacklist" permission

* fix(hooks): update permission check for 'Hide Blacklisted Items' to include 'Manage Blacklist'

* fix(settings): update tooltip for 'Hide Blacklisted Items' to clarify it applies to all users with the "Manage Blacklist" permission

* feat(settings): add experimental badge to settings tooltip for 'Hide Available' option
This commit is contained in:
Jam
2025-05-06 19:01:23 +01:00
committed by GitHub
parent 2e4d14698f
commit 185167a0a7
9 changed files with 65 additions and 2 deletions

View File

@@ -34,7 +34,7 @@ const Search = () => {
{
query: router.query.query,
},
{ hideAvailable: false }
{ hideAvailable: false, hideBlacklisted: false }
);
if (error) {

View File

@@ -45,11 +45,16 @@ const messages = defineMessages('components.Settings.SettingsMain', {
'The "Process Blacklisted Tags" job will blacklist this many pages into each sort. Larger numbers will create a more accurate blacklist, but use more space.',
streamingRegion: 'Streaming Region',
streamingRegionTip: 'Show streaming sites by regional availability',
hideBlacklisted: 'Hide Blacklisted Items',
hideBlacklistedTip:
'Hide blacklisted items from discover pages for all users with the "Manage Blacklist" permission',
toastApiKeySuccess: 'New API key generated successfully!',
toastApiKeyFailure: 'Something went wrong while generating a new API key.',
toastSettingsSuccess: 'Settings saved successfully!',
toastSettingsFailure: 'Something went wrong while saving settings.',
hideAvailable: 'Hide Available Media',
hideAvailableTip:
'Hide available media from the discover pages but not search results',
cacheImages: 'Enable Image Caching',
cacheImagesTip:
'Cache externally sourced images (requires a significant amount of disk space)',
@@ -145,6 +150,7 @@ const SettingsMain = () => {
applicationTitle: data?.applicationTitle,
applicationUrl: data?.applicationUrl,
hideAvailable: data?.hideAvailable,
hideBlacklisted: data?.hideBlacklisted,
locale: data?.locale ?? 'en',
discoverRegion: data?.discoverRegion,
originalLanguage: data?.originalLanguage,
@@ -163,6 +169,7 @@ const SettingsMain = () => {
applicationTitle: values.applicationTitle,
applicationUrl: values.applicationUrl,
hideAvailable: values.hideAvailable,
hideBlacklisted: values.hideBlacklisted,
locale: values.locale,
discoverRegion: values.discoverRegion,
streamingRegion: values.streamingRegion,
@@ -428,6 +435,9 @@ const SettingsMain = () => {
{intl.formatMessage(messages.hideAvailable)}
</span>
<SettingsBadge badgeType="experimental" />
<span className="label-tip">
{intl.formatMessage(messages.hideAvailableTip)}
</span>
</label>
<div className="form-input-area">
<Field
@@ -440,6 +450,29 @@ const SettingsMain = () => {
/>
</div>
</div>
<div className="form-row">
<label htmlFor="hideBlacklisted" className="checkbox-label">
<span className="mr-2">
{intl.formatMessage(messages.hideBlacklisted)}
</span>
<span className="label-tip">
{intl.formatMessage(messages.hideBlacklistedTip)}
</span>
</label>
<div className="form-input-area">
<Field
type="checkbox"
id="hideBlacklisted"
name="hideBlacklisted"
onChange={() => {
setFieldValue(
'hideBlacklisted',
!values.hideBlacklisted
);
}}
/>
</div>
</div>
<div className="form-row">
<label
htmlFor="partialRequestsEnabled"

View File

@@ -13,6 +13,7 @@ const defaultSettings = {
applicationTitle: 'Jellyseerr',
applicationUrl: '',
hideAvailable: false,
hideBlacklisted: false,
localLogin: true,
mediaServerLogin: true,
movie4kEnabled: false,

View File

@@ -1,6 +1,7 @@
import { MediaStatus } from '@server/constants/media';
import useSWRInfinite from 'swr/infinite';
import useSettings from './useSettings';
import { Permission, useUser } from './useUser';
export interface BaseSearchResult<T> {
page: number;
@@ -53,9 +54,10 @@ const useDiscover = <
>(
endpoint: string,
options?: O,
{ hideAvailable = true } = {}
{ hideAvailable = true, hideBlacklisted = true } = {}
): DiscoverResult<T, S> => {
const settings = useSettings();
const { hasPermission } = useUser();
const { data, error, size, setSize, isValidating, mutate } = useSWRInfinite<
BaseSearchResult<T> & S
>(
@@ -120,6 +122,18 @@ const useDiscover = <
);
}
if (
settings.currentSettings.hideBlacklisted &&
hideBlacklisted &&
hasPermission(Permission.MANAGE_BLACKLIST)
) {
titles = titles.filter(
(i) =>
(i.mediaType === 'movie' || i.mediaType === 'tv') &&
i.mediaInfo?.status !== MediaStatus.BLACKLISTED
);
}
const isEmpty = !isLoadingInitialData && titles?.length === 0;
const isReachingEnd =
isEmpty ||

View File

@@ -959,6 +959,9 @@
"components.Settings.SettingsMain.generalsettings": "General Settings",
"components.Settings.SettingsMain.generalsettingsDescription": "Configure global and default settings for Jellyseerr.",
"components.Settings.SettingsMain.hideAvailable": "Hide Available Media",
"components.Settings.SettingsMain.hideAvailableTip": "Hide available media from the discover pages but not search results",
"components.Settings.SettingsMain.hideBlacklisted": "Hide Blacklisted Items",
"components.Settings.SettingsMain.hideBlacklistedTip": "Hide blacklisted items from discover pages for all users with the \"Manage Blacklist\" permission",
"components.Settings.SettingsMain.locale": "Display Language",
"components.Settings.SettingsMain.originallanguage": "Discover Language",
"components.Settings.SettingsMain.originallanguageTip": "Filter content by original language",

View File

@@ -231,6 +231,7 @@ CoreApp.getInitialProps = async (initialProps) => {
applicationTitle: '',
applicationUrl: '',
hideAvailable: false,
hideBlacklisted: false,
movie4kEnabled: false,
series4kEnabled: false,
localLogin: true,