diff --git a/package.json b/package.json index 0e4b887a..83482454 100644 --- a/package.json +++ b/package.json @@ -41,6 +41,7 @@ "pug": "^3.0.0", "react": "17.0.1", "react-ace": "^9.2.1", + "react-animate-height": "^2.0.23", "react-dom": "17.0.1", "react-intersection-observer": "^8.31.0", "react-intl": "^5.10.16", diff --git a/src/components/Common/Accordion/index.tsx b/src/components/Common/Accordion/index.tsx new file mode 100644 index 00000000..67e883fe --- /dev/null +++ b/src/components/Common/Accordion/index.tsx @@ -0,0 +1,67 @@ +import * as React from 'react'; +import { useState } from 'react'; +import AnimateHeight from 'react-animate-height'; + +export interface AccordionProps { + children: (args: AccordionChildProps) => React.ReactElement | null; + /** If true, only one accordion item can be open at any time */ + single?: boolean; + /** If true, at least one accordion item will always be open */ + atLeastOne?: boolean; + initialOpenIndexes?: number[]; +} +export interface AccordionChildProps { + openIndexes: number[]; + handleClick(index: number): void; + AccordionContent: any; +} + +export const AccordionContent: React.FC<{ isOpen: boolean }> = ({ + isOpen, + children, +}) => { + return {children}; +}; + +const Accordion: React.FC = ({ + single, + atLeastOne, + initialOpenIndexes, + children, +}) => { + const initialState = initialOpenIndexes || (atLeastOne && [0]) || []; + const [openIndexes, setOpenIndexes] = useState(initialState); + + const close = (index: number) => { + const openCount = openIndexes.length; + const newListOfIndexes = + atLeastOne && openCount === 1 && openIndexes.includes(index) + ? openIndexes + : openIndexes.filter((i) => i !== index); + + setOpenIndexes(newListOfIndexes); + }; + + const open = (index: number) => { + const newListOfIndexes = single ? [index] : [...openIndexes, index]; + setOpenIndexes(newListOfIndexes); + }; + + const handleItemClick = (index: number) => { + const action = openIndexes.includes(index) ? 'closing' : 'opening'; + + if (action === 'closing') { + close(index); + } else { + open(index); + } + }; + + return children({ + openIndexes: openIndexes, + handleClick: handleItemClick, + AccordionContent, + }); +}; + +export default Accordion; diff --git a/src/components/Login/LocalLogin.tsx b/src/components/Login/LocalLogin.tsx index 5e807d1e..f5067ae8 100644 --- a/src/components/Login/LocalLogin.tsx +++ b/src/components/Login/LocalLogin.tsx @@ -11,17 +11,15 @@ const messages = defineMessages({ validationemailrequired: 'Not a valid email address', validationpasswordrequired: 'Password required', loginerror: 'Something went wrong when trying to sign in', - loggingin: 'Logging in...', - login: 'Login', - goback: 'Go back', + signingin: 'Signing in…', + signin: 'Sign in', }); interface LocalLoginProps { - goBack: () => void; revalidate: () => void; } -const LocalLogin: React.FC = ({ goBack, revalidate }) => { +const LocalLogin: React.FC = ({ revalidate }) => { const intl = useIntl(); const [loginError, setLoginError] = useState(null); @@ -107,18 +105,6 @@ const LocalLogin: React.FC = ({ goBack, revalidate }) => {
- - -
diff --git a/src/components/Login/index.tsx b/src/components/Login/index.tsx index 8334d66e..1b1cee18 100644 --- a/src/components/Login/index.tsx +++ b/src/components/Login/index.tsx @@ -7,11 +7,12 @@ import ImageFader from '../Common/ImageFader'; import { defineMessages, FormattedMessage, useIntl } from 'react-intl'; import Transition from '../Transition'; import LanguagePicker from '../Layout/LanguagePicker'; -import Button from '../Common/Button'; import LocalLogin from './LocalLogin'; +import Accordion from '../Common/Accordion'; const messages = defineMessages({ - signinplex: 'Sign in to continue', + signinheader: 'Sign in to continue', + signinwithplex: 'Sign in with Plex', signinwithoverseerr: 'Sign in with Overseerr', }); @@ -19,7 +20,6 @@ const Login: React.FC = () => { const intl = useIntl(); const [error, setError] = useState(''); const [isProcessing, setProcessing] = useState(false); - const [localLogin, setLocalLogin] = useState(false); const [authToken, setAuthToken] = useState(undefined); const { user, revalidate } = useUser(); const router = useRouter(); @@ -56,7 +56,7 @@ const Login: React.FC = () => { }, [user, router]); return ( -
+
{ alt="Overseerr Logo" />

- +

- {!localLogin ? ( - <> - -
-
-
- -
-
-

- {error} -

-
+ <> + +
+
+
+ +
+
+

+ {error} +

- -
- setAuthToken(authToken)} - />
- - - - - ) : ( - setLocalLogin(false)} - revalidate={revalidate} - /> - )} + + + {({ openIndexes, handleClick, AccordionContent }) => ( + <> + + +
+ setAuthToken(authToken)} + /> +
+
+ + +
+ +
+
+ + )} +
+
diff --git a/src/components/PlexLoginButton/index.tsx b/src/components/PlexLoginButton/index.tsx index 3c58e233..df0de2be 100644 --- a/src/components/PlexLoginButton/index.tsx +++ b/src/components/PlexLoginButton/index.tsx @@ -3,9 +3,9 @@ import PlexOAuth from '../../utils/plex'; import { defineMessages, useIntl } from 'react-intl'; const messages = defineMessages({ - loginwithplex: 'Login with Plex', - loading: 'Loading...', - loggingin: 'Logging in...', + signinwithplex: 'Sign in', + loading: 'Loading…', + signingin: 'Signing in…', }); const plexOAuth = new PlexOAuth(); @@ -51,8 +51,8 @@ const PlexLoginButton: React.FC = ({ {loading ? intl.formatMessage(messages.loading) : isProcessing - ? intl.formatMessage(messages.loggingin) - : intl.formatMessage(messages.loginwithplex)} + ? intl.formatMessage(messages.signingin) + : intl.formatMessage(messages.signinwithplex)} ); diff --git a/src/i18n/locale/en.json b/src/i18n/locale/en.json index 493d3c22..72456a09 100644 --- a/src/i18n/locale/en.json +++ b/src/i18n/locale/en.json @@ -27,13 +27,13 @@ "components.Layout.UserDropdown.signout": "Sign Out", "components.Layout.alphawarning": "This is ALPHA software. Almost everything is bound to be nearly broken and/or unstable. Please report issues to the Overseerr GitHub!", "components.Login.email": "Email Address", - "components.Login.goback": "Go back", - "components.Login.loggingin": "Logging in…", - "components.Login.login": "Login", "components.Login.loginerror": "Something went wrong when trying to sign in", "components.Login.password": "Password", - "components.Login.signinplex": "Sign in to continue", + "components.Login.signin": "Sign in", + "components.Login.signingin": "Signing in…", + "components.Login.signinheader": "Sign in to continue", "components.Login.signinwithoverseerr": "Sign in with Overseerr", + "components.Login.signinwithplex": "Sign in with Plex", "components.Login.validationemailrequired": "Not a valid email address", "components.Login.validationpasswordrequired": "Password required", "components.MediaSlider.ShowMoreCard.seemore": "See More", @@ -83,8 +83,8 @@ "components.PersonDetails.crewmember": "Crew Member", "components.PersonDetails.nobiography": "No biography available.", "components.PlexLoginButton.loading": "Loading…", - "components.PlexLoginButton.loggingin": "Logging in…", - "components.PlexLoginButton.loginwithplex": "Login with Plex", + "components.PlexLoginButton.loggingin": "Signing in…", + "components.PlexLoginButton.loginwithplex": "Sign in", "components.RequestBlock.profilechanged": "Profile Changed", "components.RequestBlock.requestoverrides": "Request Overrides", "components.RequestBlock.rootfolder": "Root Folder", diff --git a/yarn.lock b/yarn.lock index f589f6f4..bd516764 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3869,7 +3869,7 @@ class-utils@^0.3.5: isobject "^3.0.0" static-extend "^0.1.1" -classnames@2.2.6: +classnames@2.2.6, classnames@^2.2.5: version "2.2.6" resolved "https://registry.yarnpkg.com/classnames/-/classnames-2.2.6.tgz#43935bffdd291f326dad0a205309b38d00f650ce" integrity sha512-JR/iSQOSt+LQIWwrwEzJ9uk0xfN3mTVYMwt1Ir5mUcSN6pU+V4zQFFaJsclJbPuAUQH+yfWef6tm7l1quW3C8Q== @@ -11177,7 +11177,7 @@ promzard@^0.3.0: dependencies: read "1" -prop-types@15.7.2, prop-types@^15.5.8, prop-types@^15.6.0, prop-types@^15.6.2, prop-types@^15.7.2: +prop-types@15.7.2, prop-types@^15.5.8, prop-types@^15.6.0, prop-types@^15.6.1, prop-types@^15.6.2, prop-types@^15.7.2: version "15.7.2" resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.7.2.tgz#52c41e75b8c87e72b9d9360e0206b99dcbffa6c5" integrity sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ== @@ -11528,6 +11528,14 @@ react-ace@^9.2.1: lodash.isequal "^4.5.0" prop-types "^15.7.2" +react-animate-height@^2.0.23: + version "2.0.23" + resolved "https://registry.yarnpkg.com/react-animate-height/-/react-animate-height-2.0.23.tgz#2e14ac707b20ae67b87766ccfd581e693e0e7ec7" + integrity sha512-DucSC/1QuxWEFzR9IsHMzrf2nrcZ6qAmLIFoENa2kLK7h72XybcMA9o073z7aHccFzdMEW0/fhAdnQG7a4rDow== + dependencies: + classnames "^2.2.5" + prop-types "^15.6.1" + react-dom@17.0.1: version "17.0.1" resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-17.0.1.tgz#1de2560474ec9f0e334285662ede52dbc5426fc6"