feat(notifications): make embedded posters optional (#1364)

* feat(notifications): make images optional

* fix(notifications): added en i18n config

* fix: prettify

* fix(notifications): added embedImage support for ntfy

* fix(frontend): update embedImage on form state change and submission

* fix(locale): updated locale for embedImage

* fix: renamed embedImage to embedPoster
This commit is contained in:
Ishan Jain
2025-09-08 17:41:31 +05:30
committed by GitHub
parent 6245dae3b3
commit 479be0daeb
18 changed files with 149 additions and 28 deletions

View File

@@ -109,7 +109,9 @@ class DiscordAgent
type: Notification, type: Notification,
payload: NotificationPayload payload: NotificationPayload
): DiscordRichEmbed { ): DiscordRichEmbed {
const { applicationUrl } = getSettings().main; const settings = getSettings();
const { applicationUrl } = settings.main;
const { embedPoster } = settings.notifications.agents.discord;
const appUrl = const appUrl =
applicationUrl || `http://localhost:${process.env.port || 5055}`; applicationUrl || `http://localhost:${process.env.port || 5055}`;
@@ -223,9 +225,11 @@ class DiscordAgent
} }
: undefined, : undefined,
fields, fields,
thumbnail: { thumbnail: embedPoster
? {
url: payload.image, url: payload.image,
}, }
: undefined,
}; };
} }

View File

