From 64abf91959d7f4277933d44f17baa242ddaa95d3 Mon Sep 17 00:00:00 2001 From: Brian Egizi Date: Thu, 26 Feb 2026 13:53:35 -0800 Subject: [PATCH 1/8] feat(settings): add enableVoting option to settings and permissions --- server/interfaces/api/settingsInterfaces.ts | 1 + server/lib/settings/index.ts | 6 ++++- src/components/PermissionEdit/index.tsx | 9 +++++++ .../Settings/SettingsMain/index.tsx | 25 +++++++++++++++++++ src/context/SettingsContext.tsx | 1 + src/pages/_app.tsx | 1 + 6 files changed, 42 insertions(+), 1 deletion(-) diff --git a/server/interfaces/api/settingsInterfaces.ts b/server/interfaces/api/settingsInterfaces.ts index ea08d4e61d..535866d52b 100644 --- a/server/interfaces/api/settingsInterfaces.ts +++ b/server/interfaces/api/settingsInterfaces.ts @@ -41,6 +41,7 @@ export interface PublicSettingsResponse { mediaServerType: number; partialRequestsEnabled: boolean; enableSpecialEpisodes: boolean; + enableVoting: boolean; cacheImages: boolean; vapidPublic: string; enablePushRegistration: boolean; diff --git a/server/lib/settings/index.ts b/server/lib/settings/index.ts index 7057cf2b01..e2ced743c1 100644 --- a/server/lib/settings/index.ts +++ b/server/lib/settings/index.ts @@ -145,6 +145,7 @@ export interface MainSettings { mediaServerType: number; partialRequestsEnabled: boolean; enableSpecialEpisodes: boolean; + enableVoting: boolean; locale: string; youtubeUrl: string; } @@ -197,6 +198,7 @@ interface FullPublicSettings extends PublicSettings { jellyfinServerName?: string; partialRequestsEnabled: boolean; enableSpecialEpisodes: boolean; + enableVoting: boolean; cacheImages: boolean; vapidPublic: string; enablePushRegistration: boolean; @@ -387,7 +389,7 @@ class Settings { applicationTitle: 'Seerr', applicationUrl: '', cacheImages: false, - defaultPermissions: Permission.REQUEST, + defaultPermissions: Permission.REQUEST | Permission.VOTE, defaultQuotas: { movie: {}, tv: {}, @@ -405,6 +407,7 @@ class Settings { mediaServerType: MediaServerType.NOT_CONFIGURED, partialRequestsEnabled: true, enableSpecialEpisodes: false, + enableVoting: false, locale: 'en', youtubeUrl: '', }, @@ -694,6 +697,7 @@ class Settings { mediaServerType: this.main.mediaServerType, partialRequestsEnabled: this.data.main.partialRequestsEnabled, enableSpecialEpisodes: this.data.main.enableSpecialEpisodes, + enableVoting: this.data.main.enableVoting, cacheImages: this.data.main.cacheImages, vapidPublic: this.vapidPublic, enablePushRegistration: this.data.notifications.agents.webpush.enabled, diff --git a/src/components/PermissionEdit/index.tsx b/src/components/PermissionEdit/index.tsx index cbba176cba..e1dc2bd3a8 100644 --- a/src/components/PermissionEdit/index.tsx +++ b/src/components/PermissionEdit/index.tsx @@ -19,6 +19,9 @@ export const messages = defineMessages('components.PermissionEdit', { 'Grant permission to manage media requests. All requests made by a user with this permission will be automatically approved.', request: 'Request', requestDescription: 'Grant permission to submit requests for non-4K media.', + vote: 'Vote', + voteDescription: + 'Grant permission to express interest in media without creating requests.', requestMovies: 'Request Movies', requestMoviesDescription: 'Grant permission to submit requests for non-4K movies.', @@ -184,6 +187,12 @@ export const PermissionEdit = ({ }, ], }, + { + id: 'vote', + name: intl.formatMessage(messages.vote), + description: intl.formatMessage(messages.voteDescription), + permission: Permission.VOTE, + }, { id: 'autoapprove', name: intl.formatMessage(messages.autoapprove), diff --git a/src/components/Settings/SettingsMain/index.tsx b/src/components/Settings/SettingsMain/index.tsx index 4944c2349f..919f092513 100644 --- a/src/components/Settings/SettingsMain/index.tsx +++ b/src/components/Settings/SettingsMain/index.tsx @@ -64,6 +64,9 @@ const messages = defineMessages('components.Settings.SettingsMain', { validationApplicationUrlTrailingSlash: 'URL must not end in a trailing slash', partialRequestsEnabled: 'Allow Partial Series Requests', enableSpecialEpisodes: 'Allow Special Episodes Requests', + enableVoting: 'Enable Voting', + enableVotingTip: + 'Allow users with the Vote permission to submit interested or not interested signals', locale: 'Display Language', youtubeUrl: 'YouTube URL', youtubeUrlTip: @@ -173,6 +176,7 @@ const SettingsMain = () => { blocklistedTagsLimit: data?.blocklistedTagsLimit || 50, partialRequestsEnabled: data?.partialRequestsEnabled, enableSpecialEpisodes: data?.enableSpecialEpisodes, + enableVoting: data?.enableVoting, cacheImages: data?.cacheImages, youtubeUrl: data?.youtubeUrl, }} @@ -193,6 +197,7 @@ const SettingsMain = () => { blocklistedTagsLimit: values.blocklistedTagsLimit, partialRequestsEnabled: values.partialRequestsEnabled, enableSpecialEpisodes: values.enableSpecialEpisodes, + enableVoting: values.enableVoting, cacheImages: values.cacheImages, youtubeUrl: values.youtubeUrl, }); @@ -489,6 +494,26 @@ const SettingsMain = () => { /> +
+ +
+ { + setFieldValue('enableVoting', !values.enableVoting); + }} + /> +
+