import Button from '@app/components/Common/Button'; import LoadingSpinner from '@app/components/Common/LoadingSpinner'; import NotificationTypeSelector from '@app/components/NotificationTypeSelector'; import globalMessages from '@app/i18n/globalMessages'; import defineMessages from '@app/utils/defineMessages'; import { ArrowDownOnSquareIcon, BeakerIcon } from '@heroicons/react/24/outline'; import type { PushoverSound } from '@server/api/pushover'; import { Field, Form, Formik } from 'formik'; import { useState } from 'react'; import { useIntl } from 'react-intl'; import { useToasts } from 'react-toast-notifications'; import useSWR from 'swr'; import * as Yup from 'yup'; const messages = defineMessages( 'components.Settings.Notifications.NotificationsPushover', { agentenabled: 'Enable Agent', accessToken: 'Application API Token', accessTokenTip: 'Register an application for use with Jellyseerr', userToken: 'User or Group Key', userTokenTip: 'Your 30-character user or group identifier', sound: 'Notification Sound', deviceDefault: 'Device Default', validationAccessTokenRequired: 'You must provide a valid application token', validationUserTokenRequired: 'You must provide a valid user or group key', pushoversettingssaved: 'Pushover notification settings saved successfully!', pushoversettingsfailed: 'Pushover notification settings failed to save.', toastPushoverTestSending: 'Sending Pushover test notification…', toastPushoverTestSuccess: 'Pushover test notification sent!', toastPushoverTestFailed: 'Pushover test notification failed to send.', validationTypes: 'You must select at least one notification type', } ); const NotificationsPushover = () => { const intl = useIntl(); const { addToast, removeToast } = useToasts(); const [isTesting, setIsTesting] = useState(false); const { data, error, mutate: revalidate, } = useSWR('/api/v1/settings/notifications/pushover'); const { data: soundsData } = useSWR( data?.options.accessToken ? `/api/v1/settings/notifications/pushover/sounds?token=${data.options.accessToken}` : null ); const NotificationsPushoverSchema = Yup.object().shape({ accessToken: Yup.string() .when('enabled', { is: true, then: Yup.string() .nullable() .required(intl.formatMessage(messages.validationAccessTokenRequired)), otherwise: Yup.string().nullable(), }) .matches( /^[a-z\d]{30}$/i, intl.formatMessage(messages.validationAccessTokenRequired) ), userToken: Yup.string() .when('enabled', { is: true, then: Yup.string() .nullable() .required(intl.formatMessage(messages.validationUserTokenRequired)), otherwise: Yup.string().nullable(), }) .matches( /^[a-z\d]{30}$/i, intl.formatMessage(messages.validationUserTokenRequired) ), }); if (!data && !error) { return ; } return ( { try { const res = await fetch('/api/v1/settings/notifications/pushover', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ enabled: values.enabled, types: values.types, options: { accessToken: values.accessToken, userToken: values.userToken, }, }), }); if (!res.ok) throw new Error(); addToast(intl.formatMessage(messages.pushoversettingssaved), { appearance: 'success', autoDismiss: true, }); } catch (e) { addToast(intl.formatMessage(messages.pushoversettingsfailed), { appearance: 'error', autoDismiss: true, }); } finally { revalidate(); } }} > {({ errors, touched, isSubmitting, values, isValid, setFieldValue, setFieldTouched, }) => { const testSettings = async () => { setIsTesting(true); let toastId: string | undefined; try { addToast( intl.formatMessage(messages.toastPushoverTestSending), { autoDismiss: false, appearance: 'info', }, (id) => { toastId = id; } ); const res = await fetch( '/api/v1/settings/notifications/pushover/test', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ enabled: true, types: values.types, options: { accessToken: values.accessToken, userToken: values.userToken, sound: values.sound, }, }), } ); if (!res.ok) throw new Error(); if (toastId) { removeToast(toastId); } addToast(intl.formatMessage(messages.toastPushoverTestSuccess), { autoDismiss: true, appearance: 'success', }); } catch (e) { if (toastId) { removeToast(toastId); } addToast(intl.formatMessage(messages.toastPushoverTestFailed), { autoDismiss: true, appearance: 'error', }); } finally { setIsTesting(false); } }; return (
{errors.accessToken && touched.accessToken && typeof errors.accessToken === 'string' && (
{errors.accessToken}
)}
{errors.userToken && touched.userToken && typeof errors.userToken === 'string' && (
{errors.userToken}
)}
{soundsData?.map((sound, index) => ( ))}
{ setFieldValue('types', newTypes); setFieldTouched('types'); if (newTypes) { setFieldValue('enabled', true); } }} error={ values.enabled && !values.types && touched.types ? intl.formatMessage(messages.validationTypes) : undefined } />
); }}
); }; export default NotificationsPushover;