@@ -48,7 +48,9 @@ class EmailAgent
recipientEmail: string, recipientEmail: string,
recipientName?: string recipientName?: string
): EmailOptions | undefined { ): EmailOptions | undefined {
const { applicationUrl, applicationTitle } = getSettings().main; const settings = getSettings();
const { applicationUrl, applicationTitle } = settings.main;
const { embedPoster } = settings.notifications.agents.email;
if (type === Notification.TEST_NOTIFICATION) { if (type === Notification.TEST_NOTIFICATION) {
return { return {
@@ -129,7 +131,7 @@ class EmailAgent
body, body,
mediaName: payload.subject, mediaName: payload.subject,
mediaExtra: payload.extra ?? [], mediaExtra: payload.extra ?? [],
imageUrl: payload.image, imageUrl: embedPoster ? payload.image : undefined,
timestamp: new Date().toTimeString(), timestamp: new Date().toTimeString(),
requestedBy: payload.request.requestedBy.displayName, requestedBy: payload.request.requestedBy.displayName,
actionUrl: applicationUrl actionUrl: applicationUrl
@@ -176,7 +178,7 @@ class EmailAgent
issueComment: payload.comment?.message, issueComment: payload.comment?.message,
mediaName: payload.subject, mediaName: payload.subject,
extra: payload.extra ?? [], extra: payload.extra ?? [],
imageUrl: payload.image, imageUrl: embedPoster ? payload.image : undefined,
timestamp: new Date().toTimeString(), timestamp: new Date().toTimeString(),
actionUrl: applicationUrl actionUrl: applicationUrl
? `${applicationUrl}/issues/${payload.issue.id}` ? `${applicationUrl}/issues/${payload.issue.id}`

View File

@@ -22,7 +22,9 @@ class NtfyAgent
} }
private buildPayload(type: Notification, payload: NotificationPayload) { private buildPayload(type: Notification, payload: NotificationPayload) {
const { applicationUrl } = getSettings().main; const settings = getSettings();
const { applicationUrl } = settings.main;
const { embedPoster } = settings.notifications.agents.ntfy;
const topic = this.getSettings().options.topic; const topic = this.getSettings().options.topic;
const priority = 3; const priority = 3;
@@ -72,7 +74,7 @@ class NtfyAgent
message += `\n\n**${extra.name}**\n${extra.value}`; message += `\n\n**${extra.name}**\n${extra.value}`;
} }
const attach = payload.image; const attach = embedPoster ? payload.image : undefined;
let click; let click;
if (applicationUrl && payload.media) { if (applicationUrl && payload.media) {

View File

@@ -78,7 +78,9 @@ class PushoverAgent
type: Notification, type: Notification,
payload: NotificationPayload payload: NotificationPayload
): Promise<Partial<PushoverPayload>> { ): Promise<Partial<PushoverPayload>> {
const { applicationUrl, applicationTitle } = getSettings().main; const settings = getSettings();
const { applicationUrl, applicationTitle } = settings.main;
const { embedPoster } = settings.notifications.agents.pushover;
const title = payload.event ?? payload.subject; const title = payload.event ?? payload.subject;
let message = payload.event ? `<b>${payload.subject}</b>` : ''; let message = payload.event ? `<b>${payload.subject}</b>` : '';
@@ -155,7 +157,7 @@ class PushoverAgent
let attachment_base64; let attachment_base64;
let attachment_type; let attachment_type;
if (payload.image) { if (embedPoster && payload.image) {
const imagePayload = await this.getImagePayload(payload.image); const imagePayload = await this.getImagePayload(payload.image);
if (imagePayload.attachment_base64 && imagePayload.attachment_type) { if (imagePayload.attachment_base64 && imagePayload.attachment_type) {
attachment_base64 = imagePayload.attachment_base64; attachment_base64 = imagePayload.attachment_base64;

View File

@@ -63,7 +63,9 @@ class SlackAgent
type: Notification, type: Notification,
payload: NotificationPayload payload: NotificationPayload
): SlackBlockEmbed { ): SlackBlockEmbed {
const { applicationUrl, applicationTitle } = getSettings().main; const settings = getSettings();
const { applicationUrl, applicationTitle } = settings.main;
const { embedPoster } = settings.notifications.agents.slack;
const fields: EmbedField[] = []; const fields: EmbedField[] = [];
@@ -159,7 +161,8 @@ class SlackAgent
type: 'mrkdwn', type: 'mrkdwn',
text: payload.message, text: payload.message,
}, },
accessory: payload.image accessory:
embedPoster && payload.image
? { ? {
type: 'image', type: 'image',
image_url: payload.image, image_url: payload.image,

View File

@@ -65,7 +65,9 @@ class TelegramAgent
type: Notification, type: Notification,
payload: NotificationPayload payload: NotificationPayload
): Partial<TelegramMessagePayload | TelegramPhotoPayload> { ): Partial<TelegramMessagePayload | TelegramPhotoPayload> {
const { applicationUrl, applicationTitle } = getSettings().main; const settings = getSettings();
const { applicationUrl, applicationTitle } = settings.main;
const { embedPoster } = settings.notifications.agents.telegram;
/* eslint-disable no-useless-escape */ /* eslint-disable no-useless-escape */
let message = `\*${this.escapeText( let message = `\*${this.escapeText(
@@ -142,7 +144,7 @@ class TelegramAgent
} }
/* eslint-enable */ /* eslint-enable */
return payload.image return embedPoster && payload.image
? { ? {
photo: payload.image, photo: payload.image,
caption: message, caption: message,
@@ -160,7 +162,7 @@ class TelegramAgent
): Promise<boolean> { ): Promise<boolean> {
const settings = this.getSettings(); const settings = this.getSettings();
const endpoint = `${this.baseUrl}bot${settings.options.botAPI}/${ const endpoint = `${this.baseUrl}bot${settings.options.botAPI}/${
payload.image ? 'sendPhoto' : 'sendMessage' settings.embedPoster && payload.image ? 'sendPhoto' : 'sendMessage'
}`; }`;
const notificationPayload = this.getNotificationPayload(type, payload); const notificationPayload = this.getNotificationPayload(type, payload);

View File

@@ -42,6 +42,8 @@ class WebPushAgent
type: Notification, type: Notification,
payload: NotificationPayload payload: NotificationPayload
): PushNotificationPayload { ): PushNotificationPayload {
const { embedPoster } = getSettings().notifications.agents.webpush;
const mediaType = payload.media const mediaType = payload.media
? payload.media.mediaType === MediaType.MOVIE ? payload.media.mediaType === MediaType.MOVIE
? 'movie' ? 'movie'
@@ -128,7 +130,7 @@ class WebPushAgent
notificationType: Notification[type], notificationType: Notification[type],
subject: payload.subject, subject: payload.subject,
message, message,
image: payload.image, image: embedPoster ? payload.image : undefined,
requestId: payload.request?.id, requestId: payload.request?.id,
actionUrl, actionUrl,
actionUrlTitle, actionUrlTitle,

View File

@@ -207,6 +207,7 @@ interface FullPublicSettings extends PublicSettings {
export interface NotificationAgentConfig { export interface NotificationAgentConfig {
enabled: boolean; enabled: boolean;
embedPoster: boolean;
types?: number; types?: number;
options: Record<string, unknown>; options: Record<string, unknown>;
} }
@@ -434,6 +435,7 @@ class Settings {
agents: { agents: {
email: { email: {
enabled: false, enabled: false,
embedPoster: true,
options: { options: {
userEmailRequired: false, userEmailRequired: false,
emailFrom: '', emailFrom: '',
@@ -448,6 +450,7 @@ class Settings {
}, },
discord: { discord: {
enabled: false, enabled: false,
embedPoster: true,
types: 0, types: 0,
options: { options: {
webhookUrl: '', webhookUrl: '',
@@ -457,6 +460,7 @@ class Settings {
}, },
slack: { slack: {
enabled: false, enabled: false,
embedPoster: true,
types: 0, types: 0,
options: { options: {
webhookUrl: '', webhookUrl: '',
@@ -464,6 +468,7 @@ class Settings {
}, },
telegram: { telegram: {
enabled: false, enabled: false,
embedPoster: true,
types: 0, types: 0,
options: { options: {
botAPI: '', botAPI: '',
@@ -474,6 +479,7 @@ class Settings {
}, },
pushbullet: { pushbullet: {
enabled: false, enabled: false,
embedPoster: false,
types: 0, types: 0,
options: { options: {
accessToken: '', accessToken: '',
@@ -481,6 +487,7 @@ class Settings {
}, },
pushover: { pushover: {
enabled: false, enabled: false,
embedPoster: true,
types: 0, types: 0,
options: { options: {
accessToken: '', accessToken: '',
@@ -490,6 +497,7 @@ class Settings {
}, },
webhook: { webhook: {
enabled: false, enabled: false,
embedPoster: true,
types: 0, types: 0,
options: { options: {
webhookUrl: '', webhookUrl: '',
@@ -499,10 +507,12 @@ class Settings {
}, },
webpush: { webpush: {
enabled: false, enabled: false,
embedPoster: true,
options: {}, options: {},
}, },
gotify: { gotify: {
enabled: false, enabled: false,
embedPoster: false,
types: 0, types: 0,
options: { options: {
url: '', url: '',
@@ -512,6 +522,7 @@ class Settings {
}, },
ntfy: { ntfy: {
enabled: false, enabled: false,
embedPoster: true,
types: 0, types: 0,
options: { options: {
url: '', url: '',

View File

@@ -270,6 +270,7 @@ notificationRoutes.get('/webhook', (_req, res) => {
const response: typeof webhookSettings = { const response: typeof webhookSettings = {
enabled: webhookSettings.enabled, enabled: webhookSettings.enabled,
embedPoster: webhookSettings.embedPoster,
types: webhookSettings.types, types: webhookSettings.types,
options: { options: {
...webhookSettings.options, ...webhookSettings.options,
@@ -291,6 +292,7 @@ notificationRoutes.post('/webhook', async (req, res, next) => {
settings.notifications.agents.webhook = { settings.notifications.agents.webhook = {
enabled: req.body.enabled, enabled: req.body.enabled,
embedPoster: req.body.embedPoster,
types: req.body.types, types: req.body.types,
options: { options: {
jsonPayload: Buffer.from(req.body.options.jsonPayload).toString( jsonPayload: Buffer.from(req.body.options.jsonPayload).toString(
@@ -321,6 +323,7 @@ notificationRoutes.post('/webhook/test', async (req, res, next) => {
const testBody = { const testBody = {
enabled: req.body.enabled, enabled: req.body.enabled,
embedPoster: req.body.embedPoster,
types: req.body.types, types: req.body.types,
options: { options: {
jsonPayload: Buffer.from(req.body.options.jsonPayload).toString( jsonPayload: Buffer.from(req.body.options.jsonPayload).toString(

View File

@@ -53,6 +53,7 @@ div(style='display: block; background-color: #111827; padding: 2.5rem 0;')
b(style='color: #9ca3af; font-weight: 700;') b(style='color: #9ca3af; font-weight: 700;')
| #{extra.name}&nbsp; | #{extra.name}&nbsp;
| #{extra.value} | #{extra.value}
if imageUrl
td(rowspan='2' style='width: 7rem;') td(rowspan='2' style='width: 7rem;')
a(style='display: block; width: 7rem; overflow: hidden; border-radius: .375rem;' href=actionUrl) a(style='display: block; width: 7rem; overflow: hidden; border-radius: .375rem;' href=actionUrl)
div(style='overflow: hidden; box-sizing: border-box; margin: 0px;') div(style='overflow: hidden; box-sizing: border-box; margin: 0px;')

View File

@@ -15,6 +15,7 @@ import * as Yup from 'yup';
const messages = defineMessages('components.Settings.Notifications', { const messages = defineMessages('components.Settings.Notifications', {
agentenabled: 'Enable Agent', agentenabled: 'Enable Agent',
embedPoster: 'Embed Poster',
botUsername: 'Bot Username', botUsername: 'Bot Username',
botAvatarUrl: 'Bot Avatar URL', botAvatarUrl: 'Bot Avatar URL',
webhookUrl: 'Webhook URL', webhookUrl: 'Webhook URL',
@@ -74,6 +75,7 @@ const NotificationsDiscord = () => {
<Formik <Formik
initialValues={{ initialValues={{
enabled: data.enabled, enabled: data.enabled,
embedPoster: data.embedPoster,
types: data.types, types: data.types,
botUsername: data?.options.botUsername, botUsername: data?.options.botUsername,
botAvatarUrl: data?.options.botAvatarUrl, botAvatarUrl: data?.options.botAvatarUrl,
@@ -86,6 +88,7 @@ const NotificationsDiscord = () => {
try { try {
await axios.post('/api/v1/settings/notifications/discord', { await axios.post('/api/v1/settings/notifications/discord', {
enabled: values.enabled, enabled: values.enabled,
embedPoster: values.embedPoster,
types: values.types, types: values.types,
options: { options: {
botUsername: values.botUsername, botUsername: values.botUsername,
@@ -135,6 +138,7 @@ const NotificationsDiscord = () => {
); );
await axios.post('/api/v1/settings/notifications/discord/test', { await axios.post('/api/v1/settings/notifications/discord/test', {
enabled: true, enabled: true,
embedPoster: values.embedPoster,
types: values.types, types: values.types,
options: { options: {
botUsername: values.botUsername, botUsername: values.botUsername,
@@ -176,6 +180,14 @@ const NotificationsDiscord = () => {
<Field type="checkbox" id="enabled" name="enabled" /> <Field type="checkbox" id="enabled" name="enabled" />
</div> </div>
</div> </div>
<div className="form-row">
<label htmlFor="embedPoster" className="checkbox-label">
{intl.formatMessage(messages.embedPoster)}
</label>
<div className="form-input-area">
<Field type="checkbox" id="embedPoster" name="embedPoster" />
</div>
</div>
<div className="form-row"> <div className="form-row">
<label htmlFor="name" className="text-label"> <label htmlFor="name" className="text-label">
{intl.formatMessage(messages.webhookUrl)} {intl.formatMessage(messages.webhookUrl)}

View File

@@ -17,6 +17,7 @@ const messages = defineMessages('components.Settings.Notifications', {
validationSmtpHostRequired: 'You must provide a valid hostname or IP address', validationSmtpHostRequired: 'You must provide a valid hostname or IP address',
validationSmtpPortRequired: 'You must provide a valid port number', validationSmtpPortRequired: 'You must provide a valid port number',
agentenabled: 'Enable Agent', agentenabled: 'Enable Agent',
embedPoster: 'Embed Poster',
userEmailRequired: 'Require user email', userEmailRequired: 'Require user email',
emailsender: 'Sender Address', emailsender: 'Sender Address',
smtpHost: 'SMTP Host', smtpHost: 'SMTP Host',
@@ -122,6 +123,7 @@ const NotificationsEmail = () => {
<Formik <Formik
initialValues={{ initialValues={{
enabled: data.enabled, enabled: data.enabled,
embedPoster: data.embedPoster,
userEmailRequired: data.options.userEmailRequired, userEmailRequired: data.options.userEmailRequired,
emailFrom: data.options.emailFrom, emailFrom: data.options.emailFrom,
smtpHost: data.options.smtpHost, smtpHost: data.options.smtpHost,
@@ -145,6 +147,7 @@ const NotificationsEmail = () => {
try { try {
await axios.post('/api/v1/settings/notifications/email', { await axios.post('/api/v1/settings/notifications/email', {
enabled: values.enabled, enabled: values.enabled,
embedPoster: values.embedPoster,
options: { options: {
userEmailRequired: values.userEmailRequired, userEmailRequired: values.userEmailRequired,
emailFrom: values.emailFrom, emailFrom: values.emailFrom,
@@ -194,6 +197,7 @@ const NotificationsEmail = () => {
); );
await axios.post('/api/v1/settings/notifications/email/test', { await axios.post('/api/v1/settings/notifications/email/test', {
enabled: true, enabled: true,
embedPoster: values.embedPoster,
options: { options: {
emailFrom: values.emailFrom, emailFrom: values.emailFrom,
smtpHost: values.smtpHost, smtpHost: values.smtpHost,
@@ -241,6 +245,14 @@ const NotificationsEmail = () => {
<Field type="checkbox" id="enabled" name="enabled" /> <Field type="checkbox" id="enabled" name="enabled" />
</div> </div>
</div> </div>
<div className="form-row">
<label htmlFor="embedPoster" className="checkbox-label">
{intl.formatMessage(messages.embedPoster)}
</label>
<div className="form-input-area">
<Field type="checkbox" id="embedPoster" name="embedPoster" />
</div>
</div>
<div className="form-row"> <div className="form-row">
<label htmlFor="userEmailRequired" className="checkbox-label"> <label htmlFor="userEmailRequired" className="checkbox-label">
{intl.formatMessage(messages.userEmailRequired)} {intl.formatMessage(messages.userEmailRequired)}

View File

@@ -19,6 +19,7 @@ const messages = defineMessages(
'components.Settings.Notifications.NotificationsNtfy', 'components.Settings.Notifications.NotificationsNtfy',
{ {
agentenabled: 'Enable Agent', agentenabled: 'Enable Agent',
embedPoster: 'Embed Poster',
url: 'Server root URL', url: 'Server root URL',
topic: 'Topic', topic: 'Topic',
usernamePasswordAuth: 'Username + Password authentication', usernamePasswordAuth: 'Username + Password authentication',
@@ -80,6 +81,7 @@ const NotificationsNtfy = () => {
<Formik <Formik
initialValues={{ initialValues={{
enabled: data?.enabled, enabled: data?.enabled,
embedPoster: data?.embedPoster,
types: data?.types, types: data?.types,
url: data?.options.url, url: data?.options.url,
topic: data?.options.topic, topic: data?.options.topic,
@@ -94,6 +96,7 @@ const NotificationsNtfy = () => {
try { try {
await axios.post('/api/v1/settings/notifications/ntfy', { await axios.post('/api/v1/settings/notifications/ntfy', {
enabled: values.enabled, enabled: values.enabled,
embedPoster: values.embedPoster,
types: values.types, types: values.types,
options: { options: {
url: values.url, url: values.url,
@@ -188,6 +191,14 @@ const NotificationsNtfy = () => {
<Field type="checkbox" id="enabled" name="enabled" /> <Field type="checkbox" id="enabled" name="enabled" />
</div> </div>
</div> </div>
<div className="form-row">
<label htmlFor="embedPoster" className="checkbox-label">
{intl.formatMessage(messages.embedPoster)}
</label>
<div className="form-input-area">
<Field type="checkbox" id="embedPoster" name="embedPoster" />
</div>
</div>
<div className="form-row"> <div className="form-row">
<label htmlFor="url" className="text-label"> <label htmlFor="url" className="text-label">
{intl.formatMessage(messages.url)} {intl.formatMessage(messages.url)}

View File

@@ -17,6 +17,7 @@ const messages = defineMessages(
'components.Settings.Notifications.NotificationsPushover', 'components.Settings.Notifications.NotificationsPushover',
{ {
agentenabled: 'Enable Agent', agentenabled: 'Enable Agent',
embedPoster: 'Embed Poster',
accessToken: 'Application API Token', accessToken: 'Application API Token',
accessTokenTip: accessTokenTip:
'<ApplicationRegistrationLink>Register an application</ApplicationRegistrationLink> for use with Jellyseerr', '<ApplicationRegistrationLink>Register an application</ApplicationRegistrationLink> for use with Jellyseerr',
@@ -86,6 +87,7 @@ const NotificationsPushover = () => {
<Formik <Formik
initialValues={{ initialValues={{
enabled: data?.enabled, enabled: data?.enabled,
embedPoster: data?.embedPoster,
types: data?.types, types: data?.types,
accessToken: data?.options.accessToken, accessToken: data?.options.accessToken,
userToken: data?.options.userToken, userToken: data?.options.userToken,
@@ -96,6 +98,7 @@ const NotificationsPushover = () => {
try { try {
await axios.post('/api/v1/settings/notifications/pushover', { await axios.post('/api/v1/settings/notifications/pushover', {
enabled: values.enabled, enabled: values.enabled,
embedPoster: values.embedPoster,
types: values.types, types: values.types,
options: { options: {
accessToken: values.accessToken, accessToken: values.accessToken,
@@ -142,6 +145,7 @@ const NotificationsPushover = () => {
); );
await axios.post('/api/v1/settings/notifications/pushover/test', { await axios.post('/api/v1/settings/notifications/pushover/test', {
enabled: true, enabled: true,
embedPoster: values.embedPoster,
types: values.types, types: values.types,
options: { options: {
accessToken: values.accessToken, accessToken: values.accessToken,
@@ -181,6 +185,14 @@ const NotificationsPushover = () => {
<Field type="checkbox" id="enabled" name="enabled" /> <Field type="checkbox" id="enabled" name="enabled" />
</div> </div>
</div> </div>
<div className="form-row">
<label htmlFor="embedPoster" className="checkbox-label">
{intl.formatMessage(messages.embedPoster)}
</label>
<div className="form-input-area">
<Field type="checkbox" id="embedPoster" name="embedPoster" />
</div>
</div>
<div className="form-row"> <div className="form-row">
<label htmlFor="accessToken" className="text-label"> <label htmlFor="accessToken" className="text-label">
{intl.formatMessage(messages.accessToken)} {intl.formatMessage(messages.accessToken)}

View File

@@ -16,6 +16,7 @@ const messages = defineMessages(
'components.Settings.Notifications.NotificationsSlack', 'components.Settings.Notifications.NotificationsSlack',
{ {
agentenabled: 'Enable Agent', agentenabled: 'Enable Agent',
embedPoster: 'Embed Poster',
webhookUrl: 'Webhook URL', webhookUrl: 'Webhook URL',
webhookUrlTip: webhookUrlTip:
'Create an <WebhookLink>Incoming Webhook</WebhookLink> integration', 'Create an <WebhookLink>Incoming Webhook</WebhookLink> integration',
@@ -59,6 +60,7 @@ const NotificationsSlack = () => {
<Formik <Formik
initialValues={{ initialValues={{
enabled: data.enabled, enabled: data.enabled,
embedPoster: data.embedPoster,
types: data.types, types: data.types,
webhookUrl: data.options.webhookUrl, webhookUrl: data.options.webhookUrl,
}} }}
@@ -67,6 +69,7 @@ const NotificationsSlack = () => {
try { try {
await axios.post('/api/v1/settings/notifications/slack', { await axios.post('/api/v1/settings/notifications/slack', {
enabled: values.enabled, enabled: values.enabled,
embedPoster: values.embedPoster,
types: values.types, types: values.types,
options: { options: {
webhookUrl: values.webhookUrl, webhookUrl: values.webhookUrl,
@@ -111,6 +114,7 @@ const NotificationsSlack = () => {
); );
await axios.post('/api/v1/settings/notifications/slack/test', { await axios.post('/api/v1/settings/notifications/slack/test', {
enabled: true, enabled: true,
embedPoster: values.embedPoster,
types: values.types, types: values.types,
options: { options: {
webhookUrl: values.webhookUrl, webhookUrl: values.webhookUrl,
@@ -148,6 +152,14 @@ const NotificationsSlack = () => {
<Field type="checkbox" id="enabled" name="enabled" /> <Field type="checkbox" id="enabled" name="enabled" />
</div> </div>
</div> </div>
<div className="form-row">
<label htmlFor="embedPoster" className="checkbox-label">
{intl.formatMessage(messages.embedPoster)}
</label>
<div className="form-input-area">
<Field type="checkbox" id="embedPoster" name="embedPoster" />
</div>
</div>
<div className="form-row"> <div className="form-row">
<label htmlFor="name" className="text-label"> <label htmlFor="name" className="text-label">
{intl.formatMessage(messages.webhookUrl)} {intl.formatMessage(messages.webhookUrl)}

View File

@@ -15,6 +15,7 @@ import * as Yup from 'yup';
const messages = defineMessages('components.Settings.Notifications', { const messages = defineMessages('components.Settings.Notifications', {
agentenabled: 'Enable Agent', agentenabled: 'Enable Agent',
embedPoster: 'Embed Poster',
botUsername: 'Bot Username', botUsername: 'Bot Username',
botUsernameTip: botUsernameTip:
'Allow users to also start a chat with your bot and configure their own notifications', 'Allow users to also start a chat with your bot and configure their own notifications',
@@ -89,6 +90,7 @@ const NotificationsTelegram = () => {
<Formik <Formik
initialValues={{ initialValues={{
enabled: data?.enabled, enabled: data?.enabled,
embedPoster: data?.embedPoster,
types: data?.types, types: data?.types,
botUsername: data?.options.botUsername, botUsername: data?.options.botUsername,
botAPI: data?.options.botAPI, botAPI: data?.options.botAPI,
@@ -101,6 +103,7 @@ const NotificationsTelegram = () => {
try { try {
await axios.post('/api/v1/settings/notifications/telegram', { await axios.post('/api/v1/settings/notifications/telegram', {
enabled: values.enabled, enabled: values.enabled,
embedPoster: values.embedPoster,
types: values.types, types: values.types,
options: { options: {
botAPI: values.botAPI, botAPI: values.botAPI,
@@ -191,6 +194,14 @@ const NotificationsTelegram = () => {
<Field type="checkbox" id="enabled" name="enabled" /> <Field type="checkbox" id="enabled" name="enabled" />
</div> </div>
</div> </div>
<div className="form-row">
<label htmlFor="embedPoster" className="checkbox-label">
{intl.formatMessage(messages.embedPoster)}
</label>
<div className="form-input-area">
<Field type="checkbox" id="embedPoster" name="embedPoster" />
</div>
</div>
<div className="form-row"> <div className="form-row">
<label htmlFor="botAPI" className="text-label"> <label htmlFor="botAPI" className="text-label">
{intl.formatMessage(messages.botAPI)} {intl.formatMessage(messages.botAPI)}

View File

@@ -15,6 +15,7 @@ const messages = defineMessages(
'components.Settings.Notifications.NotificationsWebPush', 'components.Settings.Notifications.NotificationsWebPush',
{ {
agentenabled: 'Enable Agent', agentenabled: 'Enable Agent',
embedPoster: 'Embed Poster',
webpushsettingssaved: 'Web push notification settings saved successfully!', webpushsettingssaved: 'Web push notification settings saved successfully!',
webpushsettingsfailed: 'Web push notification settings failed to save.', webpushsettingsfailed: 'Web push notification settings failed to save.',
toastWebPushTestSending: 'Sending web push test notification…', toastWebPushTestSending: 'Sending web push test notification…',
@@ -55,11 +56,13 @@ const NotificationsWebPush = () => {
<Formik <Formik
initialValues={{ initialValues={{
enabled: data.enabled, enabled: data.enabled,
embedPoster: data.embedPoster,
}} }}
onSubmit={async (values) => { onSubmit={async (values) => {
try { try {
await axios.post('/api/v1/settings/notifications/webpush', { await axios.post('/api/v1/settings/notifications/webpush', {
enabled: values.enabled, enabled: values.enabled,
embedPoster: values.embedPoster,
options: {}, options: {},
}); });
mutate('/api/v1/settings/public'); mutate('/api/v1/settings/public');
@@ -77,7 +80,7 @@ const NotificationsWebPush = () => {
} }
}} }}
> >
{({ isSubmitting }) => { {({ isSubmitting, values }) => {
const testSettings = async () => { const testSettings = async () => {
setIsTesting(true); setIsTesting(true);
let toastId: string | undefined; let toastId: string | undefined;
@@ -94,6 +97,7 @@ const NotificationsWebPush = () => {
); );
await axios.post('/api/v1/settings/notifications/webpush/test', { await axios.post('/api/v1/settings/notifications/webpush/test', {
enabled: true, enabled: true,
embedPoster: values.embedPoster,
options: {}, options: {},
}); });
@@ -128,6 +132,15 @@ const NotificationsWebPush = () => {
<Field type="checkbox" id="enabled" name="enabled" /> <Field type="checkbox" id="enabled" name="enabled" />
</div> </div>
</div> </div>
<div className="form-row">
<label htmlFor="embedPoster" className="checkbox-label">
{intl.formatMessage(messages.embedPoster)}
<span className="label-required">*</span>
</label>
<div className="form-input-area">
<Field type="checkbox" id="embedPoster" name="embedPoster" />
</div>
</div>
<div className="actions"> <div className="actions">
<div className="flex justify-end"> <div className="flex justify-end">
<span className="ml-3 inline-flex rounded-md shadow-sm"> <span className="ml-3 inline-flex rounded-md shadow-sm">

View File

@@ -624,6 +624,7 @@
"components.Settings.Notifications.NotificationsGotify.validationUrlRequired": "You must provide a valid URL", "components.Settings.Notifications.NotificationsGotify.validationUrlRequired": "You must provide a valid URL",
"components.Settings.Notifications.NotificationsGotify.validationUrlTrailingSlash": "URL must not end in a trailing slash", "components.Settings.Notifications.NotificationsGotify.validationUrlTrailingSlash": "URL must not end in a trailing slash",
"components.Settings.Notifications.NotificationsNtfy.agentenabled": "Enable Agent", "components.Settings.Notifications.NotificationsNtfy.agentenabled": "Enable Agent",
"components.Settings.Notifications.NotificationsNtfy.embedPoster": "Embed Poster",
"components.Settings.Notifications.NotificationsNtfy.ntfysettingsfailed": "Ntfy notification settings failed to save.", "components.Settings.Notifications.NotificationsNtfy.ntfysettingsfailed": "Ntfy notification settings failed to save.",
"components.Settings.Notifications.NotificationsNtfy.ntfysettingssaved": "Ntfy notification settings saved successfully!", "components.Settings.Notifications.NotificationsNtfy.ntfysettingssaved": "Ntfy notification settings saved successfully!",
"components.Settings.Notifications.NotificationsNtfy.password": "Password", "components.Settings.Notifications.NotificationsNtfy.password": "Password",
@@ -654,6 +655,7 @@
"components.Settings.Notifications.NotificationsPushover.accessTokenTip": "<ApplicationRegistrationLink>Register an application</ApplicationRegistrationLink> for use with Jellyseerr", "components.Settings.Notifications.NotificationsPushover.accessTokenTip": "<ApplicationRegistrationLink>Register an application</ApplicationRegistrationLink> for use with Jellyseerr",
"components.Settings.Notifications.NotificationsPushover.agentenabled": "Enable Agent", "components.Settings.Notifications.NotificationsPushover.agentenabled": "Enable Agent",
"components.Settings.Notifications.NotificationsPushover.deviceDefault": "Device Default", "components.Settings.Notifications.NotificationsPushover.deviceDefault": "Device Default",
"components.Settings.Notifications.NotificationsPushover.embedPoster": "Embed Poster",
"components.Settings.Notifications.NotificationsPushover.pushoversettingsfailed": "Pushover notification settings failed to save.", "components.Settings.Notifications.NotificationsPushover.pushoversettingsfailed": "Pushover notification settings failed to save.",
"components.Settings.Notifications.NotificationsPushover.pushoversettingssaved": "Pushover notification settings saved successfully!", "components.Settings.Notifications.NotificationsPushover.pushoversettingssaved": "Pushover notification settings saved successfully!",
"components.Settings.Notifications.NotificationsPushover.sound": "Notification Sound", "components.Settings.Notifications.NotificationsPushover.sound": "Notification Sound",
@@ -666,6 +668,7 @@
"components.Settings.Notifications.NotificationsPushover.validationTypes": "You must select at least one notification type", "components.Settings.Notifications.NotificationsPushover.validationTypes": "You must select at least one notification type",
"components.Settings.Notifications.NotificationsPushover.validationUserTokenRequired": "You must provide a valid user or group key", "components.Settings.Notifications.NotificationsPushover.validationUserTokenRequired": "You must provide a valid user or group key",
"components.Settings.Notifications.NotificationsSlack.agentenabled": "Enable Agent", "components.Settings.Notifications.NotificationsSlack.agentenabled": "Enable Agent",
"components.Settings.Notifications.NotificationsSlack.embedPoster": "Embed Poster",
"components.Settings.Notifications.NotificationsSlack.slacksettingsfailed": "Slack notification settings failed to save.", "components.Settings.Notifications.NotificationsSlack.slacksettingsfailed": "Slack notification settings failed to save.",
"components.Settings.Notifications.NotificationsSlack.slacksettingssaved": "Slack notification settings saved successfully!", "components.Settings.Notifications.NotificationsSlack.slacksettingssaved": "Slack notification settings saved successfully!",
"components.Settings.Notifications.NotificationsSlack.toastSlackTestFailed": "Slack test notification failed to send.", "components.Settings.Notifications.NotificationsSlack.toastSlackTestFailed": "Slack test notification failed to send.",
@@ -691,6 +694,7 @@
"components.Settings.Notifications.NotificationsWebhook.webhooksettingsfailed": "Webhook notification settings failed to save.", "components.Settings.Notifications.NotificationsWebhook.webhooksettingsfailed": "Webhook notification settings failed to save.",
"components.Settings.Notifications.NotificationsWebhook.webhooksettingssaved": "Webhook notification settings saved successfully!", "components.Settings.Notifications.NotificationsWebhook.webhooksettingssaved": "Webhook notification settings saved successfully!",
"components.Settings.Notifications.NotificationsWebPush.agentenabled": "Enable Agent", "components.Settings.Notifications.NotificationsWebPush.agentenabled": "Enable Agent",
"components.Settings.Notifications.NotificationsWebPush.embedPoster": "Embed Poster",
"components.Settings.Notifications.NotificationsWebPush.httpsRequirement": "In order to receive web push notifications, Jellyseerr must be served over HTTPS.", "components.Settings.Notifications.NotificationsWebPush.httpsRequirement": "In order to receive web push notifications, Jellyseerr must be served over HTTPS.",
"components.Settings.Notifications.NotificationsWebPush.toastWebPushTestFailed": "Web push test notification failed to send.", "components.Settings.Notifications.NotificationsWebPush.toastWebPushTestFailed": "Web push test notification failed to send.",
"components.Settings.Notifications.NotificationsWebPush.toastWebPushTestSending": "Sending web push test notification…", "components.Settings.Notifications.NotificationsWebPush.toastWebPushTestSending": "Sending web push test notification…",
@@ -713,6 +717,7 @@
"components.Settings.Notifications.emailsender": "Sender Address", "components.Settings.Notifications.emailsender": "Sender Address",
"components.Settings.Notifications.emailsettingsfailed": "Email notification settings failed to save.", "components.Settings.Notifications.emailsettingsfailed": "Email notification settings failed to save.",
"components.Settings.Notifications.emailsettingssaved": "Email notification settings saved successfully!", "components.Settings.Notifications.emailsettingssaved": "Email notification settings saved successfully!",
"components.Settings.Notifications.embedPoster": "Embed Poster",
"components.Settings.Notifications.enableMentions": "Enable Mentions", "components.Settings.Notifications.enableMentions": "Enable Mentions",
"components.Settings.Notifications.encryption": "Encryption Method", "components.Settings.Notifications.encryption": "Encryption Method",
"components.Settings.Notifications.encryptionDefault": "Use STARTTLS if available", "components.Settings.Notifications.encryptionDefault": "Use STARTTLS if available",
@@ -1158,6 +1163,7 @@
"components.Settings.menuServices": "Services", "components.Settings.menuServices": "Services",
"components.Settings.menuUsers": "Users", "components.Settings.menuUsers": "Users",
"components.Settings.metadataProviderSelection": "Metadata Provider Selection", "components.Settings.metadataProviderSelection": "Metadata Provider Selection",
"components.Settings.metadataProviderSettings": "Metadata Providers",
"components.Settings.metadataSettings": "Settings for metadata provider", "components.Settings.metadataSettings": "Settings for metadata provider",
"components.Settings.metadataSettingsSaved": "Metadata provider settings saved", "components.Settings.metadataSettingsSaved": "Metadata provider settings saved",
"components.Settings.no": "No", "components.Settings.no": "No",