Merge remote-tracking branch 'overseerr/develop' into develop
This commit is contained in:
@@ -1,30 +1,28 @@
|
||||
import Badge from '@app/components/Common/Badge';
|
||||
import Button from '@app/components/Common/Button';
|
||||
import LoadingSpinner from '@app/components/Common/LoadingSpinner';
|
||||
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 type { AvailableLocale } from '@app/context/LanguageContext';
|
||||
import { availableLanguages } from '@app/context/LanguageContext';
|
||||
import useLocale from '@app/hooks/useLocale';
|
||||
import useSettings from '@app/hooks/useSettings';
|
||||
import { Permission, UserType, useUser } from '@app/hooks/useUser';
|
||||
import globalMessages from '@app/i18n/globalMessages';
|
||||
import Error from '@app/pages/_error';
|
||||
import { SaveIcon } from '@heroicons/react/outline';
|
||||
import type { UserSettingsGeneralResponse } from '@server/interfaces/api/userSettingsInterfaces';
|
||||
import axios from 'axios';
|
||||
import { Field, Form, Formik } from 'formik';
|
||||
import getConfig from 'next/config';
|
||||
import { useRouter } from 'next/router';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { defineMessages, useIntl } from 'react-intl';
|
||||
import { useToasts } from 'react-toast-notifications';
|
||||
import useSWR from 'swr';
|
||||
import * as Yup from 'yup';
|
||||
import { UserSettingsGeneralResponse } from '../../../../../server/interfaces/api/userSettingsInterfaces';
|
||||
import {
|
||||
availableLanguages,
|
||||
AvailableLocale,
|
||||
} from '../../../../context/LanguageContext';
|
||||
import useLocale from '../../../../hooks/useLocale';
|
||||
import useSettings from '../../../../hooks/useSettings';
|
||||
import { Permission, UserType, useUser } from '../../../../hooks/useUser';
|
||||
import globalMessages from '../../../../i18n/globalMessages';
|
||||
import Error from '../../../../pages/_error';
|
||||
import Badge from '../../../Common/Badge';
|
||||
import Button from '../../../Common/Button';
|
||||
import LoadingSpinner from '../../../Common/LoadingSpinner';
|
||||
import PageTitle from '../../../Common/PageTitle';
|
||||
import LanguageSelector from '../../../LanguageSelector';
|
||||
import QuotaSelector from '../../../QuotaSelector';
|
||||
import RegionSelector from '../../../RegionSelector';
|
||||
import getConfig from 'next/config';
|
||||
|
||||
const messages = defineMessages({
|
||||
general: 'General',
|
||||
@@ -56,9 +54,15 @@ const messages = defineMessages({
|
||||
discordIdTip:
|
||||
'The <FindDiscordIdLink>multi-digit ID number</FindDiscordIdLink> associated with your Discord user account',
|
||||
validationDiscordId: 'You must provide a valid Discord user ID',
|
||||
plexwatchlistsyncmovies: 'Auto-Request Movies',
|
||||
plexwatchlistsyncmoviestip:
|
||||
'Automatically request movies on your <PlexWatchlistSupportLink>Plex Watchlist</PlexWatchlistSupportLink>',
|
||||
plexwatchlistsyncseries: 'Auto-Request Series',
|
||||
plexwatchlistsyncseriestip:
|
||||
'Automatically request series on your <PlexWatchlistSupportLink>Plex Watchlist</PlexWatchlistSupportLink>',
|
||||
});
|
||||
|
||||
const UserGeneralSettings: React.FC = () => {
|
||||
const UserGeneralSettings = () => {
|
||||
const intl = useIntl();
|
||||
const { publicRuntimeConfig } = getConfig();
|
||||
const { addToast } = useToasts();
|
||||
@@ -86,7 +90,7 @@ const UserGeneralSettings: React.FC = () => {
|
||||
const UserGeneralSettingsSchema = Yup.object().shape({
|
||||
discordId: Yup.string()
|
||||
.nullable()
|
||||
.matches(/^\d{17,18}$/, intl.formatMessage(messages.validationDiscordId)),
|
||||
.matches(/^\d{17,19}$/, intl.formatMessage(messages.validationDiscordId)),
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
@@ -131,6 +135,8 @@ const UserGeneralSettings: React.FC = () => {
|
||||
movieQuotaDays: data?.movieQuotaDays,
|
||||
tvQuotaLimit: data?.tvQuotaLimit,
|
||||
tvQuotaDays: data?.tvQuotaDays,
|
||||
watchlistSyncMovies: data?.watchlistSyncMovies,
|
||||
watchlistSyncTv: data?.watchlistSyncTv,
|
||||
}}
|
||||
validationSchema={UserGeneralSettingsSchema}
|
||||
enableReinitialize
|
||||
@@ -149,6 +155,8 @@ const UserGeneralSettings: React.FC = () => {
|
||||
movieQuotaDays: movieQuotaEnabled ? values.movieQuotaDays : null,
|
||||
tvQuotaLimit: tvQuotaEnabled ? values.tvQuotaLimit : null,
|
||||
tvQuotaDays: tvQuotaEnabled ? values.tvQuotaDays : null,
|
||||
watchlistSyncMovies: values.watchlistSyncMovies,
|
||||
watchlistSyncTv: values.watchlistSyncTv,
|
||||
});
|
||||
|
||||
if (currentUser?.id === user?.id && setLocale) {
|
||||
@@ -243,9 +251,11 @@ const UserGeneralSettings: React.FC = () => {
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
{errors.displayName && touched.displayName && (
|
||||
<div className="error">{errors.displayName}</div>
|
||||
)}
|
||||
{errors.displayName &&
|
||||
touched.displayName &&
|
||||
typeof errors.displayName === 'string' && (
|
||||
<div className="error">{errors.displayName}</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
<div className="form-row">
|
||||
@@ -280,17 +290,15 @@ const UserGeneralSettings: React.FC = () => {
|
||||
{currentUser?.id === user?.id && (
|
||||
<span className="label-tip">
|
||||
{intl.formatMessage(messages.discordIdTip, {
|
||||
FindDiscordIdLink: function FindDiscordIdLink(msg) {
|
||||
return (
|
||||
<a
|
||||
href="https://support.discord.com/hc/en-us/articles/206346498-Where-can-I-find-my-User-Server-Message-ID-"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
>
|
||||
{msg}
|
||||
</a>
|
||||
);
|
||||
},
|
||||
FindDiscordIdLink: (msg: React.ReactNode) => (
|
||||
<a
|
||||
href="https://support.discord.com/hc/en-us/articles/206346498-Where-can-I-find-my-User-Server-Message-ID-"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
>
|
||||
{msg}
|
||||
</a>
|
||||
),
|
||||
})}
|
||||
</span>
|
||||
)}
|
||||
@@ -299,9 +307,11 @@ const UserGeneralSettings: React.FC = () => {
|
||||
<div className="form-input-field">
|
||||
<Field id="discordId" name="discordId" type="text" />
|
||||
</div>
|
||||
{errors.discordId && touched.discordId && (
|
||||
<div className="error">{errors.discordId}</div>
|
||||
)}
|
||||
{errors.discordId &&
|
||||
touched.discordId &&
|
||||
typeof errors.discordId === 'string' && (
|
||||
<div className="error">{errors.discordId}</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
<div className="form-row">
|
||||
@@ -455,6 +465,99 @@ const UserGeneralSettings: React.FC = () => {
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
{hasPermission(
|
||||
[Permission.AUTO_REQUEST, Permission.AUTO_REQUEST_MOVIE],
|
||||
{ type: 'or' }
|
||||
) &&
|
||||
user?.userType === UserType.PLEX && (
|
||||
<div className="form-row">
|
||||
<label
|
||||
htmlFor="watchlistSyncMovies"
|
||||
className="checkbox-label"
|
||||
>
|
||||
<span>
|
||||
{intl.formatMessage(messages.plexwatchlistsyncmovies)}
|
||||
</span>
|
||||
<span className="label-tip">
|
||||
{intl.formatMessage(
|
||||
messages.plexwatchlistsyncmoviestip,
|
||||
{
|
||||
PlexWatchlistSupportLink: (
|
||||
msg: React.ReactNode
|
||||
) => (
|
||||
<a
|
||||
href="https://support.plex.tv/articles/universal-watchlist/"
|
||||
className="text-white transition duration-300 hover:underline"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
>
|
||||
{msg}
|
||||
</a>
|
||||
),
|
||||
}
|
||||
)}
|
||||
</span>
|
||||
</label>
|
||||
<div className="form-input-area">
|
||||
<Field
|
||||
type="checkbox"
|
||||
id="watchlistSyncMovies"
|
||||
name="watchlistSyncMovies"
|
||||
onChange={() => {
|
||||
setFieldValue(
|
||||
'watchlistSyncMovies',
|
||||
!values.watchlistSyncMovies
|
||||
);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
{hasPermission(
|
||||
[Permission.AUTO_REQUEST, Permission.AUTO_REQUEST_TV],
|
||||
{ type: 'or' }
|
||||
) &&
|
||||
user?.userType === UserType.PLEX && (
|
||||
<div className="form-row">
|
||||
<label htmlFor="watchlistSyncTv" className="checkbox-label">
|
||||
<span>
|
||||
{intl.formatMessage(messages.plexwatchlistsyncseries)}
|
||||
</span>
|
||||
<span className="label-tip">
|
||||
{intl.formatMessage(
|
||||
messages.plexwatchlistsyncseriestip,
|
||||
{
|
||||
PlexWatchlistSupportLink: (
|
||||
msg: React.ReactNode
|
||||
) => (
|
||||
<a
|
||||
href="https://support.plex.tv/articles/universal-watchlist/"
|
||||
className="text-white transition duration-300 hover:underline"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
>
|
||||
{msg}
|
||||
</a>
|
||||
),
|
||||
}
|
||||
)}
|
||||
</span>
|
||||
</label>
|
||||
<div className="form-input-area">
|
||||
<Field
|
||||
type="checkbox"
|
||||
id="watchlistSyncTv"
|
||||
name="watchlistSyncTv"
|
||||
onChange={() => {
|
||||
setFieldValue(
|
||||
'watchlistSyncTv',
|
||||
!values.watchlistSyncTv
|
||||
);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
<div className="actions">
|
||||
<div className="flex justify-end">
|
||||
<span className="ml-3 inline-flex rounded-md shadow-sm">
|
||||
|
||||
Reference in New Issue
Block a user