Merge remote-tracking branch 'overseerr/develop' into develop

This commit is contained in:
notfakie
2022-09-01 18:11:15 +12:00
473 changed files with 15548 additions and 8433 deletions

View File

@@ -1,12 +1,12 @@
import { PencilIcon } from '@heroicons/react/solid';
import Modal from '@app/components/Common/Modal';
import PermissionEdit from '@app/components/PermissionEdit';
import type { User } from '@app/hooks/useUser';
import { useUser } from '@app/hooks/useUser';
import globalMessages from '@app/i18n/globalMessages';
import axios from 'axios';
import React, { useEffect, useState } from 'react';
import { useEffect, useState } from 'react';
import { defineMessages, useIntl } from 'react-intl';
import { useToasts } from 'react-toast-notifications';
import { User, useUser } from '../../hooks/useUser';
import globalMessages from '../../i18n/globalMessages';
import Modal from '../Common/Modal';
import PermissionEdit from '../PermissionEdit';
interface BulkEditProps {
selectedUserIds: number[];
@@ -22,13 +22,13 @@ const messages = defineMessages({
edituser: 'Edit User Permissions',
});
const BulkEditModal: React.FC<BulkEditProps> = ({
const BulkEditModal = ({
selectedUserIds,
users,
onCancel,
onComplete,
onSaving,
}) => {
}: BulkEditProps) => {
const { user: currentUser } = useUser();
const intl = useIntl();
const { addToast } = useToasts();
@@ -85,7 +85,6 @@ const BulkEditModal: React.FC<BulkEditProps> = ({
return (
<Modal
title={intl.formatMessage(messages.edituser)}
iconSvg={<PencilIcon />}
onOk={() => {
updateUsers();
}}

View File

@@ -1,19 +1,20 @@
import { InboxInIcon } from '@heroicons/react/solid';
import Alert from '@app/components/Common/Alert';
import Modal from '@app/components/Common/Modal';
import useSettings from '@app/hooks/useSettings';
import globalMessages from '@app/i18n/globalMessages';
import type { UserResultsResponse } from '@server/interfaces/api/userInterfaces';
import axios from 'axios';
import React, { useState } from 'react';
import getConfig from 'next/config';
import type React from 'react';
import { useState } from 'react';
import { defineMessages, useIntl } from 'react-intl';
import { useToasts } from 'react-toast-notifications';
import useSWR from 'swr';
import useSettings from '../../hooks/useSettings';
import globalMessages from '../../i18n/globalMessages';
import Alert from '../Common/Alert';
import Modal from '../Common/Modal';
import getConfig from 'next/config';
import { UserResultsResponse } from '../../../server/interfaces/api/userInterfaces';
interface JellyfinImportProps {
onCancel?: () => void;
onComplete?: () => void;
children?: React.ReactNode;
}
const messages = defineMessages({
@@ -79,9 +80,7 @@ const JellyfinImportModal: React.FC<JellyfinImportProps> = ({
addToast(
intl.formatMessage(messages.importedfromJellyfin, {
userCount: createdUsers.length,
strong: function strong(msg) {
return <strong>{msg}</strong>;
},
strong: (msg: React.ReactNode) => <strong>{msg}</strong>,
mediaServerName:
publicRuntimeConfig.JELLYFIN_TYPE == 'emby' ? 'Emby' : 'Jellyfin',
}),
@@ -138,7 +137,6 @@ const JellyfinImportModal: React.FC<JellyfinImportProps> = ({
mediaServerName:
publicRuntimeConfig.JELLYFIN_TYPE == 'emby' ? 'Emby' : 'Jellyfin',
})}
iconSvg={<InboxInIcon />}
onOk={() => {
importUsers();
}}
@@ -157,11 +155,9 @@ const JellyfinImportModal: React.FC<JellyfinImportProps> = ({
publicRuntimeConfig.JELLYFIN_TYPE == 'emby'
? 'Emby'
: 'Jellyfin',
strong: function strong(msg) {
return (
<strong className="font-semibold text-white">{msg}</strong>
);
},
strong: (msg: React.ReactNode) => (
<strong className="font-semibold text-white">{msg}</strong>
),
})}
type="info"
/>

View File

@@ -1,13 +1,12 @@
import { InboxInIcon } from '@heroicons/react/solid';
import Alert from '@app/components/Common/Alert';
import Modal from '@app/components/Common/Modal';
import useSettings from '@app/hooks/useSettings';
import globalMessages from '@app/i18n/globalMessages';
import axios from 'axios';
import React, { useState } from 'react';
import { useState } from 'react';
import { defineMessages, useIntl } from 'react-intl';
import { useToasts } from 'react-toast-notifications';
import useSWR from 'swr';
import useSettings from '../../hooks/useSettings';
import globalMessages from '../../i18n/globalMessages';
import Alert from '../Common/Alert';
import Modal from '../Common/Modal';
interface PlexImportProps {
onCancel?: () => void;
@@ -25,10 +24,7 @@ const messages = defineMessages({
'The <strong>Enable New Plex Sign-In</strong> setting is currently enabled. Plex users with library access do not need to be imported in order to sign in.',
});
const PlexImportModal: React.FC<PlexImportProps> = ({
onCancel,
onComplete,
}) => {
const PlexImportModal = ({ onCancel, onComplete }: PlexImportProps) => {
const intl = useIntl();
const settings = useSettings();
const { addToast } = useToasts();
@@ -62,9 +58,7 @@ const PlexImportModal: React.FC<PlexImportProps> = ({
addToast(
intl.formatMessage(messages.importedfromplex, {
userCount: createdUsers.length,
strong: function strong(msg) {
return <strong>{msg}</strong>;
},
strong: (msg: React.ReactNode) => <strong>{msg}</strong>,
}),
{
autoDismiss: true,
@@ -110,7 +104,6 @@ const PlexImportModal: React.FC<PlexImportProps> = ({
<Modal
loading={!data && !error}
title={intl.formatMessage(messages.importfromplex)}
iconSvg={<InboxInIcon />}
onOk={() => {
importUsers();
}}
@@ -125,11 +118,9 @@ const PlexImportModal: React.FC<PlexImportProps> = ({
{settings.currentSettings.newPlexLogin && (
<Alert
title={intl.formatMessage(messages.newplexsigninenabled, {
strong: function strong(msg) {
return (
<strong className="font-semibold text-white">{msg}</strong>
);
},
strong: (msg: React.ReactNode) => (
<strong className="font-semibold text-white">{msg}</strong>
),
})}
type="info"
/>

View File

@@ -1,4 +1,20 @@
import { TrashIcon } from '@heroicons/react/outline';
import Alert from '@app/components/Common/Alert';
import Badge from '@app/components/Common/Badge';
import Button from '@app/components/Common/Button';
import Header from '@app/components/Common/Header';
import LoadingSpinner from '@app/components/Common/LoadingSpinner';
import Modal from '@app/components/Common/Modal';
import PageTitle from '@app/components/Common/PageTitle';
import SensitiveInput from '@app/components/Common/SensitiveInput';
import Table from '@app/components/Common/Table';
import BulkEditModal from '@app/components/UserList/BulkEditModal';
import PlexImportModal from '@app/components/UserList/PlexImportModal';
import useSettings from '@app/hooks/useSettings';
import { useUpdateQueryParams } from '@app/hooks/useUpdateQueryParams';
import type { User } from '@app/hooks/useUser';
import { Permission, UserType, useUser } from '@app/hooks/useUser';
import globalMessages from '@app/i18n/globalMessages';
import { Transition } from '@headlessui/react';
import {
ChevronLeftIcon,
ChevronRightIcon,
@@ -7,36 +23,20 @@ import {
SortDescendingIcon,
UserAddIcon,
} from '@heroicons/react/solid';
import { MediaServerType } from '@server/constants/server';
import type { UserResultsResponse } from '@server/interfaces/api/userInterfaces';
import { hasPermission } from '@server/lib/permissions';
import axios from 'axios';
import { Field, Form, Formik } from 'formik';
import getConfig from 'next/config';
import Link from 'next/link';
import { useRouter } from 'next/router';
import React, { useEffect, useState } from 'react';
import { useEffect, useState } from 'react';
import { defineMessages, useIntl } from 'react-intl';
import { useToasts } from 'react-toast-notifications';
import useSWR from 'swr';
import * as Yup from 'yup';
import { MediaServerType } from '../../../server/constants/server';
import type { UserResultsResponse } from '../../../server/interfaces/api/userInterfaces';
import { hasPermission } from '../../../server/lib/permissions';
import useSettings from '../../hooks/useSettings';
import { useUpdateQueryParams } from '../../hooks/useUpdateQueryParams';
import { Permission, User, UserType, useUser } from '../../hooks/useUser';
import globalMessages from '../../i18n/globalMessages';
import Alert from '../Common/Alert';
import Badge from '../Common/Badge';
import Button from '../Common/Button';
import Header from '../Common/Header';
import LoadingSpinner from '../Common/LoadingSpinner';
import Modal from '../Common/Modal';
import PageTitle from '../Common/PageTitle';
import SensitiveInput from '../Common/SensitiveInput';
import Table from '../Common/Table';
import Transition from '../Transition';
import BulkEditModal from './BulkEditModal';
import JellyfinImportModal from './JellyfinImportModal';
import PlexImportModal from './PlexImportModal';
const messages = defineMessages({
users: 'Users',
@@ -84,7 +84,7 @@ const messages = defineMessages({
type Sort = 'created' | 'updated' | 'requests' | 'displayname';
const UserList: React.FC = () => {
const UserList = () => {
const intl = useIntl();
const router = useRouter();
const settings = useSettings();
@@ -187,7 +187,7 @@ const UserList: React.FC = () => {
autoDismiss: true,
appearance: 'success',
});
setDeleteModal({ isOpen: false });
setDeleteModal({ isOpen: false, user: deleteModal.user });
} catch (e) {
addToast(intl.formatMessage(messages.userdeleteerror), {
autoDismiss: true,
@@ -232,6 +232,7 @@ const UserList: React.FC = () => {
<>
<PageTitle title={intl.formatMessage(messages.users)} />
<Transition
as="div"
enter="opacity-0 transition duration-300"
enterFrom="opacity-0"
enterTo="opacity-100"
@@ -249,15 +250,18 @@ const UserList: React.FC = () => {
}
okDisabled={isDeleting}
okButtonType="danger"
onCancel={() => setDeleteModal({ isOpen: false })}
onCancel={() =>
setDeleteModal({ isOpen: false, user: deleteModal.user })
}
title={intl.formatMessage(messages.deleteuser)}
iconSvg={<TrashIcon />}
subTitle={deleteModal.user?.displayName}
>
{intl.formatMessage(messages.deleteconfirm)}
</Modal>
</Transition>
<Transition
as="div"
enter="opacity-0 transition duration-300"
enterFrom="opacity-0"
enterTo="opacity-100"
@@ -315,7 +319,6 @@ const UserList: React.FC = () => {
return (
<Modal
title={intl.formatMessage(messages.createlocaluser)}
iconSvg={<UserAddIcon />}
onOk={() => handleSubmit()}
okText={
isSubmitting
@@ -329,18 +332,16 @@ const UserList: React.FC = () => {
{!settings.currentSettings.localLogin && (
<Alert
title={intl.formatMessage(messages.localLoginDisabled, {
strong: function strong(msg) {
return (
<strong className="font-semibold text-white">
{msg}
</strong>
);
},
strong: (msg: React.ReactNode) => (
<strong className="font-semibold text-white">
{msg}
</strong>
),
})}
type="warning"
/>
)}
{currentHasPermission(Permission.MANAGE_SETTINGS) &&
{currentHasPermission(Permission.ADMIN) &&
!passwordGenerationEnabled && (
<Alert
title={intl.formatMessage(
@@ -378,9 +379,11 @@ const UserList: React.FC = () => {
inputMode="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>
</div>
<div
@@ -426,9 +429,11 @@ const UserList: React.FC = () => {
disabled={values.genpassword}
/>
</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>
</div>
</Form>
@@ -439,6 +444,7 @@ const UserList: React.FC = () => {
</Transition>
<Transition
as="div"
enter="opacity-0 transition duration-300"
enterFrom="opacity-0"
enterTo="opacity-100"
@@ -459,6 +465,7 @@ const UserList: React.FC = () => {
</Transition>
<Transition
as="div"
enter="opacity-0 transition duration-300"
enterFrom="opacity-0"
enterTo="opacity-100"
@@ -586,7 +593,7 @@ const UserList: React.FC = () => {
</thead>
<Table.TBody>
{data?.results.map((user) => (
<tr key={`user-list-${user.id}`}>
<tr key={`user-list-${user.id}`} data-testid="user-list-row">
<Table.TD>
{isUserPermsEditable(user.id) && (
<input
@@ -613,7 +620,10 @@ const UserList: React.FC = () => {
</Link>
<div className="ml-4">
<Link href={`/users/${user.id}`}>
<a className="text-base font-bold leading-5 transition duration-300 hover:underline">
<a
className="text-base font-bold leading-5 transition duration-300 hover:underline"
data-testid="user-list-username-link"
>
{user.displayName}
</a>
</Link>
@@ -721,9 +731,9 @@ const UserList: 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>