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