diff --git a/src/features/AuthServiceApp/components/App/index.tsx b/src/features/AuthServiceApp/components/App/index.tsx index 11d8549b..422a130e 100644 --- a/src/features/AuthServiceApp/components/App/index.tsx +++ b/src/features/AuthServiceApp/components/App/index.tsx @@ -6,37 +6,18 @@ import { Switch, } from 'react-router-dom' -import styled, { css } from 'styled-components/macro' - -import { isMobileDevice } from 'config/userAgent' - import { useLexicsConfig } from 'features/LexicsStore' import { PAGES } from '../../config/pages' import { lexics } from '../../config/lexics' +import { Main } from './styled' + const Login = lazy(() => import('../Login')) const Registration = lazy(() => import('../Registration')) const ChangePassword = lazy(() => import('../ChangePassword')) const Oauth = lazy(() => import('../Oauth')) -const Main = styled.main` - width: 100%; - height: 100vh; - overflow-y: auto; - - ::-webkit-scrollbar { - display: none; - } - - ${isMobileDevice - ? css` - padding: 0 12px; - max-height: 100vh; - ` - : ''}; -` - export const App = () => { useLexicsConfig(lexics) diff --git a/src/features/AuthServiceApp/components/App/styled.tsx b/src/features/AuthServiceApp/components/App/styled.tsx new file mode 100644 index 00000000..d27460c8 --- /dev/null +++ b/src/features/AuthServiceApp/components/App/styled.tsx @@ -0,0 +1,20 @@ +import styled, { css } from 'styled-components/macro' + +import { isMobileDevice } from 'config/userAgent' + +export const Main = styled.main` + width: 100%; + height: 100vh; + overflow-y: auto; + + ::-webkit-scrollbar { + display: none; + } + + ${isMobileDevice + ? css` + padding: 0 12px; + max-height: 100vh; + ` + : ''}; +` diff --git a/src/features/AuthServiceApp/components/Login/hooks.tsx b/src/features/AuthServiceApp/components/Login/hooks.tsx index 5488400f..17f7da25 100644 --- a/src/features/AuthServiceApp/components/Login/hooks.tsx +++ b/src/features/AuthServiceApp/components/Login/hooks.tsx @@ -1,4 +1,5 @@ import type { FormEvent, MouseEvent } from 'react' + import { useRef, useState, @@ -13,6 +14,7 @@ import { useLocalStore } from 'hooks' import type { Settings } from 'features/AuthStore/helpers' import { loginCheck } from 'features/AuthServiceApp/requests/auth' +import { resendConfirmation } from 'features/AuthServiceApp/requests/resendConfirmation' import { getApiUrl } from 'features/AuthServiceApp/config/routes' import { useAuthFields } from 'features/AuthServiceApp/hooks/useAuthFields' @@ -36,8 +38,10 @@ export const useLoginForm = () => { } = urlParams const [authError, setAuthError] = useState('') + const [resendConfirm, setResendConfirm] = useState(false) const [isFetching, setIsFetching] = useState(false) const [isRecoveryPopupOpen, setIsRecoveryPopupOpen] = useState(false) + const [isModalOpen, setIsModalOpen] = useState(false) const formRef = useRef(null) const { @@ -73,6 +77,10 @@ export const useLoginForm = () => { const handleError = (error: string) => { setAuthError(error) setIsFetching(false) + + if (error === 'error_user_not_confirm') { + setResendConfirm(true) + } } const handleSubmit = async (e: FormEvent) => { @@ -97,6 +105,26 @@ export const useLoginForm = () => { } } + const handleResendConfirm = async () => { + setIsFetching(true) + try { + await resendConfirmation({ + email, + urlParams, + }) + setAuthError('') + setIsFetching(false) + setIsModalOpen(true) + } catch (err) { + handleError(String(err)) + setIsFetching(false) + } + } + + const handleModalClose = () => { + setIsModalOpen(false) + } + const handleAuthButtonClick = (authProvider: AuthProviders) => ( e: MouseEvent, ) => { @@ -119,9 +147,12 @@ export const useLoginForm = () => { formError, formRef, handleAuthButtonClick, + handleModalClose, handleModalOpen, + handleResendConfirm, handleSubmit, isFetching, + isModalOpen, isRecoveryPopupOpen, isSubmitDisabled, lang, @@ -131,6 +162,7 @@ export const useLoginForm = () => { onPasswordChange, password, redirect_uri, + resendConfirm, response_mode, response_type, scope, diff --git a/src/features/AuthServiceApp/components/Login/index.tsx b/src/features/AuthServiceApp/components/Login/index.tsx index 7c3645f0..a5ce7f04 100644 --- a/src/features/AuthServiceApp/components/Login/index.tsx +++ b/src/features/AuthServiceApp/components/Login/index.tsx @@ -1,5 +1,6 @@ import { T9n } from 'features/T9n' import { RecoveryPopup } from 'features/AuthServiceApp/components/RecoveryPopup' +import { RegisterPopup } from 'features/AuthServiceApp/components/RegisterPopup' import { client } from 'features/AuthServiceApp/config/clients' import { PAGES } from '../../config/pages' @@ -17,6 +18,8 @@ import { AuthButtonText, AuthButtonImage, ContinueWith, + ButtonsContainer, + ResendEmailButton, } from './styled' import { @@ -45,9 +48,12 @@ const Login = () => { formError, formRef, handleAuthButtonClick, + handleModalClose, handleModalOpen, + handleResendConfirm, handleSubmit, isFetching, + isModalOpen, isRecoveryPopupOpen, isSubmitDisabled, lang, @@ -57,6 +63,7 @@ const Login = () => { onPasswordChange, password, redirect_uri, + resendConfirm, response_mode, response_type, scope, @@ -120,13 +127,22 @@ const Login = () => { ) : () } - - - - + + + + + {resendConfirm && ( + + + + )} + @@ -152,7 +168,15 @@ const Login = () => { - + + { const history = useHistory() const urlParams = useParamsUrl() + const { search } = useLocation() const [authError, setAuthError] = useState('') const [termsAccepted, setTermsAccepted] = useState(true) const [cookiesAccepted, setCookiesAccepted] = useState(true) const [isFetching, setIsFetching] = useState(false) const [isModalOpen, setIsModalOpen] = useState(false) + const { email, error: formError, @@ -56,7 +58,7 @@ export const useRegistrationForm = () => { const handleModalClose = () => { setIsModalOpen(false) - history.replace(PAGES.login) + history.replace(`${PAGES.login}${search}`) } const onTermsChange = () => { diff --git a/src/features/AuthServiceApp/components/Registration/index.tsx b/src/features/AuthServiceApp/components/Registration/index.tsx index 505841cf..ed3a0b53 100644 --- a/src/features/AuthServiceApp/components/Registration/index.tsx +++ b/src/features/AuthServiceApp/components/Registration/index.tsx @@ -27,6 +27,7 @@ import { Wrapper, ScArrowLoader, } from '../../styled' + import { Label, Link, @@ -71,7 +72,6 @@ const Registration = () => {
- { - + { isFetching ? diff --git a/src/features/AuthServiceApp/config/lexics.tsx b/src/features/AuthServiceApp/config/lexics.tsx index ce82d7b6..ded5e23f 100644 --- a/src/features/AuthServiceApp/config/lexics.tsx +++ b/src/features/AuthServiceApp/config/lexics.tsx @@ -11,6 +11,7 @@ export const lexics = { error_accept_cookies: 17268, error_email_already_exist: 18253, error_email_already_in_use: 11156, + error_email_field_required: 20102, error_empty_email: 2498, error_empty_password: 2499, error_failed_to_send_email: 15902, @@ -18,10 +19,12 @@ export const lexics = { error_invalid_email_or_password: 15774, error_invalid_platform: 15925, error_missing_required_argument: 15921, + error_no_more_than_one_email_per_minute: 20093, error_passwords_missmatch: 15841, error_simple_password: 12940, error_unsupported_response_type: 15922, error_user_already_created: 15926, + error_user_is_already_verified: 20092, error_user_not_confirm: 16069, error_user_not_found: 15956, error_user_not_found_recovery: 1417, diff --git a/src/features/AuthServiceApp/requests/resendConfirmation.tsx b/src/features/AuthServiceApp/requests/resendConfirmation.tsx new file mode 100644 index 00000000..0c0f3aae --- /dev/null +++ b/src/features/AuthServiceApp/requests/resendConfirmation.tsx @@ -0,0 +1,57 @@ +import type { ClientIds } from 'config/clients/types' + +import { getApiUrl } from 'features/AuthServiceApp/config/routes' + +const errorLexics = { + 8: 'error_failed_to_send_email', + 15: 'error_email_field_required', + 18: 'error_user_is_already_verified', + 19: 'error_no_more_than_one_email_per_minute', +} + +type FailedResponse = { + error: { + code: keyof typeof errorLexics, + message?: string, + }, + ok: false, +} + +type SuccessResponse = { + ok: true, +} + +export type UrlParams = { + client_id: ClientIds, + lang?: string, + nonce?: string, + redirect_uri: string, + response_mode: string, + response_type: string, + scope?: string, + state?: string, +} + +type TResendConfirmation = { + email: string, + urlParams: UrlParams, +} + +export const resendConfirmation = async ({ + email, + urlParams, +} : TResendConfirmation) => { + const url = getApiUrl('/repeat_confirm_email') + const init: RequestInit = { + body: new URLSearchParams({ + email, + ...urlParams, + }), + method: 'POST', + } + const response = await fetch(url, init) + const body: SuccessResponse | FailedResponse = await response.json() + if (body.ok) return Promise.resolve() + + return Promise.reject(errorLexics[body.error.code]) +}