mirror of
https://github.com/fallenbagel/jellyseerr.git
synced 2025-12-24 02:39:18 -05:00
chore(deps): update react to 18 (#2943)
This commit is contained in:
@@ -1,3 +1,6 @@
|
||||
/**
|
||||
* @type {import('next').NextConfig}
|
||||
*/
|
||||
module.exports = {
|
||||
env: {
|
||||
commitTag: process.env.COMMIT_TAG || 'local',
|
||||
|
||||
17
package.json
17
package.json
@@ -29,6 +29,9 @@
|
||||
},
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@formatjs/intl-displaynames": "^6.0.3",
|
||||
"@formatjs/intl-locale": "^3.0.3",
|
||||
"@formatjs/intl-pluralrules": "^5.0.3",
|
||||
"@headlessui/react": "1.6.6",
|
||||
"@heroicons/react": "1.0.6",
|
||||
"@supercharge/request-ip": "1.2.0",
|
||||
@@ -62,12 +65,12 @@
|
||||
"openpgp": "5.4.0",
|
||||
"plex-api": "5.3.2",
|
||||
"pug": "3.0.2",
|
||||
"react": "17.0.2",
|
||||
"react": "18.2.0",
|
||||
"react-ace": "10.1.0",
|
||||
"react-animate-height": "2.1.2",
|
||||
"react-dom": "17.0.2",
|
||||
"react-dom": "18.2.0",
|
||||
"react-intersection-observer": "9.4.0",
|
||||
"react-intl": "5.25.1",
|
||||
"react-intl": "6.0.5",
|
||||
"react-markdown": "8.0.3",
|
||||
"react-select": "5.4.0",
|
||||
"react-spring": "9.5.2",
|
||||
@@ -111,8 +114,8 @@
|
||||
"@types/node": "17.0.36",
|
||||
"@types/node-schedule": "2.1.0",
|
||||
"@types/nodemailer": "6.4.5",
|
||||
"@types/react": "17.0.45",
|
||||
"@types/react-dom": "17.0.17",
|
||||
"@types/react": "18.0.17",
|
||||
"@types/react-dom": "18.0.6",
|
||||
"@types/react-transition-group": "4.4.5",
|
||||
"@types/secure-random-password": "0.2.1",
|
||||
"@types/semver": "7.3.12",
|
||||
@@ -153,7 +156,9 @@
|
||||
"typescript": "4.7.4"
|
||||
},
|
||||
"resolutions": {
|
||||
"sqlite3/node-gyp": "8.4.1"
|
||||
"sqlite3/node-gyp": "8.4.1",
|
||||
"@types/react": "18.0.17",
|
||||
"@types/react-dom": "18.0.6"
|
||||
},
|
||||
"config": {
|
||||
"commitizen": {
|
||||
|
||||
@@ -8,7 +8,7 @@ const messages = defineMessages({
|
||||
'The <code>{appDataPath}</code> volume mount was not configured properly. All data will be cleared when the container is stopped or restarted.',
|
||||
});
|
||||
|
||||
const AppDataWarning: React.FC = () => {
|
||||
const AppDataWarning = () => {
|
||||
const intl = useIntl();
|
||||
const { data, error } = useSWR<{ appData: boolean; appDataPath: string }>(
|
||||
'/api/v1/status/appdata'
|
||||
@@ -27,9 +27,9 @@ const AppDataWarning: React.FC = () => {
|
||||
{!data.appData && (
|
||||
<Alert
|
||||
title={intl.formatMessage(messages.dockerVolumeMissingDescription, {
|
||||
code: function code(msg) {
|
||||
return <code className="bg-opacity-50">{msg}</code>;
|
||||
},
|
||||
code: (msg: React.ReactNode) => (
|
||||
<code className="bg-opacity-50">{msg}</code>
|
||||
),
|
||||
appDataPath: data.appDataPath,
|
||||
})}
|
||||
/>
|
||||
|
||||
@@ -31,9 +31,7 @@ interface CollectionDetailsProps {
|
||||
collection?: Collection;
|
||||
}
|
||||
|
||||
const CollectionDetails: React.FC<CollectionDetailsProps> = ({
|
||||
collection,
|
||||
}) => {
|
||||
const CollectionDetails = ({ collection }: CollectionDetailsProps) => {
|
||||
const intl = useIntl();
|
||||
const router = useRouter();
|
||||
const settings = useSettings();
|
||||
|
||||
@@ -16,19 +16,24 @@ export interface AccordionChildProps {
|
||||
AccordionContent: any;
|
||||
}
|
||||
|
||||
export const AccordionContent: React.FC<{ isOpen: boolean }> = ({
|
||||
type AccordionContentProps = {
|
||||
isOpen: boolean;
|
||||
children: React.ReactNode;
|
||||
};
|
||||
|
||||
export const AccordionContent = ({
|
||||
isOpen,
|
||||
children,
|
||||
}) => {
|
||||
}: AccordionContentProps) => {
|
||||
return <AnimateHeight height={isOpen ? 'auto' : 0}>{children}</AnimateHeight>;
|
||||
};
|
||||
|
||||
const Accordion: React.FC<AccordionProps> = ({
|
||||
const Accordion = ({
|
||||
single,
|
||||
atLeastOne,
|
||||
initialOpenIndexes,
|
||||
children,
|
||||
}) => {
|
||||
}: AccordionProps) => {
|
||||
const initialState = initialOpenIndexes || (atLeastOne && [0]) || [];
|
||||
const [openIndexes, setOpenIndexes] = useState<number[]>(initialState);
|
||||
|
||||
|
||||
@@ -8,9 +8,10 @@ import React from 'react';
|
||||
interface AlertProps {
|
||||
title?: React.ReactNode;
|
||||
type?: 'warning' | 'info' | 'error';
|
||||
children?: React.ReactNode;
|
||||
}
|
||||
|
||||
const Alert: React.FC<AlertProps> = ({ title, children, type }) => {
|
||||
const Alert = ({ title, children, type }: AlertProps) => {
|
||||
let design = {
|
||||
bgColor: 'bg-yellow-600',
|
||||
titleColor: 'text-yellow-100',
|
||||
|
||||
@@ -5,14 +5,15 @@ interface BadgeProps {
|
||||
badgeType?: 'default' | 'primary' | 'danger' | 'warning' | 'success';
|
||||
className?: string;
|
||||
href?: string;
|
||||
children: React.ReactNode;
|
||||
}
|
||||
|
||||
const Badge: React.FC<BadgeProps> = ({
|
||||
const Badge = ({
|
||||
badgeType = 'default',
|
||||
className,
|
||||
href,
|
||||
children,
|
||||
}) => {
|
||||
}: BadgeProps) => {
|
||||
const badgeStyle = [
|
||||
'px-2 inline-flex text-xs leading-5 font-semibold rounded-full whitespace-nowrap',
|
||||
];
|
||||
|
||||
@@ -13,11 +13,11 @@ interface DropdownItemProps extends AnchorHTMLAttributes<HTMLAnchorElement> {
|
||||
buttonType?: 'primary' | 'ghost';
|
||||
}
|
||||
|
||||
const DropdownItem: React.FC<DropdownItemProps> = ({
|
||||
const DropdownItem = ({
|
||||
children,
|
||||
buttonType = 'primary',
|
||||
...props
|
||||
}) => {
|
||||
}: DropdownItemProps) => {
|
||||
let styleClass = 'button-md text-white';
|
||||
|
||||
switch (buttonType) {
|
||||
@@ -46,14 +46,14 @@ interface ButtonWithDropdownProps
|
||||
buttonType?: 'primary' | 'ghost';
|
||||
}
|
||||
|
||||
const ButtonWithDropdown: React.FC<ButtonWithDropdownProps> = ({
|
||||
const ButtonWithDropdown = ({
|
||||
text,
|
||||
children,
|
||||
dropdownIcon,
|
||||
className,
|
||||
buttonType = 'primary',
|
||||
...props
|
||||
}) => {
|
||||
}: ButtonWithDropdownProps) => {
|
||||
const [isOpen, setIsOpen] = useState(false);
|
||||
const buttonRef = useRef<HTMLButtonElement>(null);
|
||||
useClickOutside(buttonRef, () => setIsOpen(false));
|
||||
|
||||
@@ -10,7 +10,7 @@ import useSettings from '../../../hooks/useSettings';
|
||||
* It uses the `next/image` Image component but overrides
|
||||
* the `unoptimized` prop based on the application setting `cacheImages`.
|
||||
**/
|
||||
const CachedImage: React.FC<ImageProps> = (props) => {
|
||||
const CachedImage = (props: ImageProps) => {
|
||||
const { currentSettings } = useSettings();
|
||||
|
||||
return <Image unoptimized={!currentSettings.cacheImages} {...props} />;
|
||||
|
||||
@@ -6,14 +6,15 @@ interface ConfirmButtonProps {
|
||||
onClick: () => void;
|
||||
confirmText: React.ReactNode;
|
||||
className?: string;
|
||||
children: React.ReactNode;
|
||||
}
|
||||
|
||||
const ConfirmButton: React.FC<ConfirmButtonProps> = ({
|
||||
const ConfirmButton = ({
|
||||
onClick,
|
||||
children,
|
||||
confirmText,
|
||||
className,
|
||||
}) => {
|
||||
}: ConfirmButtonProps) => {
|
||||
const ref = useRef(null);
|
||||
useClickOutside(ref, () => setIsClicked(false));
|
||||
const [isClicked, setIsClicked] = useState(false);
|
||||
|
||||
@@ -3,13 +3,10 @@ import React from 'react';
|
||||
interface HeaderProps {
|
||||
extraMargin?: number;
|
||||
subtext?: React.ReactNode;
|
||||
children: React.ReactNode;
|
||||
}
|
||||
|
||||
const Header: React.FC<HeaderProps> = ({
|
||||
children,
|
||||
extraMargin = 0,
|
||||
subtext,
|
||||
}) => {
|
||||
const Header = ({ children, extraMargin = 0, subtext }: HeaderProps) => {
|
||||
return (
|
||||
<div className="mt-8 md:flex md:items-center md:justify-between">
|
||||
<div className={`min-w-0 flex-1 mx-${extraMargin}`}>
|
||||
|
||||
@@ -4,9 +4,10 @@ import { withProperties } from '../../../utils/typeHelpers';
|
||||
interface ListItemProps {
|
||||
title: string;
|
||||
className?: string;
|
||||
children: React.ReactNode;
|
||||
}
|
||||
|
||||
const ListItem: React.FC<ListItemProps> = ({ title, className, children }) => {
|
||||
const ListItem = ({ title, className, children }: ListItemProps) => {
|
||||
return (
|
||||
<div>
|
||||
<div className="max-w-6xl py-4 sm:grid sm:grid-cols-3 sm:gap-4">
|
||||
@@ -22,9 +23,10 @@ const ListItem: React.FC<ListItemProps> = ({ title, className, children }) => {
|
||||
interface ListProps {
|
||||
title: string;
|
||||
subTitle?: string;
|
||||
children: React.ReactNode;
|
||||
}
|
||||
|
||||
const List: React.FC<ListProps> = ({ title, subTitle, children }) => {
|
||||
const List = ({ title, subTitle, children }: ListProps) => {
|
||||
return (
|
||||
<>
|
||||
<div>
|
||||
|
||||
@@ -18,13 +18,13 @@ interface ListViewProps {
|
||||
onScrollBottom: () => void;
|
||||
}
|
||||
|
||||
const ListView: React.FC<ListViewProps> = ({
|
||||
const ListView = ({
|
||||
items,
|
||||
isEmpty,
|
||||
isLoading,
|
||||
onScrollBottom,
|
||||
isReachingEnd,
|
||||
}) => {
|
||||
}: ListViewProps) => {
|
||||
const intl = useIntl();
|
||||
useVerticalScroll(onScrollBottom, !isLoading && !isEmpty && !isReachingEnd);
|
||||
return (
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import React from 'react';
|
||||
|
||||
export const SmallLoadingSpinner: React.FC = () => {
|
||||
export const SmallLoadingSpinner = () => {
|
||||
return (
|
||||
<div className="inset-0 flex h-full w-full items-center justify-center text-gray-200">
|
||||
<svg
|
||||
@@ -29,7 +29,7 @@ export const SmallLoadingSpinner: React.FC = () => {
|
||||
);
|
||||
};
|
||||
|
||||
const LoadingSpinner: React.FC = () => {
|
||||
const LoadingSpinner = () => {
|
||||
return (
|
||||
<div className="inset-0 flex h-64 items-center justify-center text-gray-200">
|
||||
<svg
|
||||
|
||||
@@ -33,9 +33,10 @@ interface ModalProps {
|
||||
iconSvg?: ReactNode;
|
||||
loading?: boolean;
|
||||
backdrop?: string;
|
||||
children?: ReactNode;
|
||||
}
|
||||
|
||||
const Modal: React.FC<ModalProps> = ({
|
||||
const Modal = ({
|
||||
title,
|
||||
onCancel,
|
||||
onOk,
|
||||
@@ -58,7 +59,7 @@ const Modal: React.FC<ModalProps> = ({
|
||||
tertiaryText,
|
||||
onTertiary,
|
||||
backdrop,
|
||||
}) => {
|
||||
}: ModalProps) => {
|
||||
const intl = useIntl();
|
||||
const modalRef = useRef<HTMLDivElement>(null);
|
||||
useClickOutside(modalRef, () => {
|
||||
|
||||
@@ -6,15 +6,16 @@ interface PageTitleProps {
|
||||
title: string | (string | undefined)[];
|
||||
}
|
||||
|
||||
const PageTitle: React.FC<PageTitleProps> = ({ title }) => {
|
||||
const PageTitle = ({ title }: PageTitleProps) => {
|
||||
const settings = useSettings();
|
||||
|
||||
const titleText = `${
|
||||
Array.isArray(title) ? title.filter(Boolean).join(' - ') : title
|
||||
} - ${settings.currentSettings.applicationTitle}`;
|
||||
|
||||
return (
|
||||
<Head>
|
||||
<title>
|
||||
{Array.isArray(title) ? title.filter(Boolean).join(' - ') : title} -{' '}
|
||||
{settings.currentSettings.applicationTitle}
|
||||
</title>
|
||||
<title>{titleText}</title>
|
||||
</Head>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -12,7 +12,7 @@ export interface PlayButtonLink {
|
||||
svg: ReactNode;
|
||||
}
|
||||
|
||||
const PlayButton: React.FC<PlayButtonProps> = ({ links }) => {
|
||||
const PlayButton = ({ links }: PlayButtonProps) => {
|
||||
if (!links || !links.length) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -6,11 +6,11 @@ interface ProgressCircleProps {
|
||||
useHeatLevel?: boolean;
|
||||
}
|
||||
|
||||
const ProgressCircle: React.FC<ProgressCircleProps> = ({
|
||||
const ProgressCircle = ({
|
||||
className,
|
||||
progress = 0,
|
||||
useHeatLevel,
|
||||
}) => {
|
||||
}: ProgressCircleProps) => {
|
||||
const ref = useRef<SVGCircleElement>(null);
|
||||
|
||||
let color = '';
|
||||
|
||||
@@ -12,10 +12,7 @@ interface CustomFieldProps extends React.ComponentProps<typeof Field> {
|
||||
|
||||
type SensitiveInputProps = CustomInputProps | CustomFieldProps;
|
||||
|
||||
const SensitiveInput: React.FC<SensitiveInputProps> = ({
|
||||
as = 'input',
|
||||
...props
|
||||
}) => {
|
||||
const SensitiveInput = ({ as = 'input', ...props }: SensitiveInputProps) => {
|
||||
const [isHidden, setHidden] = useState(true);
|
||||
const Component = as === 'input' ? 'input' : Field;
|
||||
const componentProps =
|
||||
|
||||
@@ -15,14 +15,17 @@ export interface SettingsRoute {
|
||||
hidden?: boolean;
|
||||
}
|
||||
|
||||
const SettingsLink: React.FC<{
|
||||
type SettingsLinkProps = {
|
||||
tabType: 'default' | 'button';
|
||||
currentPath: string;
|
||||
route: string;
|
||||
regex: RegExp;
|
||||
hidden?: boolean;
|
||||
isMobile?: boolean;
|
||||
}> = ({
|
||||
children: React.ReactNode;
|
||||
};
|
||||
|
||||
const SettingsLink = ({
|
||||
children,
|
||||
tabType,
|
||||
currentPath,
|
||||
@@ -30,7 +33,7 @@ const SettingsLink: React.FC<{
|
||||
regex,
|
||||
hidden = false,
|
||||
isMobile = false,
|
||||
}) => {
|
||||
}: SettingsLinkProps) => {
|
||||
if (hidden) {
|
||||
return null;
|
||||
}
|
||||
@@ -66,10 +69,13 @@ const SettingsLink: React.FC<{
|
||||
);
|
||||
};
|
||||
|
||||
const SettingsTabs: React.FC<{
|
||||
const SettingsTabs = ({
|
||||
tabType = 'default',
|
||||
settingsRoutes,
|
||||
}: {
|
||||
tabType?: 'default' | 'button';
|
||||
settingsRoutes: SettingsRoute[];
|
||||
}> = ({ tabType = 'default', settingsRoutes }) => {
|
||||
}) => {
|
||||
const router = useRouter();
|
||||
const { user: currentUser } = useUser();
|
||||
|
||||
|
||||
@@ -10,15 +10,16 @@ interface SlideOverProps {
|
||||
title: React.ReactNode;
|
||||
subText?: string;
|
||||
onClose: () => void;
|
||||
children: React.ReactNode;
|
||||
}
|
||||
|
||||
const SlideOver: React.FC<SlideOverProps> = ({
|
||||
const SlideOver = ({
|
||||
show = false,
|
||||
title,
|
||||
subText,
|
||||
onClose,
|
||||
children,
|
||||
}) => {
|
||||
}: SlideOverProps) => {
|
||||
const [isMounted, setIsMounted] = useState(false);
|
||||
const slideoverRef = useRef(null);
|
||||
useLockBodyScroll(show);
|
||||
|
||||
@@ -1,18 +1,21 @@
|
||||
import type { AllHTMLAttributes } from 'react';
|
||||
import React from 'react';
|
||||
import { withProperties } from '../../../utils/typeHelpers';
|
||||
|
||||
const TBody: React.FC = ({ children }) => {
|
||||
type TBodyProps = {
|
||||
children: React.ReactNode;
|
||||
};
|
||||
|
||||
const TBody = ({ children }: TBodyProps) => {
|
||||
return (
|
||||
<tbody className="divide-y divide-gray-700 bg-gray-800">{children}</tbody>
|
||||
);
|
||||
};
|
||||
|
||||
const TH: React.FC<AllHTMLAttributes<HTMLTableHeaderCellElement>> = ({
|
||||
const TH = ({
|
||||
children,
|
||||
className,
|
||||
...props
|
||||
}) => {
|
||||
}: React.ComponentPropsWithoutRef<'th'>) => {
|
||||
const style = [
|
||||
'px-4 py-3 bg-gray-500 text-left text-xs leading-4 font-medium text-gray-200 uppercase tracking-wider truncate',
|
||||
];
|
||||
@@ -28,18 +31,18 @@ const TH: React.FC<AllHTMLAttributes<HTMLTableHeaderCellElement>> = ({
|
||||
);
|
||||
};
|
||||
|
||||
interface TDProps extends AllHTMLAttributes<HTMLTableCellElement> {
|
||||
type TDProps = {
|
||||
alignText?: 'left' | 'center' | 'right';
|
||||
noPadding?: boolean;
|
||||
}
|
||||
};
|
||||
|
||||
const TD: React.FC<TDProps> = ({
|
||||
const TD = ({
|
||||
children,
|
||||
alignText = 'left',
|
||||
noPadding,
|
||||
className,
|
||||
...props
|
||||
}) => {
|
||||
}: TDProps & React.ComponentPropsWithoutRef<'td'>) => {
|
||||
const style = ['text-sm leading-5 text-white'];
|
||||
|
||||
switch (alignText) {
|
||||
@@ -69,7 +72,11 @@ const TD: React.FC<TDProps> = ({
|
||||
);
|
||||
};
|
||||
|
||||
const Table: React.FC = ({ children }) => {
|
||||
type TableProps = {
|
||||
children: React.ReactNode;
|
||||
};
|
||||
|
||||
const Table = ({ children }: TableProps) => {
|
||||
return (
|
||||
<div className="flex flex-col">
|
||||
<div className="my-2 -mx-4 overflow-x-auto md:mx-0 lg:mx-0">
|
||||
|
||||
@@ -7,7 +7,7 @@ interface CompanyCardProps {
|
||||
url: string;
|
||||
}
|
||||
|
||||
const CompanyCard: React.FC<CompanyCardProps> = ({ image, url, name }) => {
|
||||
const CompanyCard = ({ image, url, name }: CompanyCardProps) => {
|
||||
const [isHovered, setHovered] = useState(false);
|
||||
|
||||
return (
|
||||
|
||||
@@ -13,7 +13,7 @@ const messages = defineMessages({
|
||||
genreMovies: '{genre} Movies',
|
||||
});
|
||||
|
||||
const DiscoverMovieGenre: React.FC = () => {
|
||||
const DiscoverMovieGenre = () => {
|
||||
const router = useRouter();
|
||||
const intl = useIntl();
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@ const messages = defineMessages({
|
||||
languageMovies: '{language} Movies',
|
||||
});
|
||||
|
||||
const DiscoverMovieLanguage: React.FC = () => {
|
||||
const DiscoverMovieLanguage = () => {
|
||||
const router = useRouter();
|
||||
const intl = useIntl();
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@ const messages = defineMessages({
|
||||
discovermovies: 'Popular Movies',
|
||||
});
|
||||
|
||||
const DiscoverMovies: React.FC = () => {
|
||||
const DiscoverMovies = () => {
|
||||
const intl = useIntl();
|
||||
|
||||
const {
|
||||
|
||||
@@ -14,7 +14,7 @@ const messages = defineMessages({
|
||||
networkSeries: '{network} Series',
|
||||
});
|
||||
|
||||
const DiscoverTvNetwork: React.FC = () => {
|
||||
const DiscoverTvNetwork = () => {
|
||||
const router = useRouter();
|
||||
const intl = useIntl();
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@ const messages = defineMessages({
|
||||
studioMovies: '{studio} Movies',
|
||||
});
|
||||
|
||||
const DiscoverMovieStudio: React.FC = () => {
|
||||
const DiscoverMovieStudio = () => {
|
||||
const router = useRouter();
|
||||
const intl = useIntl();
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@ const messages = defineMessages({
|
||||
discovertv: 'Popular Series',
|
||||
});
|
||||
|
||||
const DiscoverTv: React.FC = () => {
|
||||
const DiscoverTv = () => {
|
||||
const intl = useIntl();
|
||||
|
||||
const {
|
||||
|
||||
@@ -13,7 +13,7 @@ const messages = defineMessages({
|
||||
genreSeries: '{genre} Series',
|
||||
});
|
||||
|
||||
const DiscoverTvGenre: React.FC = () => {
|
||||
const DiscoverTvGenre = () => {
|
||||
const router = useRouter();
|
||||
const intl = useIntl();
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@ const messages = defineMessages({
|
||||
languageSeries: '{language} Series',
|
||||
});
|
||||
|
||||
const DiscoverTvLanguage: React.FC = () => {
|
||||
const DiscoverTvLanguage = () => {
|
||||
const router = useRouter();
|
||||
const intl = useIntl();
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@ const messages = defineMessages({
|
||||
upcomingtv: 'Upcoming Series',
|
||||
});
|
||||
|
||||
const DiscoverTvUpcoming: React.FC = () => {
|
||||
const DiscoverTvUpcoming = () => {
|
||||
const intl = useIntl();
|
||||
|
||||
const {
|
||||
|
||||
@@ -13,7 +13,7 @@ const messages = defineMessages({
|
||||
moviegenres: 'Movie Genres',
|
||||
});
|
||||
|
||||
const MovieGenreList: React.FC = () => {
|
||||
const MovieGenreList = () => {
|
||||
const intl = useIntl();
|
||||
const { data, error } = useSWR<GenreSliderItem[]>(
|
||||
`/api/v1/discover/genreslider/movie`
|
||||
|
||||
@@ -12,7 +12,7 @@ const messages = defineMessages({
|
||||
moviegenres: 'Movie Genres',
|
||||
});
|
||||
|
||||
const MovieGenreSlider: React.FC = () => {
|
||||
const MovieGenreSlider = () => {
|
||||
const intl = useIntl();
|
||||
const { data, error } = useSWR<GenreSliderItem[]>(
|
||||
`/api/v1/discover/genreslider/movie`,
|
||||
|
||||
@@ -142,7 +142,7 @@ const networks: Network[] = [
|
||||
},
|
||||
];
|
||||
|
||||
const NetworkSlider: React.FC = () => {
|
||||
const NetworkSlider = () => {
|
||||
const intl = useIntl();
|
||||
|
||||
return (
|
||||
|
||||
@@ -76,7 +76,7 @@ const studios: Studio[] = [
|
||||
},
|
||||
];
|
||||
|
||||
const StudioSlider: React.FC = () => {
|
||||
const StudioSlider = () => {
|
||||
const intl = useIntl();
|
||||
|
||||
return (
|
||||
|
||||
@@ -15,7 +15,7 @@ const messages = defineMessages({
|
||||
trending: 'Trending',
|
||||
});
|
||||
|
||||
const Trending: React.FC = () => {
|
||||
const Trending = () => {
|
||||
const intl = useIntl();
|
||||
const {
|
||||
isLoadingInitialData,
|
||||
|
||||
@@ -13,7 +13,7 @@ const messages = defineMessages({
|
||||
seriesgenres: 'Series Genres',
|
||||
});
|
||||
|
||||
const TvGenreList: React.FC = () => {
|
||||
const TvGenreList = () => {
|
||||
const intl = useIntl();
|
||||
const { data, error } = useSWR<GenreSliderItem[]>(
|
||||
`/api/v1/discover/genreslider/tv`
|
||||
|
||||
@@ -12,7 +12,7 @@ const messages = defineMessages({
|
||||
tvgenres: 'Series Genres',
|
||||
});
|
||||
|
||||
const TvGenreSlider: React.FC = () => {
|
||||
const TvGenreSlider = () => {
|
||||
const intl = useIntl();
|
||||
const { data, error } = useSWR<GenreSliderItem[]>(
|
||||
`/api/v1/discover/genreslider/tv`,
|
||||
|
||||
@@ -11,7 +11,7 @@ const messages = defineMessages({
|
||||
upcomingmovies: 'Upcoming Movies',
|
||||
});
|
||||
|
||||
const UpcomingMovies: React.FC = () => {
|
||||
const UpcomingMovies = () => {
|
||||
const intl = useIntl();
|
||||
|
||||
const {
|
||||
|
||||
@@ -27,7 +27,7 @@ const messages = defineMessages({
|
||||
trending: 'Trending',
|
||||
});
|
||||
|
||||
const Discover: React.FC = () => {
|
||||
const Discover = () => {
|
||||
const intl = useIntl();
|
||||
|
||||
const { data: media, error: mediaError } = useSWR<MediaResultsResponse>(
|
||||
|
||||
@@ -12,10 +12,7 @@ interface DownloadBlockProps {
|
||||
is4k?: boolean;
|
||||
}
|
||||
|
||||
const DownloadBlock: React.FC<DownloadBlockProps> = ({
|
||||
downloadItem,
|
||||
is4k = false,
|
||||
}) => {
|
||||
const DownloadBlock = ({ downloadItem, is4k = false }: DownloadBlockProps) => {
|
||||
const intl = useIntl();
|
||||
|
||||
return (
|
||||
|
||||
@@ -17,14 +17,14 @@ interface ExternalLinkBlockProps {
|
||||
plexUrl?: string;
|
||||
}
|
||||
|
||||
const ExternalLinkBlock: React.FC<ExternalLinkBlockProps> = ({
|
||||
const ExternalLinkBlock = ({
|
||||
mediaType,
|
||||
tmdbId,
|
||||
tvdbId,
|
||||
imdbId,
|
||||
rtUrl,
|
||||
plexUrl,
|
||||
}) => {
|
||||
}: ExternalLinkBlockProps) => {
|
||||
const { locale } = useLocale();
|
||||
|
||||
return (
|
||||
|
||||
@@ -10,12 +10,7 @@ interface GenreCardProps {
|
||||
canExpand?: boolean;
|
||||
}
|
||||
|
||||
const GenreCard: React.FC<GenreCardProps> = ({
|
||||
image,
|
||||
url,
|
||||
name,
|
||||
canExpand = false,
|
||||
}) => {
|
||||
const GenreCard = ({ image, url, name, canExpand = false }: GenreCardProps) => {
|
||||
const [isHovered, setHovered] = useState(false);
|
||||
|
||||
return (
|
||||
@@ -54,7 +49,7 @@ const GenreCard: React.FC<GenreCardProps> = ({
|
||||
);
|
||||
};
|
||||
|
||||
const GenreCardPlaceholder: React.FC = () => {
|
||||
const GenreCardPlaceholder = () => {
|
||||
return (
|
||||
<div
|
||||
className={`relative h-32 w-56 animate-pulse rounded-xl bg-gray-700 sm:h-40 sm:w-72`}
|
||||
|
||||
@@ -16,7 +16,7 @@ interface IssueBlockProps {
|
||||
issue: Issue;
|
||||
}
|
||||
|
||||
const IssueBlock: React.FC<IssueBlockProps> = ({ issue }) => {
|
||||
const IssueBlock = ({ issue }: IssueBlockProps) => {
|
||||
const { user } = useUser();
|
||||
const intl = useIntl();
|
||||
const issueOption = issueOptions.find(
|
||||
|
||||
@@ -30,12 +30,12 @@ interface IssueCommentProps {
|
||||
onUpdate?: () => void;
|
||||
}
|
||||
|
||||
const IssueComment: React.FC<IssueCommentProps> = ({
|
||||
const IssueComment = ({
|
||||
comment,
|
||||
isReversed = false,
|
||||
isActiveUser = false,
|
||||
onUpdate,
|
||||
}) => {
|
||||
}: IssueCommentProps) => {
|
||||
const intl = useIntl();
|
||||
const [showDeleteModal, setShowDeleteModal] = useState(false);
|
||||
const [isEditing, setIsEditing] = useState(false);
|
||||
@@ -195,9 +195,11 @@ const IssueComment: React.FC<IssueCommentProps> = ({
|
||||
name="newMessage"
|
||||
className="h-24"
|
||||
/>
|
||||
{errors.newMessage && touched.newMessage && (
|
||||
<div className="error">{errors.newMessage}</div>
|
||||
)}
|
||||
{errors.newMessage &&
|
||||
touched.newMessage &&
|
||||
typeof errors.newMessage === 'string' && (
|
||||
<div className="error">{errors.newMessage}</div>
|
||||
)}
|
||||
<div className="mt-4 flex items-center justify-end space-x-2">
|
||||
<Button
|
||||
type="button"
|
||||
|
||||
@@ -22,13 +22,13 @@ interface IssueDescriptionProps {
|
||||
onDelete: () => void;
|
||||
}
|
||||
|
||||
const IssueDescription: React.FC<IssueDescriptionProps> = ({
|
||||
const IssueDescription = ({
|
||||
description,
|
||||
belongsToUser,
|
||||
commentCount,
|
||||
onEdit,
|
||||
onDelete,
|
||||
}) => {
|
||||
}: IssueDescriptionProps) => {
|
||||
const intl = useIntl();
|
||||
const { hasPermission } = useUser();
|
||||
const [isEditing, setIsEditing] = useState(false);
|
||||
|
||||
@@ -74,7 +74,7 @@ const isMovie = (movie: MovieDetails | TvDetails): movie is MovieDetails => {
|
||||
return (movie as MovieDetails).title !== undefined;
|
||||
};
|
||||
|
||||
const IssueDetails: React.FC = () => {
|
||||
const IssueDetails = () => {
|
||||
const { addToast } = useToasts();
|
||||
const router = useRouter();
|
||||
const intl = useIntl();
|
||||
|
||||
@@ -36,7 +36,7 @@ interface IssueItemProps {
|
||||
issue: Issue;
|
||||
}
|
||||
|
||||
const IssueItem: React.FC<IssueItemProps> = ({ issue }) => {
|
||||
const IssueItem = ({ issue }: IssueItemProps) => {
|
||||
const intl = useIntl();
|
||||
const { hasPermission } = useUser();
|
||||
const { ref, inView } = useInView({
|
||||
|
||||
@@ -32,7 +32,7 @@ enum Filter {
|
||||
|
||||
type Sort = 'added' | 'modified';
|
||||
|
||||
const IssueList: React.FC = () => {
|
||||
const IssueList = () => {
|
||||
const intl = useIntl();
|
||||
const router = useRouter();
|
||||
const [currentFilter, setCurrentFilter] = useState<Filter>(Filter.OPEN);
|
||||
@@ -194,9 +194,9 @@ const IssueList: React.FC = () => {
|
||||
? pageIndex * currentPageSize + data.results.length
|
||||
: (pageIndex + 1) * currentPageSize,
|
||||
total: data.pageInfo.results,
|
||||
strong: function strong(msg) {
|
||||
return <span className="font-medium">{msg}</span>;
|
||||
},
|
||||
strong: (msg: React.ReactNode) => (
|
||||
<span className="font-medium">{msg}</span>
|
||||
),
|
||||
})}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
@@ -55,11 +55,11 @@ interface CreateIssueModalProps {
|
||||
onCancel?: () => void;
|
||||
}
|
||||
|
||||
const CreateIssueModal: React.FC<CreateIssueModalProps> = ({
|
||||
const CreateIssueModal = ({
|
||||
onCancel,
|
||||
mediaType,
|
||||
tmdbId,
|
||||
}) => {
|
||||
}: CreateIssueModalProps) => {
|
||||
const intl = useIntl();
|
||||
const settings = useSettings();
|
||||
const { hasPermission } = useUser();
|
||||
@@ -118,9 +118,7 @@ const CreateIssueModal: React.FC<CreateIssueModalProps> = ({
|
||||
<div>
|
||||
{intl.formatMessage(messages.toastSuccessCreate, {
|
||||
title: isMovie(data) ? data.title : data.name,
|
||||
strong: function strong(msg) {
|
||||
return <strong>{msg}</strong>;
|
||||
},
|
||||
strong: (msg: React.ReactNode) => <strong>{msg}</strong>,
|
||||
})}
|
||||
</div>
|
||||
<Link href={`/issues/${newIssue.data.id}`}>
|
||||
@@ -315,9 +313,11 @@ const CreateIssueModal: React.FC<CreateIssueModalProps> = ({
|
||||
className="h-28"
|
||||
placeholder={intl.formatMessage(messages.providedetail)}
|
||||
/>
|
||||
{errors.message && touched.message && (
|
||||
<div className="error">{errors.message}</div>
|
||||
)}
|
||||
{errors.message &&
|
||||
touched.message &&
|
||||
typeof errors.message === 'string' && (
|
||||
<div className="error">{errors.message}</div>
|
||||
)}
|
||||
</div>
|
||||
</Modal>
|
||||
);
|
||||
|
||||
@@ -10,12 +10,7 @@ interface IssueModalProps {
|
||||
issueId?: never;
|
||||
}
|
||||
|
||||
const IssueModal: React.FC<IssueModalProps> = ({
|
||||
show,
|
||||
mediaType,
|
||||
onCancel,
|
||||
tmdbId,
|
||||
}) => (
|
||||
const IssueModal = ({ show, mediaType, onCancel, tmdbId }: IssueModalProps) => (
|
||||
<Transition
|
||||
enter="transition opacity-0 duration-300"
|
||||
enterFrom="opacity-0"
|
||||
|
||||
@@ -10,12 +10,7 @@ interface JSONEditorProps extends HTMLAttributes<HTMLDivElement> {
|
||||
onUpdate: (value: string) => void;
|
||||
}
|
||||
|
||||
const JSONEditor: React.FC<JSONEditorProps> = ({
|
||||
name,
|
||||
value,
|
||||
onUpdate,
|
||||
onBlur,
|
||||
}) => {
|
||||
const JSONEditor = ({ name, value, onUpdate, onBlur }: JSONEditorProps) => {
|
||||
return (
|
||||
<div className="w-full overflow-hidden rounded-md">
|
||||
<AceEditor
|
||||
|
||||
@@ -34,12 +34,12 @@ interface LanguageSelectorProps {
|
||||
isUserSettings?: boolean;
|
||||
}
|
||||
|
||||
const LanguageSelector: React.FC<LanguageSelectorProps> = ({
|
||||
const LanguageSelector = ({
|
||||
value,
|
||||
setFieldValue,
|
||||
serverValue,
|
||||
isUserSettings = false,
|
||||
}) => {
|
||||
}: LanguageSelectorProps) => {
|
||||
const intl = useIntl();
|
||||
const { data: languages } = useSWR<Language[]>('/api/v1/languages');
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@ const messages = defineMessages({
|
||||
displaylanguage: 'Display Language',
|
||||
});
|
||||
|
||||
const LanguagePicker: React.FC = () => {
|
||||
const LanguagePicker = () => {
|
||||
const intl = useIntl();
|
||||
const dropdownRef = useRef<HTMLDivElement>(null);
|
||||
const { locale, setLocale } = useLocale();
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { BellIcon } from '@heroicons/react/outline';
|
||||
import React from 'react';
|
||||
|
||||
const Notifications: React.FC = () => {
|
||||
const Notifications = () => {
|
||||
return (
|
||||
<button
|
||||
className="rounded-full p-1 text-gray-400 hover:bg-gray-500 hover:text-white focus:text-white focus:outline-none focus:ring"
|
||||
|
||||
@@ -8,7 +8,7 @@ const messages = defineMessages({
|
||||
searchPlaceholder: 'Search Movies & TV',
|
||||
});
|
||||
|
||||
const SearchInput: React.FC = () => {
|
||||
const SearchInput = () => {
|
||||
const intl = useIntl();
|
||||
const { searchValue, setSearchValue, setIsOpen, clear } = useSearchInput();
|
||||
return (
|
||||
|
||||
@@ -85,7 +85,7 @@ const SidebarLinks: SidebarLinkProps[] = [
|
||||
},
|
||||
];
|
||||
|
||||
const Sidebar: React.FC<SidebarProps> = ({ open, setClosed }) => {
|
||||
const Sidebar = ({ open, setClosed }: SidebarProps) => {
|
||||
const navRef = useRef<HTMLDivElement>(null);
|
||||
const router = useRouter();
|
||||
const intl = useIntl();
|
||||
|
||||
@@ -14,7 +14,7 @@ const messages = defineMessages({
|
||||
signout: 'Sign Out',
|
||||
});
|
||||
|
||||
const UserDropdown: React.FC = () => {
|
||||
const UserDropdown = () => {
|
||||
const intl = useIntl();
|
||||
const dropdownRef = useRef<HTMLDivElement>(null);
|
||||
const { user, revalidate } = useUser();
|
||||
|
||||
@@ -22,7 +22,7 @@ interface VersionStatusProps {
|
||||
onClick?: () => void;
|
||||
}
|
||||
|
||||
const VersionStatus: React.FC<VersionStatusProps> = ({ onClick }) => {
|
||||
const VersionStatus = ({ onClick }: VersionStatusProps) => {
|
||||
const intl = useIntl();
|
||||
const { data } = useSWR<StatusResponse>('/api/v1/status', {
|
||||
refreshInterval: 60 * 1000,
|
||||
|
||||
@@ -10,7 +10,11 @@ import SearchInput from './SearchInput';
|
||||
import Sidebar from './Sidebar';
|
||||
import UserDropdown from './UserDropdown';
|
||||
|
||||
const Layout: React.FC = ({ children }) => {
|
||||
type LayoutProps = {
|
||||
children: React.ReactNode;
|
||||
};
|
||||
|
||||
const Layout = ({ children }: LayoutProps) => {
|
||||
const [isSidebarOpen, setSidebarOpen] = useState(false);
|
||||
const [isScrolled, setIsScrolled] = useState(false);
|
||||
const { user } = useUser();
|
||||
|
||||
@@ -24,7 +24,7 @@ interface LocalLoginProps {
|
||||
revalidate: () => void;
|
||||
}
|
||||
|
||||
const LocalLogin: React.FC<LocalLoginProps> = ({ revalidate }) => {
|
||||
const LocalLogin = ({ revalidate }: LocalLoginProps) => {
|
||||
const intl = useIntl();
|
||||
const settings = useSettings();
|
||||
const [loginError, setLoginError] = useState<string | null>(null);
|
||||
@@ -80,9 +80,11 @@ const LocalLogin: React.FC<LocalLoginProps> = ({ revalidate }) => {
|
||||
data-testid="email"
|
||||
/>
|
||||
</div>
|
||||
{errors.email && touched.email && (
|
||||
<div className="error">{errors.email}</div>
|
||||
)}
|
||||
{errors.email &&
|
||||
touched.email &&
|
||||
typeof errors.email === 'string' && (
|
||||
<div className="error">{errors.email}</div>
|
||||
)}
|
||||
</div>
|
||||
<label htmlFor="password" className="text-label">
|
||||
{intl.formatMessage(messages.password)}
|
||||
@@ -98,9 +100,11 @@ const LocalLogin: React.FC<LocalLoginProps> = ({ revalidate }) => {
|
||||
data-testid="password"
|
||||
/>
|
||||
</div>
|
||||
{errors.password && touched.password && (
|
||||
<div className="error">{errors.password}</div>
|
||||
)}
|
||||
{errors.password &&
|
||||
touched.password &&
|
||||
typeof errors.password === 'string' && (
|
||||
<div className="error">{errors.password}</div>
|
||||
)}
|
||||
</div>
|
||||
{loginError && (
|
||||
<div className="mt-1 mb-2 sm:col-span-2 sm:mt-0">
|
||||
|
||||
@@ -21,7 +21,7 @@ const messages = defineMessages({
|
||||
signinwithoverseerr: 'Use your {applicationTitle} account',
|
||||
});
|
||||
|
||||
const Login: React.FC = () => {
|
||||
const Login = () => {
|
||||
const intl = useIntl();
|
||||
const [error, setError] = useState('');
|
||||
const [isProcessing, setProcessing] = useState(false);
|
||||
|
||||
@@ -115,9 +115,9 @@ const ManageSlideOver: React.FC<
|
||||
<>
|
||||
{intl.formatMessage(messages.plays, {
|
||||
playCount,
|
||||
strong: function strong(msg) {
|
||||
return <strong className="text-2xl font-semibold">{msg}</strong>;
|
||||
},
|
||||
strong: (msg: React.ReactNode) => (
|
||||
<strong className="text-2xl font-semibold">{msg}</strong>
|
||||
),
|
||||
})}
|
||||
</>
|
||||
);
|
||||
|
||||
@@ -12,7 +12,7 @@ interface ShowMoreCardProps {
|
||||
posters: (string | undefined)[];
|
||||
}
|
||||
|
||||
const ShowMoreCard: React.FC<ShowMoreCardProps> = ({ url, posters }) => {
|
||||
const ShowMoreCard = ({ url, posters }: ShowMoreCardProps) => {
|
||||
const intl = useIntl();
|
||||
const [isHovered, setHovered] = useState(false);
|
||||
return (
|
||||
|
||||
@@ -29,13 +29,13 @@ interface MediaSliderProps {
|
||||
hideWhenEmpty?: boolean;
|
||||
}
|
||||
|
||||
const MediaSlider: React.FC<MediaSliderProps> = ({
|
||||
const MediaSlider = ({
|
||||
title,
|
||||
url,
|
||||
linkUrl,
|
||||
sliderKey,
|
||||
hideWhenEmpty = false,
|
||||
}) => {
|
||||
}: MediaSliderProps) => {
|
||||
const settings = useSettings();
|
||||
const { data, error, setSize, size } = useSWRInfinite<MixedResult>(
|
||||
(pageIndex: number, previousPageData: MixedResult | null) => {
|
||||
|
||||
@@ -14,7 +14,7 @@ const messages = defineMessages({
|
||||
fullcast: 'Full Cast',
|
||||
});
|
||||
|
||||
const MovieCast: React.FC = () => {
|
||||
const MovieCast = () => {
|
||||
const router = useRouter();
|
||||
const intl = useIntl();
|
||||
const { data, error } = useSWR<MovieDetails>(
|
||||
|
||||
@@ -14,7 +14,7 @@ const messages = defineMessages({
|
||||
fullcrew: 'Full Crew',
|
||||
});
|
||||
|
||||
const MovieCrew: React.FC = () => {
|
||||
const MovieCrew = () => {
|
||||
const router = useRouter();
|
||||
const intl = useIntl();
|
||||
const { data, error } = useSWR<MovieDetails>(
|
||||
|
||||
@@ -15,7 +15,7 @@ const messages = defineMessages({
|
||||
recommendations: 'Recommendations',
|
||||
});
|
||||
|
||||
const MovieRecommendations: React.FC = () => {
|
||||
const MovieRecommendations = () => {
|
||||
const intl = useIntl();
|
||||
const router = useRouter();
|
||||
const { data: movieData } = useSWR<MovieDetails>(
|
||||
|
||||
@@ -15,7 +15,7 @@ const messages = defineMessages({
|
||||
similar: 'Similar Titles',
|
||||
});
|
||||
|
||||
const MovieSimilar: React.FC = () => {
|
||||
const MovieSimilar = () => {
|
||||
const router = useRouter();
|
||||
const intl = useIntl();
|
||||
const { data: movieData } = useSWR<MovieDetails>(
|
||||
|
||||
@@ -80,7 +80,7 @@ interface MovieDetailsProps {
|
||||
movie?: MovieDetailsType;
|
||||
}
|
||||
|
||||
const MovieDetails: React.FC<MovieDetailsProps> = ({ movie }) => {
|
||||
const MovieDetails = ({ movie }: MovieDetailsProps) => {
|
||||
const settings = useSettings();
|
||||
const { user, hasPermission } = useUser();
|
||||
const router = useRouter();
|
||||
|
||||
@@ -9,12 +9,12 @@ interface NotificationTypeProps {
|
||||
onUpdate: (newTypes: number) => void;
|
||||
}
|
||||
|
||||
const NotificationType: React.FC<NotificationTypeProps> = ({
|
||||
const NotificationType = ({
|
||||
option,
|
||||
currentTypes,
|
||||
onUpdate,
|
||||
parent,
|
||||
}) => {
|
||||
}: NotificationTypeProps) => {
|
||||
return (
|
||||
<>
|
||||
<div
|
||||
|
||||
@@ -125,13 +125,13 @@ interface NotificationTypeSelectorProps {
|
||||
error?: string;
|
||||
}
|
||||
|
||||
const NotificationTypeSelector: React.FC<NotificationTypeSelectorProps> = ({
|
||||
const NotificationTypeSelector = ({
|
||||
user,
|
||||
enabledTypes = ALL_NOTIFICATIONS,
|
||||
currentTypes,
|
||||
onUpdate,
|
||||
error,
|
||||
}) => {
|
||||
}: NotificationTypeSelectorProps) => {
|
||||
const intl = useIntl();
|
||||
const settings = useSettings();
|
||||
const { hasPermission } = useUser({ id: user?.id });
|
||||
|
||||
@@ -4,9 +4,7 @@ interface PWAHeaderProps {
|
||||
applicationTitle?: string;
|
||||
}
|
||||
|
||||
const PWAHeader: React.FC<PWAHeaderProps> = ({
|
||||
applicationTitle = 'Overseerr',
|
||||
}) => {
|
||||
const PWAHeader = ({ applicationTitle = 'Overseerr' }: PWAHeaderProps) => {
|
||||
return (
|
||||
<>
|
||||
<link
|
||||
|
||||
@@ -70,12 +70,12 @@ interface PermissionEditProps {
|
||||
onUpdate: (newPermissions: number) => void;
|
||||
}
|
||||
|
||||
export const PermissionEdit: React.FC<PermissionEditProps> = ({
|
||||
export const PermissionEdit = ({
|
||||
actingUser,
|
||||
currentUser,
|
||||
currentPermission,
|
||||
onUpdate,
|
||||
}) => {
|
||||
}: PermissionEditProps) => {
|
||||
const intl = useIntl();
|
||||
|
||||
const permissionList: PermissionItem[] = [
|
||||
|
||||
@@ -27,14 +27,14 @@ interface PermissionOptionProps {
|
||||
onUpdate: (newPermissions: number) => void;
|
||||
}
|
||||
|
||||
const PermissionOption: React.FC<PermissionOptionProps> = ({
|
||||
const PermissionOption = ({
|
||||
option,
|
||||
actingUser,
|
||||
currentUser,
|
||||
currentPermission,
|
||||
onUpdate,
|
||||
parent,
|
||||
}) => {
|
||||
}: PermissionOptionProps) => {
|
||||
const settings = useSettings();
|
||||
|
||||
const autoApprovePermissions = [
|
||||
|
||||
@@ -11,13 +11,13 @@ interface PersonCardProps {
|
||||
canExpand?: boolean;
|
||||
}
|
||||
|
||||
const PersonCard: React.FC<PersonCardProps> = ({
|
||||
const PersonCard = ({
|
||||
personId,
|
||||
name,
|
||||
subName,
|
||||
profilePath,
|
||||
canExpand = false,
|
||||
}) => {
|
||||
}: PersonCardProps) => {
|
||||
const [isHovered, setHovered] = useState(false);
|
||||
|
||||
return (
|
||||
|
||||
@@ -24,7 +24,7 @@ const messages = defineMessages({
|
||||
ascharacter: 'as {character}',
|
||||
});
|
||||
|
||||
const PersonDetails: React.FC = () => {
|
||||
const PersonDetails = () => {
|
||||
const intl = useIntl();
|
||||
const router = useRouter();
|
||||
const { data, error } = useSWR<PersonDetailsType>(
|
||||
|
||||
@@ -17,11 +17,11 @@ interface PlexLoginButtonProps {
|
||||
onError?: (message: string) => void;
|
||||
}
|
||||
|
||||
const PlexLoginButton: React.FC<PlexLoginButtonProps> = ({
|
||||
const PlexLoginButton = ({
|
||||
onAuthToken,
|
||||
onError,
|
||||
isProcessing,
|
||||
}) => {
|
||||
}: PlexLoginButtonProps) => {
|
||||
const intl = useIntl();
|
||||
const [loading, setLoading] = useState(false);
|
||||
|
||||
|
||||
@@ -24,7 +24,7 @@ interface QuotaSelectorProps {
|
||||
onChange: (fieldName: string, value: number) => void;
|
||||
}
|
||||
|
||||
const QuotaSelector: React.FC<QuotaSelectorProps> = ({
|
||||
const QuotaSelector = ({
|
||||
mediaType,
|
||||
dayFieldName,
|
||||
limitFieldName,
|
||||
@@ -34,7 +34,7 @@ const QuotaSelector: React.FC<QuotaSelectorProps> = ({
|
||||
limitOverride,
|
||||
isDisabled = false,
|
||||
onChange,
|
||||
}) => {
|
||||
}: QuotaSelectorProps) => {
|
||||
const initialDays = defaultDays ?? 7;
|
||||
const initialLimit = defaultLimit ?? 0;
|
||||
const [quotaDays, setQuotaDays] = useState(initialDays);
|
||||
|
||||
@@ -21,12 +21,12 @@ interface RegionSelectorProps {
|
||||
onChange?: (fieldName: string, region: string) => void;
|
||||
}
|
||||
|
||||
const RegionSelector: React.FC<RegionSelectorProps> = ({
|
||||
const RegionSelector = ({
|
||||
name,
|
||||
value,
|
||||
isUserSetting = false,
|
||||
onChange,
|
||||
}) => {
|
||||
}: RegionSelectorProps) => {
|
||||
const { currentSettings } = useSettings();
|
||||
const intl = useIntl();
|
||||
const { data: regions } = useSWR<Region[]>('/api/v1/regions');
|
||||
|
||||
@@ -34,7 +34,7 @@ interface RequestBlockProps {
|
||||
onUpdate?: () => void;
|
||||
}
|
||||
|
||||
const RequestBlock: React.FC<RequestBlockProps> = ({ request, onUpdate }) => {
|
||||
const RequestBlock = ({ request, onUpdate }: RequestBlockProps) => {
|
||||
const { user } = useUser();
|
||||
const intl = useIntl();
|
||||
const [isUpdating, setIsUpdating] = useState(false);
|
||||
|
||||
@@ -54,14 +54,14 @@ interface RequestButtonProps {
|
||||
is4kShowComplete?: boolean;
|
||||
}
|
||||
|
||||
const RequestButton: React.FC<RequestButtonProps> = ({
|
||||
const RequestButton = ({
|
||||
tmdbId,
|
||||
onUpdate,
|
||||
media,
|
||||
mediaType,
|
||||
isShowComplete = false,
|
||||
is4kShowComplete = false,
|
||||
}) => {
|
||||
}: RequestButtonProps) => {
|
||||
const intl = useIntl();
|
||||
const settings = useSettings();
|
||||
const { user, hasPermission } = useUser();
|
||||
|
||||
@@ -39,7 +39,7 @@ const isMovie = (movie: MovieDetails | TvDetails): movie is MovieDetails => {
|
||||
return (movie as MovieDetails).title !== undefined;
|
||||
};
|
||||
|
||||
const RequestCardPlaceholder: React.FC = () => {
|
||||
const RequestCardPlaceholder = () => {
|
||||
return (
|
||||
<div className="relative w-72 animate-pulse rounded-xl bg-gray-700 p-4 sm:w-96">
|
||||
<div className="w-20 sm:w-28">
|
||||
@@ -53,7 +53,7 @@ interface RequestCardErrorProps {
|
||||
mediaId?: number;
|
||||
}
|
||||
|
||||
const RequestCardError: React.FC<RequestCardErrorProps> = ({ mediaId }) => {
|
||||
const RequestCardError = ({ mediaId }: RequestCardErrorProps) => {
|
||||
const { hasPermission } = useUser();
|
||||
const intl = useIntl();
|
||||
|
||||
@@ -93,7 +93,7 @@ interface RequestCardProps {
|
||||
onTitleData?: (requestId: number, title: MovieDetails | TvDetails) => void;
|
||||
}
|
||||
|
||||
const RequestCard: React.FC<RequestCardProps> = ({ request, onTitleData }) => {
|
||||
const RequestCard = ({ request, onTitleData }: RequestCardProps) => {
|
||||
const { ref, inView } = useInView({
|
||||
triggerOnce: true,
|
||||
});
|
||||
|
||||
@@ -50,10 +50,10 @@ interface RequestItemErroProps {
|
||||
revalidateList: () => void;
|
||||
}
|
||||
|
||||
const RequestItemError: React.FC<RequestItemErroProps> = ({
|
||||
const RequestItemError = ({
|
||||
mediaId,
|
||||
revalidateList,
|
||||
}) => {
|
||||
}: RequestItemErroProps) => {
|
||||
const intl = useIntl();
|
||||
const { hasPermission } = useUser();
|
||||
|
||||
@@ -88,10 +88,7 @@ interface RequestItemProps {
|
||||
revalidateList: () => void;
|
||||
}
|
||||
|
||||
const RequestItem: React.FC<RequestItemProps> = ({
|
||||
request,
|
||||
revalidateList,
|
||||
}) => {
|
||||
const RequestItem = ({ request, revalidateList }: RequestItemProps) => {
|
||||
const { ref, inView } = useInView({
|
||||
triggerOnce: true,
|
||||
});
|
||||
|
||||
@@ -37,7 +37,7 @@ enum Filter {
|
||||
|
||||
type Sort = 'added' | 'modified';
|
||||
|
||||
const RequestList: React.FC = () => {
|
||||
const RequestList = () => {
|
||||
const router = useRouter();
|
||||
const intl = useIntl();
|
||||
const { user } = useUser({
|
||||
@@ -238,9 +238,9 @@ const RequestList: React.FC = () => {
|
||||
? pageIndex * currentPageSize + data.results.length
|
||||
: (pageIndex + 1) * currentPageSize,
|
||||
total: data.pageInfo.results,
|
||||
strong: function strong(msg) {
|
||||
return <span className="font-medium">{msg}</span>;
|
||||
},
|
||||
strong: (msg: React.ReactNode) => (
|
||||
<span className="font-medium">{msg}</span>
|
||||
),
|
||||
})}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
@@ -56,14 +56,14 @@ interface AdvancedRequesterProps {
|
||||
onChange: (overrides: RequestOverrides) => void;
|
||||
}
|
||||
|
||||
const AdvancedRequester: React.FC<AdvancedRequesterProps> = ({
|
||||
const AdvancedRequester = ({
|
||||
type,
|
||||
is4k = false,
|
||||
isAnime = false,
|
||||
defaultOverrides,
|
||||
requestUser,
|
||||
onChange,
|
||||
}) => {
|
||||
}: AdvancedRequesterProps) => {
|
||||
const intl = useIntl();
|
||||
const { user, hasPermission } = useUser();
|
||||
const { data, error } = useSWR<ServiceCommonServer[]>(
|
||||
|
||||
@@ -42,13 +42,13 @@ interface RequestModalProps extends React.HTMLAttributes<HTMLDivElement> {
|
||||
onUpdating?: (isUpdating: boolean) => void;
|
||||
}
|
||||
|
||||
const CollectionRequestModal: React.FC<RequestModalProps> = ({
|
||||
const CollectionRequestModal = ({
|
||||
onCancel,
|
||||
onComplete,
|
||||
tmdbId,
|
||||
onUpdating,
|
||||
is4k = false,
|
||||
}) => {
|
||||
}: RequestModalProps) => {
|
||||
const [isUpdating, setIsUpdating] = useState(false);
|
||||
const [requestOverrides, setRequestOverrides] =
|
||||
useState<RequestOverrides | null>(null);
|
||||
@@ -221,9 +221,7 @@ const CollectionRequestModal: React.FC<RequestModalProps> = ({
|
||||
<span>
|
||||
{intl.formatMessage(messages.requestSuccess, {
|
||||
title: data?.name,
|
||||
strong: function strong(msg) {
|
||||
return <strong>{msg}</strong>;
|
||||
},
|
||||
strong: (msg: React.ReactNode) => <strong>{msg}</strong>,
|
||||
})}
|
||||
</span>,
|
||||
{ appearance: 'success', autoDismiss: true }
|
||||
|
||||
@@ -45,14 +45,14 @@ interface RequestModalProps extends React.HTMLAttributes<HTMLDivElement> {
|
||||
onUpdating?: (isUpdating: boolean) => void;
|
||||
}
|
||||
|
||||
const MovieRequestModal: React.FC<RequestModalProps> = ({
|
||||
const MovieRequestModal = ({
|
||||
onCancel,
|
||||
onComplete,
|
||||
tmdbId,
|
||||
onUpdating,
|
||||
editRequest,
|
||||
is4k = false,
|
||||
}) => {
|
||||
}: RequestModalProps) => {
|
||||
const [isUpdating, setIsUpdating] = useState(false);
|
||||
const [requestOverrides, setRequestOverrides] =
|
||||
useState<RequestOverrides | null>(null);
|
||||
@@ -115,9 +115,7 @@ const MovieRequestModal: React.FC<RequestModalProps> = ({
|
||||
<span>
|
||||
{intl.formatMessage(messages.requestSuccess, {
|
||||
title: data?.title,
|
||||
strong: function strong(msg) {
|
||||
return <strong>{msg}</strong>;
|
||||
},
|
||||
strong: (msg: React.ReactNode) => <strong>{msg}</strong>,
|
||||
})}
|
||||
</span>,
|
||||
{ appearance: 'success', autoDismiss: true }
|
||||
@@ -149,9 +147,7 @@ const MovieRequestModal: React.FC<RequestModalProps> = ({
|
||||
<span>
|
||||
{intl.formatMessage(messages.requestCancel, {
|
||||
title: data?.title,
|
||||
strong: function strong(msg) {
|
||||
return <strong>{msg}</strong>;
|
||||
},
|
||||
strong: (msg: React.ReactNode) => <strong>{msg}</strong>,
|
||||
})}
|
||||
</span>,
|
||||
{ appearance: 'success', autoDismiss: true }
|
||||
@@ -187,9 +183,7 @@ const MovieRequestModal: React.FC<RequestModalProps> = ({
|
||||
: messages.requestedited,
|
||||
{
|
||||
title: data?.title,
|
||||
strong: function strong(msg) {
|
||||
return <strong>{msg}</strong>;
|
||||
},
|
||||
strong: (msg: React.ReactNode) => <strong>{msg}</strong>,
|
||||
}
|
||||
)}
|
||||
</span>,
|
||||
|
||||
@@ -35,13 +35,13 @@ interface QuotaDisplayProps {
|
||||
overLimit?: number;
|
||||
}
|
||||
|
||||
const QuotaDisplay: React.FC<QuotaDisplayProps> = ({
|
||||
const QuotaDisplay = ({
|
||||
quota,
|
||||
mediaType,
|
||||
userOverride,
|
||||
remaining,
|
||||
overLimit,
|
||||
}) => {
|
||||
}: QuotaDisplayProps) => {
|
||||
const intl = useIntl();
|
||||
const [showDetails, setShowDetails] = useState(false);
|
||||
return (
|
||||
@@ -79,9 +79,7 @@ const QuotaDisplay: React.FC<QuotaDisplayProps> = ({
|
||||
type: intl.formatMessage(
|
||||
mediaType === 'movie' ? messages.movie : messages.season
|
||||
),
|
||||
strong: function strong(msg) {
|
||||
return <span className="font-bold">{msg}</span>;
|
||||
},
|
||||
strong: (msg: React.ReactNode) => <strong>{msg}</strong>,
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
@@ -103,9 +101,7 @@ const QuotaDisplay: React.FC<QuotaDisplayProps> = ({
|
||||
: messages.requiredquota,
|
||||
{
|
||||
seasons: overLimit,
|
||||
strong: function strong(msg) {
|
||||
return <span className="font-bold">{msg}</span>;
|
||||
},
|
||||
strong: (msg: React.ReactNode) => <strong>{msg}</strong>,
|
||||
}
|
||||
)}
|
||||
</div>
|
||||
@@ -124,9 +120,7 @@ const QuotaDisplay: React.FC<QuotaDisplayProps> = ({
|
||||
: messages.seasonlimit,
|
||||
{ limit: quota?.limit }
|
||||
),
|
||||
strong: function strong(msg) {
|
||||
return <span className="font-bold">{msg}</span>;
|
||||
},
|
||||
strong: (msg: React.ReactNode) => <strong>{msg}</strong>,
|
||||
}
|
||||
)}
|
||||
</div>
|
||||
@@ -134,19 +128,15 @@ const QuotaDisplay: React.FC<QuotaDisplayProps> = ({
|
||||
{intl.formatMessage(
|
||||
userOverride ? messages.quotaLinkUser : messages.quotaLink,
|
||||
{
|
||||
ProfileLink: function ProfileLink(msg) {
|
||||
return (
|
||||
<Link
|
||||
href={
|
||||
userOverride ? `/users/${userOverride}` : '/profile'
|
||||
}
|
||||
>
|
||||
<a className="text-white transition duration-300 hover:underline">
|
||||
{msg}
|
||||
</a>
|
||||
</Link>
|
||||
);
|
||||
},
|
||||
ProfileLink: (msg: React.ReactNode) => (
|
||||
<Link
|
||||
href={userOverride ? `/users/${userOverride}` : '/profile'}
|
||||
>
|
||||
<a className="text-white transition duration-300 hover:underline">
|
||||
{msg}
|
||||
</a>
|
||||
</Link>
|
||||
),
|
||||
}
|
||||
)}
|
||||
</div>
|
||||
|
||||
@@ -24,7 +24,7 @@ interface SearchByNameModalProps {
|
||||
tmdbId: number;
|
||||
}
|
||||
|
||||
const SearchByNameModal: React.FC<SearchByNameModalProps> = ({
|
||||
const SearchByNameModal = ({
|
||||
setTvdbId,
|
||||
tvdbId,
|
||||
loading,
|
||||
@@ -32,7 +32,7 @@ const SearchByNameModal: React.FC<SearchByNameModalProps> = ({
|
||||
closeModal,
|
||||
modalTitle,
|
||||
tmdbId,
|
||||
}) => {
|
||||
}: SearchByNameModalProps) => {
|
||||
const intl = useIntl();
|
||||
const { data, error } = useSWR<SonarrSeries[]>(
|
||||
`/api/v1/service/sonarr/lookup/${tmdbId}`
|
||||
|
||||
@@ -64,14 +64,14 @@ interface RequestModalProps extends React.HTMLAttributes<HTMLDivElement> {
|
||||
editRequest?: MediaRequest;
|
||||
}
|
||||
|
||||
const TvRequestModal: React.FC<RequestModalProps> = ({
|
||||
const TvRequestModal = ({
|
||||
onCancel,
|
||||
onComplete,
|
||||
tmdbId,
|
||||
onUpdating,
|
||||
editRequest,
|
||||
is4k = false,
|
||||
}) => {
|
||||
}: RequestModalProps) => {
|
||||
const settings = useSettings();
|
||||
const { addToast } = useToasts();
|
||||
const editingSeasons: number[] = (editRequest?.seasons ?? []).map(
|
||||
@@ -141,16 +141,12 @@ const TvRequestModal: React.FC<RequestModalProps> = ({
|
||||
: messages.requestedited,
|
||||
{
|
||||
title: data?.name,
|
||||
strong: function strong(msg) {
|
||||
return <strong>{msg}</strong>;
|
||||
},
|
||||
strong: (msg: React.ReactNode) => <strong>{msg}</strong>,
|
||||
}
|
||||
)
|
||||
: intl.formatMessage(messages.requestcancelled, {
|
||||
title: data?.name,
|
||||
strong: function strong(msg) {
|
||||
return <strong>{msg}</strong>;
|
||||
},
|
||||
strong: (msg: React.ReactNode) => <strong>{msg}</strong>,
|
||||
})}
|
||||
</span>,
|
||||
{
|
||||
@@ -218,9 +214,7 @@ const TvRequestModal: React.FC<RequestModalProps> = ({
|
||||
<span>
|
||||
{intl.formatMessage(messages.requestSuccess, {
|
||||
title: data?.name,
|
||||
strong: function strong(msg) {
|
||||
return <strong>{msg}</strong>;
|
||||
},
|
||||
strong: (msg: React.ReactNode) => <strong>{msg}</strong>,
|
||||
})}
|
||||
</span>,
|
||||
{ appearance: 'success', autoDismiss: true }
|
||||
|
||||
@@ -17,7 +17,7 @@ interface RequestModalProps {
|
||||
onUpdating?: (isUpdating: boolean) => void;
|
||||
}
|
||||
|
||||
const RequestModal: React.FC<RequestModalProps> = ({
|
||||
const RequestModal = ({
|
||||
type,
|
||||
show,
|
||||
tmdbId,
|
||||
@@ -26,7 +26,7 @@ const RequestModal: React.FC<RequestModalProps> = ({
|
||||
onComplete,
|
||||
onUpdating,
|
||||
onCancel,
|
||||
}) => {
|
||||
}: RequestModalProps) => {
|
||||
return (
|
||||
<Transition
|
||||
enter="transition opacity-0 duration-300"
|
||||
|
||||
@@ -21,7 +21,7 @@ const messages = defineMessages({
|
||||
'A password reset link will be sent to the provided email address if it is associated with a valid user.',
|
||||
});
|
||||
|
||||
const ResetPassword: React.FC = () => {
|
||||
const ResetPassword = () => {
|
||||
const intl = useIntl();
|
||||
const [hasSubmitted, setSubmitted] = useState(false);
|
||||
|
||||
@@ -113,9 +113,11 @@ const ResetPassword: React.FC = () => {
|
||||
className="form-input-area block w-full min-w-0 flex-1 rounded-md border border-gray-500 bg-gray-700 text-white transition duration-150 ease-in-out sm:text-sm sm:leading-5"
|
||||
/>
|
||||
</div>
|
||||
{errors.email && touched.email && (
|
||||
<div className="error">{errors.email}</div>
|
||||
)}
|
||||
{errors.email &&
|
||||
touched.email &&
|
||||
typeof errors.email === 'string' && (
|
||||
<div className="error">{errors.email}</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
<div className="mt-4 border-t border-gray-700 pt-5">
|
||||
|
||||
@@ -25,7 +25,7 @@ const messages = defineMessages({
|
||||
resetpasswordsuccessmessage: 'Password reset successfully!',
|
||||
});
|
||||
|
||||
const ResetPassword: React.FC = () => {
|
||||
const ResetPassword = () => {
|
||||
const intl = useIntl();
|
||||
const router = useRouter();
|
||||
const [hasSubmitted, setSubmitted] = useState(false);
|
||||
@@ -129,9 +129,11 @@ const ResetPassword: React.FC = () => {
|
||||
className="form-input-area block w-full min-w-0 flex-1 rounded-md border border-gray-500 bg-gray-700 text-white transition duration-150 ease-in-out sm:text-sm sm:leading-5"
|
||||
/>
|
||||
</div>
|
||||
{errors.password && touched.password && (
|
||||
<div className="error">{errors.password}</div>
|
||||
)}
|
||||
{errors.password &&
|
||||
touched.password &&
|
||||
typeof errors.password === 'string' && (
|
||||
<div className="error">{errors.password}</div>
|
||||
)}
|
||||
</div>
|
||||
<label
|
||||
htmlFor="confirmPassword"
|
||||
|
||||
@@ -17,7 +17,7 @@ const messages = defineMessages({
|
||||
searchresults: 'Search Results',
|
||||
});
|
||||
|
||||
const Search: React.FC = () => {
|
||||
const Search = () => {
|
||||
const intl = useIntl();
|
||||
const router = useRouter();
|
||||
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
/* eslint-disable no-console */
|
||||
import axios from 'axios';
|
||||
import type React from 'react';
|
||||
import { useEffect } from 'react';
|
||||
import useSettings from '../../hooks/useSettings';
|
||||
import { useUser } from '../../hooks/useUser';
|
||||
|
||||
const ServiceWorkerSetup: React.FC = () => {
|
||||
const ServiceWorkerSetup = () => {
|
||||
const { currentSettings } = useSettings();
|
||||
const { user } = useUser();
|
||||
useEffect(() => {
|
||||
|
||||
@@ -8,7 +8,7 @@ const messages = defineMessages({
|
||||
copied: 'Copied API key to clipboard.',
|
||||
});
|
||||
|
||||
const CopyButton: React.FC<{ textToCopy: string }> = ({ textToCopy }) => {
|
||||
const CopyButton = ({ textToCopy }: { textToCopy: string }) => {
|
||||
const intl = useIntl();
|
||||
const [isCopied, setCopied] = useClipboard(textToCopy, {
|
||||
successDuration: 1000,
|
||||
|
||||
@@ -7,11 +7,7 @@ interface LibraryItemProps {
|
||||
onToggle: () => void;
|
||||
}
|
||||
|
||||
const LibraryItem: React.FC<LibraryItemProps> = ({
|
||||
isEnabled,
|
||||
name,
|
||||
onToggle,
|
||||
}) => {
|
||||
const LibraryItem = ({ isEnabled, name, onToggle }: LibraryItemProps) => {
|
||||
return (
|
||||
<li className="col-span-1 flex rounded-md shadow-sm">
|
||||
<div className="flex flex-1 items-center justify-between truncate rounded-md border-t border-b border-r border-gray-700 bg-gray-600">
|
||||
|
||||
@@ -29,7 +29,7 @@ const messages = defineMessages({
|
||||
enableMentions: 'Enable Mentions',
|
||||
});
|
||||
|
||||
const NotificationsDiscord: React.FC = () => {
|
||||
const NotificationsDiscord = () => {
|
||||
const intl = useIntl();
|
||||
const settings = useSettings();
|
||||
const { addToast, removeToast } = useToasts();
|
||||
@@ -168,18 +168,16 @@ const NotificationsDiscord: React.FC = () => {
|
||||
<span className="label-required">*</span>
|
||||
<span className="label-tip">
|
||||
{intl.formatMessage(messages.webhookUrlTip, {
|
||||
DiscordWebhookLink: function DiscordWebhookLink(msg) {
|
||||
return (
|
||||
<a
|
||||
href="https://support.discord.com/hc/en-us/articles/228383668-Intro-to-Webhooks"
|
||||
className="text-white transition duration-300 hover:underline"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
>
|
||||
{msg}
|
||||
</a>
|
||||
);
|
||||
},
|
||||
DiscordWebhookLink: (msg: React.ReactNode) => (
|
||||
<a
|
||||
href="https://support.discord.com/hc/en-us/articles/228383668-Intro-to-Webhooks"
|
||||
className="text-white transition duration-300 hover:underline"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
>
|
||||
{msg}
|
||||
</a>
|
||||
),
|
||||
})}
|
||||
</span>
|
||||
</label>
|
||||
@@ -192,9 +190,11 @@ const NotificationsDiscord: React.FC = () => {
|
||||
inputMode="url"
|
||||
/>
|
||||
</div>
|
||||
{errors.webhookUrl && touched.webhookUrl && (
|
||||
<div className="error">{errors.webhookUrl}</div>
|
||||
)}
|
||||
{errors.webhookUrl &&
|
||||
touched.webhookUrl &&
|
||||
typeof errors.webhookUrl === 'string' && (
|
||||
<div className="error">{errors.webhookUrl}</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
<div className="form-row">
|
||||
@@ -210,9 +210,11 @@ const NotificationsDiscord: React.FC = () => {
|
||||
placeholder={settings.currentSettings.applicationTitle}
|
||||
/>
|
||||
</div>
|
||||
{errors.botUsername && touched.botUsername && (
|
||||
<div className="error">{errors.botUsername}</div>
|
||||
)}
|
||||
{errors.botUsername &&
|
||||
touched.botUsername &&
|
||||
typeof errors.botUsername === 'string' && (
|
||||
<div className="error">{errors.botUsername}</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
<div className="form-row">
|
||||
@@ -228,9 +230,11 @@ const NotificationsDiscord: React.FC = () => {
|
||||
inputMode="url"
|
||||
/>
|
||||
</div>
|
||||
{errors.botAvatarUrl && touched.botAvatarUrl && (
|
||||
<div className="error">{errors.botAvatarUrl}</div>
|
||||
)}
|
||||
{errors.botAvatarUrl &&
|
||||
touched.botAvatarUrl &&
|
||||
typeof errors.botAvatarUrl === 'string' && (
|
||||
<div className="error">{errors.botAvatarUrl}</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
<div className="form-row">
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user