From a231eba896051044372a48a1da715dba81cbfd3e Mon Sep 17 00:00:00 2001 From: Ryan Cohen Date: Fri, 2 Sep 2022 14:49:43 +0900 Subject: [PATCH 1/9] refactor: decouple Plex as a requirement for setting up Overseerr --- cypress/e2e/discover.cy.ts | 1 - overseerr-api.yml | 12 +- server/constants/user.ts | 4 - server/entity/User.ts | 42 +++-- server/lib/scanners/plex/index.ts | 4 + server/routes/auth.ts | 55 ++++--- server/routes/settings/index.ts | 27 ++- server/routes/user/index.ts | 10 +- server/scripts/prepareTestDb.ts | 3 - src/assets/services/plex.svg | 26 ++- src/components/Discover/index.tsx | 6 +- src/components/PlexLoginButton/index.tsx | 4 + src/components/Settings/SettingsPlex.tsx | 47 +++++- .../Setup/ConfigureMediaServers/index.tsx | 104 ++++++++++++ src/components/Setup/CreateAccount/index.tsx | 155 ++++++++++++++++++ src/components/Setup/LoginWithPlex.tsx | 14 +- src/components/Setup/index.tsx | 39 +---- src/components/UserList/index.tsx | 4 +- .../UserGeneralSettings/index.tsx | 8 +- src/components/UserProfile/index.tsx | 10 +- src/hooks/useUser.ts | 6 +- src/i18n/locale/en.json | 2 +- 22 files changed, 460 insertions(+), 123 deletions(-) delete mode 100644 server/constants/user.ts create mode 100644 src/components/Setup/ConfigureMediaServers/index.tsx create mode 100644 src/components/Setup/CreateAccount/index.tsx diff --git a/cypress/e2e/discover.cy.ts b/cypress/e2e/discover.cy.ts index 3489061b..38943322 100644 --- a/cypress/e2e/discover.cy.ts +++ b/cypress/e2e/discover.cy.ts @@ -145,7 +145,6 @@ describe('Discover', () => { plexUsername: null, username: '', recoveryLinkExpirationDate: null, - userType: 2, avatar: 'https://gravatar.com/avatar/c77fdc27cab83732b8623d2ea873d330?default=mm&size=200', movieQuotaLimit: null, diff --git a/overseerr-api.yml b/overseerr-api.yml index 164187de..80d80c5e 100644 --- a/overseerr-api.yml +++ b/overseerr-api.yml @@ -61,10 +61,6 @@ components: plexUsername: type: string readOnly: true - userType: - type: integer - example: 1 - readOnly: true permissions: type: number example: 0 @@ -83,6 +79,14 @@ components: type: number example: 5 readOnly: true + isPlexUser: + type: boolean + example: false + readOnly: true + isLocalUser: + type: boolean + example: true + readOnly: true required: - id - email diff --git a/server/constants/user.ts b/server/constants/user.ts deleted file mode 100644 index 38aa50a4..00000000 --- a/server/constants/user.ts +++ /dev/null @@ -1,4 +0,0 @@ -export enum UserType { - PLEX = 1, - LOCAL = 2, -} diff --git a/server/entity/User.ts b/server/entity/User.ts index 0c540fee..6abf413e 100644 --- a/server/entity/User.ts +++ b/server/entity/User.ts @@ -1,5 +1,4 @@ import { MediaRequestStatus, MediaType } from '@server/constants/media'; -import { UserType } from '@server/constants/user'; import { getRepository } from '@server/datasource'; import type { QuotaResponse } from '@server/interfaces/api/userInterfaces'; import PreparedEmail from '@server/lib/email'; @@ -35,14 +34,22 @@ export class User { public static filterMany( users: User[], showFiltered?: boolean - ): Partial[] { + ): Omit[] { return users.map((u) => u.filter(showFiltered)); } - static readonly filteredFields: string[] = ['email']; + // Fields that show only be shown to admins in user API responses + static readonly filteredFields: (keyof User)[] = ['email', 'plexId']; + + // Fields that should never be shown in API responses + static readonly secureFields: (keyof User)[] = ['password']; public displayName: string; + public isPlexUser: boolean; + + public isLocalUser: boolean; + @PrimaryGeneratedColumn() public id: number; @@ -61,7 +68,7 @@ export class User { @Column({ nullable: true }) public username?: string; - @Column({ nullable: true, select: false }) + @Column({ nullable: true }) public password?: string; @Column({ nullable: true, select: false }) @@ -70,10 +77,7 @@ export class User { @Column({ type: 'date', nullable: true }) public recoveryLinkExpirationDate?: Date | null; - @Column({ type: 'integer', default: UserType.PLEX }) - public userType: UserType; - - @Column({ nullable: true, select: false }) + @Column({ nullable: true }) public plexId?: number; @Column({ nullable: true, select: false }) @@ -126,13 +130,17 @@ export class User { Object.assign(this, init); } - public filter(showFiltered?: boolean): Partial { - const filtered: Partial = Object.assign( - {}, - ...(Object.keys(this) as (keyof User)[]) - .filter((k) => showFiltered || !User.filteredFields.includes(k)) - .map((k) => ({ [k]: this[k] })) - ); + public filter( + showFiltered?: boolean + ): Omit { + const filtered: Omit = + Object.assign( + {}, + ...(Object.keys(this) as (keyof User)[]) + .filter((k) => showFiltered || !User.filteredFields.includes(k)) + .filter((k) => !User.secureFields.includes(k)) + .map((k) => ({ [k]: this[k] })) + ); return filtered; } @@ -229,8 +237,10 @@ export class User { } @AfterLoad() - public setDisplayName(): void { + public setLocalProperties(): void { this.displayName = this.username || this.plexUsername || this.email; + this.isPlexUser = !!this.plexId; + this.isLocalUser = !!this.password; } public async getQuota(): Promise { diff --git a/server/lib/scanners/plex/index.ts b/server/lib/scanners/plex/index.ts index 73e4d9b2..a5db2259 100644 --- a/server/lib/scanners/plex/index.ts +++ b/server/lib/scanners/plex/index.ts @@ -70,6 +70,10 @@ class PlexScanner return this.log('No admin configured. Plex scan skipped.', 'warn'); } + if (!admin.plexToken || !settings.plex.ip) { + return this.log('Plex server is not configured.', 'warn'); + } + this.plexClient = new PlexAPI({ plexToken: admin.plexToken }); this.libraries = settings.plex.libraries.filter( diff --git a/server/routes/auth.ts b/server/routes/auth.ts index cf4a4e86..09c013b7 100644 --- a/server/routes/auth.ts +++ b/server/routes/auth.ts @@ -1,5 +1,4 @@ import PlexTvAPI from '@server/api/plextv'; -import { UserType } from '@server/constants/user'; import { getRepository } from '@server/datasource'; import { User } from '@server/entity/User'; import { Permission } from '@server/lib/permissions'; @@ -7,6 +6,7 @@ import { getSettings } from '@server/lib/settings'; import logger from '@server/logger'; import { isAuthenticated } from '@server/middleware/auth'; import { Router } from 'express'; +import gravatarUrl from 'gravatar-url'; const authRoutes = Router(); @@ -41,14 +41,21 @@ authRoutes.post('/plex', async (req, res, next) => { const plextv = new PlexTvAPI(body.authToken); const account = await plextv.getUser(); - // Next let's see if the user already exists - let user = await userRepository - .createQueryBuilder('user') - .where('user.plexId = :id', { id: account.id }) - .orWhere('user.email = :email', { - email: account.email.toLowerCase(), - }) - .getOne(); + let user: User | null; + + // If we are already logged in, we should just get the currently logged in user + // otherwise we will try to match to an existing users email or plex ID + if (req.user) { + user = await userRepository.findOneBy({ id: req.user.id }); + } else { + user = await userRepository + .createQueryBuilder('user') + .where('user.plexId = :id', { id: account.id }) + .orWhere('user.email = :email', { + email: account.email.toLowerCase(), + }) + .getOne(); + } if (!user && !(await userRepository.count())) { user = new User({ @@ -58,7 +65,6 @@ authRoutes.post('/plex', async (req, res, next) => { plexToken: account.authToken, permissions: Permission.ADMIN, avatar: account.thumb, - userType: UserType.PLEX, }); await userRepository.save(user); @@ -71,12 +77,13 @@ authRoutes.post('/plex', async (req, res, next) => { if ( account.id === mainUser.plexId || + (user && user.id === 1 && !user.plexId) || (await mainPlexTv.checkUserAccess(account.id)) ) { if (user) { if (!user.plexId) { logger.info( - 'Found matching Plex user; updating user with Plex data', + 'Found matching Plex user; updating user with Plex data. Notice: Emails are no longer synced.', { label: 'API', ip: req.ip, @@ -91,9 +98,7 @@ authRoutes.post('/plex', async (req, res, next) => { user.plexToken = body.authToken; user.plexId = account.id; user.avatar = account.thumb; - user.email = account.email; user.plexUsername = account.username; - user.userType = UserType.PLEX; await userRepository.save(user); } else if (!settings.main.newPlexLogin) { @@ -129,7 +134,6 @@ authRoutes.post('/plex', async (req, res, next) => { plexToken: account.authToken, permissions: settings.main.defaultPermissions, avatar: account.thumb, - userType: UserType.PLEX, }); await userRepository.save(user); @@ -184,13 +188,22 @@ authRoutes.post('/local', async (req, res, next) => { }); } try { - const user = await userRepository + let user = await userRepository .createQueryBuilder('user') .select(['user.id', 'user.email', 'user.password', 'user.plexId']) .where('user.email = :email', { email: body.email.toLowerCase() }) .getOne(); - if (!user || !(await user.passwordMatch(body.password))) { + if (!user && !(await userRepository.count())) { + const avatar = gravatarUrl(body.email, { default: 'mm', size: 200 }); + user = new User({ + email: body.email, + permissions: Permission.ADMIN, + avatar, + }); + await user.setPassword(body.password); + await userRepository.save(user); + } else if (!user || !(await user.passwordMatch(body.password))) { logger.warn('Failed sign-in attempt using invalid Overseerr password', { label: 'API', ip: req.ip, @@ -203,19 +216,19 @@ authRoutes.post('/local', async (req, res, next) => { }); } - const mainUser = await userRepository.findOneOrFail({ + const mainUser = await userRepository.findOne({ select: { id: true, plexToken: true, plexId: true }, where: { id: 1 }, }); - const mainPlexTv = new PlexTvAPI(mainUser.plexToken ?? ''); + const mainPlexTv = new PlexTvAPI(mainUser?.plexToken ?? ''); - if (!user.plexId) { + if (!user.plexId && mainUser?.isPlexUser) { try { const plexUsersResponse = await mainPlexTv.getUsers(); const account = plexUsersResponse.MediaContainer.User.find( (account) => account.$.email && - account.$.email.toLowerCase() === user.email.toLowerCase() + account.$.email.toLowerCase() === user?.email.toLowerCase() )?.$; if ( @@ -238,7 +251,6 @@ authRoutes.post('/local', async (req, res, next) => { user.avatar = account.thumb; user.email = account.email; user.plexUsername = account.username; - user.userType = UserType.PLEX; await userRepository.save(user); } @@ -251,6 +263,7 @@ authRoutes.post('/local', async (req, res, next) => { } if ( + mainUser?.isPlexUser && user.plexId && user.plexId !== mainUser.plexId && !(await mainPlexTv.checkUserAccess(user.plexId)) diff --git a/server/routes/settings/index.ts b/server/routes/settings/index.ts index 41d2c745..29daee54 100644 --- a/server/routes/settings/index.ts +++ b/server/routes/settings/index.ts @@ -16,7 +16,7 @@ import type { AvailableCacheIds } from '@server/lib/cache'; import cacheManager from '@server/lib/cache'; import { Permission } from '@server/lib/permissions'; import { plexFullScanner } from '@server/lib/scanners/plex'; -import type { MainSettings } from '@server/lib/settings'; +import type { MainSettings, PlexSettings } from '@server/lib/settings'; import { getSettings } from '@server/lib/settings'; import logger from '@server/logger'; import { isAuthenticated } from '@server/middleware/auth'; @@ -82,10 +82,25 @@ settingsRoutes.post('/main/regenerate', (req, res, next) => { return res.status(200).json(filteredMainSettings(req.user, main)); }); -settingsRoutes.get('/plex', (_req, res) => { +type PlexSettingsResponse = PlexSettings & { + plexAvailable: boolean; +}; + +settingsRoutes.get('/plex', async (_req, res) => { const settings = getSettings(); + const userRepository = getRepository(User); + + const admin = await userRepository.findOneOrFail({ + select: { id: true, plexToken: true }, + where: { id: 1 }, + }); - res.status(200).json(settings.plex); + const settingsResponse: PlexSettingsResponse = { + ...settings.plex, + plexAvailable: !!admin.plexToken, + }; + + res.status(200).json(settingsResponse); }); settingsRoutes.post('/plex', async (req, res, next) => { @@ -97,6 +112,12 @@ settingsRoutes.post('/plex', async (req, res, next) => { where: { id: 1 }, }); + if (!admin.plexToken) { + throw new Error( + 'The administrator must have their account connected to Plex to be able to set up a Plex server.' + ); + } + Object.assign(settings.plex, req.body); const plexClient = new PlexAPI({ plexToken: admin.plexToken }); diff --git a/server/routes/user/index.ts b/server/routes/user/index.ts index f77b7e51..bd7707c4 100644 --- a/server/routes/user/index.ts +++ b/server/routes/user/index.ts @@ -1,7 +1,6 @@ import PlexTvAPI from '@server/api/plextv'; import TautulliAPI from '@server/api/tautulli'; import { MediaType } from '@server/constants/media'; -import { UserType } from '@server/constants/user'; import { getRepository } from '@server/datasource'; import Media from '@server/entity/Media'; import { MediaRequest } from '@server/entity/MediaRequest'; @@ -121,7 +120,6 @@ router.post( password: body.password, permissions: settings.main.defaultPermissions, plexToken: '', - userType: UserType.LOCAL, }); if (passedExplicitPassword) { @@ -434,12 +432,7 @@ router.post( user.avatar = account.thumb; user.email = account.email; user.plexUsername = account.username; - - // In case the user was previously a local account - if (user.userType === UserType.LOCAL) { - user.userType = UserType.PLEX; - user.plexId = parseInt(account.id); - } + user.plexId = parseInt(account.id); await userRepository.save(user); } else if (!body || body.plexIds.includes(account.id)) { if (await mainPlexTv.checkUserAccess(parseInt(account.id))) { @@ -450,7 +443,6 @@ router.post( plexId: parseInt(account.id), plexToken: '', avatar: account.thumb, - userType: UserType.PLEX, }); await userRepository.save(newUser); createdUsers.push(newUser); diff --git a/server/scripts/prepareTestDb.ts b/server/scripts/prepareTestDb.ts index 7caede41..9f0eb776 100644 --- a/server/scripts/prepareTestDb.ts +++ b/server/scripts/prepareTestDb.ts @@ -1,4 +1,3 @@ -import { UserType } from '@server/constants/user'; import dataSource, { getRepository } from '@server/datasource'; import { User } from '@server/entity/User'; import { copyFileSync } from 'fs'; @@ -43,7 +42,6 @@ const prepareDb = async () => { user.plexUsername = 'admin'; user.username = 'admin'; user.email = 'admin@seerr.dev'; - user.userType = UserType.PLEX; await user.setPassword('test1234'); user.permissions = 2; user.avatar = gravatarUrl('admin@seerr.dev', { default: 'mm', size: 200 }); @@ -59,7 +57,6 @@ const prepareDb = async () => { otherUser.plexUsername = 'friend'; otherUser.username = 'friend'; otherUser.email = 'friend@seerr.dev'; - otherUser.userType = UserType.PLEX; await otherUser.setPassword('test1234'); otherUser.permissions = 32; otherUser.avatar = gravatarUrl('friend@seerr.dev', { diff --git a/src/assets/services/plex.svg b/src/assets/services/plex.svg index 5991b25d..62100de2 100644 --- a/src/assets/services/plex.svg +++ b/src/assets/services/plex.svg @@ -1 +1,25 @@ - \ No newline at end of file + + + diff --git a/src/components/Discover/index.tsx b/src/components/Discover/index.tsx index 24dc6fea..3eb05177 100644 --- a/src/components/Discover/index.tsx +++ b/src/components/Discover/index.tsx @@ -7,7 +7,7 @@ import MediaSlider from '@app/components/MediaSlider'; import RequestCard from '@app/components/RequestCard'; import Slider from '@app/components/Slider'; import TmdbTitleCard from '@app/components/TitleCard/TmdbTitleCard'; -import { Permission, UserType, useUser } from '@app/hooks/useUser'; +import { Permission, useUser } from '@app/hooks/useUser'; import { ArrowCircleRightIcon } from '@heroicons/react/outline'; import type { WatchlistItem } from '@server/interfaces/api/discoverInterfaces'; import type { MediaResultsResponse } from '@server/interfaces/api/mediaInterfaces'; @@ -52,7 +52,7 @@ const Discover = () => { totalPages: number; totalResults: number; results: WatchlistItem[]; - }>(user?.userType === UserType.PLEX ? '/api/v1/discover/watchlist' : null, { + }>(user?.isPlexUser ? '/api/v1/discover/watchlist' : null, { revalidateOnMount: true, }); @@ -108,7 +108,7 @@ const Discover = () => { /> )} - {user?.userType === UserType.PLEX && + {user?.isPlexUser && (!watchlistItems || !!watchlistItems.results.length || user.settings?.watchlistSyncMovies || diff --git a/src/components/PlexLoginButton/index.tsx b/src/components/PlexLoginButton/index.tsx index c89f1021..f28a1e23 100644 --- a/src/components/PlexLoginButton/index.tsx +++ b/src/components/PlexLoginButton/index.tsx @@ -15,12 +15,14 @@ interface PlexLoginButtonProps { onAuthToken: (authToken: string) => void; isProcessing?: boolean; onError?: (message: string) => void; + textOverride?: string; } const PlexLoginButton = ({ onAuthToken, onError, isProcessing, + textOverride, }: PlexLoginButtonProps) => { const intl = useIntl(); const [loading, setLoading] = useState(false); @@ -55,6 +57,8 @@ const PlexLoginButton = ({ ? intl.formatMessage(globalMessages.loading) : isProcessing ? intl.formatMessage(messages.signingin) + : textOverride + ? textOverride : intl.formatMessage(messages.signinwithplex)} diff --git a/src/components/Settings/SettingsPlex.tsx b/src/components/Settings/SettingsPlex.tsx index 27564aca..26fcf42e 100644 --- a/src/components/Settings/SettingsPlex.tsx +++ b/src/components/Settings/SettingsPlex.tsx @@ -6,6 +6,8 @@ import PageTitle from '@app/components/Common/PageTitle'; import SensitiveInput from '@app/components/Common/SensitiveInput'; import LibraryItem from '@app/components/Settings/LibraryItem'; import SettingsBadge from '@app/components/Settings/SettingsBadge'; +import LoginWithPlex from '@app/components/Setup/LoginWithPlex'; +import { useUser } from '@app/hooks/useUser'; import globalMessages from '@app/i18n/globalMessages'; import { SaveIcon } from '@heroicons/react/outline'; import { RefreshIcon, SearchIcon, XIcon } from '@heroicons/react/solid'; @@ -106,6 +108,7 @@ interface SettingsPlexProps { } const SettingsPlex = ({ onComplete }: SettingsPlexProps) => { + const { user } = useUser(); const [isSyncing, setIsSyncing] = useState(false); const [isRefreshingPresets, setIsRefreshingPresets] = useState(false); const [availableServers, setAvailableServers] = useState( @@ -115,7 +118,11 @@ const SettingsPlex = ({ onComplete }: SettingsPlexProps) => { data, error, mutate: revalidate, - } = useSWR('/api/v1/settings/plex'); + } = useSWR< + PlexSettings & { + plexAvailable: boolean; + } + >('/api/v1/settings/plex'); const { data: dataTautulli, mutate: revalidateTautulli } = useSWR('/api/v1/settings/tautulli'); const { data: dataSync, mutate: revalidateSync } = useSWR( @@ -325,7 +332,8 @@ const SettingsPlex = ({ onComplete }: SettingsPlexProps) => { if ((!data || !dataTautulli) && !error) { return ; } - return ( + + const TitleContent = () => ( <> {

{intl.formatMessage(messages.plexsettingsDescription)}

- {!!onComplete && ( + {!!onComplete && data?.plexAvailable && (
{
)} + + ); + + if (!data?.plexAvailable && user?.id !== 1) { + return ( + <> + + + The owner account must first link their Plex server to be able to + access these settings. + + + ); + } + + if (!data?.plexAvailable) { + return ( + <> + + + You must connect your Plex account to continue configuring a Plex + Media Server. + +
+ revalidate()} /> +
+ + ); + } + + return ( + <> + void; +}; + +type ConfigureStatus = { + configuring?: 'plex'; +}; + +const ConfigureMediaServers = ({ onComplete }: ConfigureMediaServersProps) => { + const intl = useIntl(); + const [configureStatus, setConfigureStatus] = useState(); + const [plexConfigured, setPlexConfigured] = useState(false); + return ( + <> + {configureStatus?.configuring === 'plex' && ( + <> + setPlexConfigured(true)} /> +
+ + {intl.formatMessage(messages.tip)} + + {intl.formatMessage(messages.scanbackground)} +
+
+
+ + {!plexConfigured && ( + + )} +
+
+ + )} + {!configureStatus?.configuring && ( + <> +

Configure Media Servers

+

+ Select the media servers you would like to configure below. +

+
+
+
+ +
+
+ {plexConfigured ? ( + <> + + Configured + + ) : ( + + )} +
+
+
+
+
+ +
+
+ + )} + + ); +}; + +export default ConfigureMediaServers; diff --git a/src/components/Setup/CreateAccount/index.tsx b/src/components/Setup/CreateAccount/index.tsx new file mode 100644 index 00000000..21937ca9 --- /dev/null +++ b/src/components/Setup/CreateAccount/index.tsx @@ -0,0 +1,155 @@ +import Button from '@app/components/Common/Button'; +import SensitiveInput from '@app/components/Common/SensitiveInput'; +import LoginWithPlex from '@app/components/Setup/LoginWithPlex'; +import { useUser } from '@app/hooks/useUser'; +import { ArrowLeftIcon, UserIcon } from '@heroicons/react/solid'; +import axios from 'axios'; +import { Field, Form, Formik } from 'formik'; +import { useState } from 'react'; +import { defineMessages, useIntl } from 'react-intl'; +import * as Yup from 'yup'; + +const messages = defineMessages({ + welcometooverseerr: 'Welcome to Overseerr!', + getstarted: + "Let's get started! To begin, we will need to create your administrator account. You can either do this by logging in with your Plex account, or creating a local user.", + validationEmail: 'You must provide a valid email address', + validationpasswordminchars: + 'Password is too short; should be a minimum of 8 characters', +}); + +type StepOneProps = { + onComplete: () => void; +}; + +const StepOne = ({ onComplete }: StepOneProps) => { + const { revalidate } = useUser(); + const [showLocalCreateForm, setShowLocalCreateForm] = useState(false); + const intl = useIntl(); + + const CreateUserSchema = Yup.object().shape({ + email: Yup.string() + .required(intl.formatMessage(messages.validationEmail)) + .email(intl.formatMessage(messages.validationEmail)), + password: Yup.lazy((value) => + !value + ? Yup.string() + : Yup.string().min( + 8, + intl.formatMessage(messages.validationpasswordminchars) + ) + ), + }); + + return ( + <> +

+ {intl.formatMessage(messages.welcometooverseerr)} +

+

{intl.formatMessage(messages.getstarted)}

+
+ {showLocalCreateForm ? ( + { + try { + await axios.post('/api/v1/auth/local', { + email: values.email, + password: values.password, + }); + revalidate(); + onComplete(); + } catch (e) { + console.log(e.message); + } + }} + validationSchema={CreateUserSchema} + > + {({ isSubmitting, errors, touched, isValid }) => ( +
+
+ +
+
+ +
+ {errors.email && + touched.email && + typeof errors.email === 'string' && ( +
{errors.email}
+ )} +
+ +
+
+ +
+ {errors.password && + touched.password && + typeof errors.password === 'string' && ( +
{errors.password}
+ )} +
+
+
+
+ + +
+
+
+ )} +
+ ) : ( +
+ + +
+ )} +
+ + ); +}; + +export default StepOne; diff --git a/src/components/Setup/LoginWithPlex.tsx b/src/components/Setup/LoginWithPlex.tsx index 62778aed..34cadc03 100644 --- a/src/components/Setup/LoginWithPlex.tsx +++ b/src/components/Setup/LoginWithPlex.tsx @@ -5,8 +5,7 @@ import { useEffect, useState } from 'react'; import { defineMessages, useIntl } from 'react-intl'; const messages = defineMessages({ - welcome: 'Welcome to Overseerr', - signinMessage: 'Get started by signing in with your Plex account', + signinwithplex: 'Sign In with Plex', }); interface LoginWithPlexProps { @@ -45,14 +44,11 @@ const LoginWithPlex = ({ onComplete }: LoginWithPlexProps) => { return (
-
- {intl.formatMessage(messages.welcome)} -
-
- {intl.formatMessage(messages.signinMessage)} -
- setAuthToken(authToken)} /> + setAuthToken(authToken)} + textOverride={intl.formatMessage(messages.signinwithplex)} + />
); diff --git a/src/components/Setup/index.tsx b/src/components/Setup/index.tsx index 3926da90..f1fb50f8 100644 --- a/src/components/Setup/index.tsx +++ b/src/components/Setup/index.tsx @@ -1,12 +1,11 @@ import AppDataWarning from '@app/components/AppDataWarning'; -import Badge from '@app/components/Common/Badge'; import Button from '@app/components/Common/Button'; import ImageFader from '@app/components/Common/ImageFader'; import PageTitle from '@app/components/Common/PageTitle'; import LanguagePicker from '@app/components/Layout/LanguagePicker'; -import SettingsPlex from '@app/components/Settings/SettingsPlex'; import SettingsServices from '@app/components/Settings/SettingsServices'; -import LoginWithPlex from '@app/components/Setup/LoginWithPlex'; +import ConfigureMediaServers from '@app/components/Setup/ConfigureMediaServers'; +import CreateAccount from '@app/components/Setup/CreateAccount'; import SetupSteps from '@app/components/Setup/SetupSteps'; import useLocale from '@app/hooks/useLocale'; import axios from 'axios'; @@ -20,8 +19,8 @@ const messages = defineMessages({ finish: 'Finish Setup', finishing: 'Finishing…', continue: 'Continue', - loginwithplex: 'Sign in with Plex', - configureplex: 'Configure Plex', + loginwithplex: 'Create Admin Account', + configuremediaserver: 'Configure Media Server', configureservices: 'Configure Services', tip: 'Tip', scanbackground: @@ -32,7 +31,6 @@ const Setup = () => { const intl = useIntl(); const [isUpdating, setIsUpdating] = useState(false); const [currentStep, setCurrentStep] = useState(1); - const [plexSettingsComplete, setPlexSettingsComplete] = useState(false); const router = useRouter(); const { locale } = useLocale(); @@ -70,7 +68,7 @@ const Setup = () => {
-
+
{ /> 2} /> @@ -104,31 +102,10 @@ const Setup = () => {
{currentStep === 1 && ( - setCurrentStep(2)} /> + setCurrentStep(2)} /> )} {currentStep === 2 && ( -
- setPlexSettingsComplete(true)} /> -
- - {intl.formatMessage(messages.tip)} - - {intl.formatMessage(messages.scanbackground)} -
-
-
- - - -
-
-
+ setCurrentStep(3)} /> )} {currentStep === 3 && (
diff --git a/src/components/UserList/index.tsx b/src/components/UserList/index.tsx index dfc0c2c2..c05df596 100644 --- a/src/components/UserList/index.tsx +++ b/src/components/UserList/index.tsx @@ -12,7 +12,7 @@ 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 { Permission, useUser } from '@app/hooks/useUser'; import globalMessages from '@app/i18n/globalMessages'; import { Transition } from '@headlessui/react'; import { @@ -621,7 +621,7 @@ const UserList = () => { )} - {user.userType === UserType.PLEX ? ( + {user.isPlexUser ? ( {intl.formatMessage(messages.plexuser)} diff --git a/src/components/UserProfile/UserSettings/UserGeneralSettings/index.tsx b/src/components/UserProfile/UserSettings/UserGeneralSettings/index.tsx index fe768b28..06c10d20 100644 --- a/src/components/UserProfile/UserSettings/UserGeneralSettings/index.tsx +++ b/src/components/UserProfile/UserSettings/UserGeneralSettings/index.tsx @@ -9,7 +9,7 @@ import type { AvailableLocale } from '@app/context/LanguageContext'; import { availableLanguages } from '@app/context/LanguageContext'; import useLocale from '@app/hooks/useLocale'; import useSettings from '@app/hooks/useSettings'; -import { Permission, UserType, useUser } from '@app/hooks/useUser'; +import { Permission, useUser } from '@app/hooks/useUser'; import globalMessages from '@app/i18n/globalMessages'; import Error from '@app/pages/_error'; import { SaveIcon } from '@heroicons/react/outline'; @@ -190,7 +190,7 @@ const UserGeneralSettings = () => {
- {user?.userType === UserType.PLEX ? ( + {user?.isPlexUser ? ( {intl.formatMessage(messages.plexuser)} @@ -423,7 +423,7 @@ const UserGeneralSettings = () => { [Permission.AUTO_REQUEST, Permission.AUTO_REQUEST_MOVIE], { type: 'or' } ) && - user?.userType === UserType.PLEX && ( + user?.isPlexUser && (