Skip to content

Commit

Permalink
Moved captcha to the last signup step (#4911)
Browse files Browse the repository at this point in the history
* moved captcha to the last step

* removed unused var

---------

Co-authored-by: Artem <Artem Slugin>
  • Loading branch information
attemka authored Sep 22, 2023
1 parent 0c41bc2 commit 6efbcf4
Show file tree
Hide file tree
Showing 3 changed files with 47 additions and 44 deletions.
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import HCaptcha from '@hcaptcha/react-hcaptcha'
import debouncePromise from 'awesome-debounce-promise'
import { FC, RefObject, useCallback, useEffect, useMemo, useRef, useState } from 'react'
import { Controller, useForm } from 'react-hook-form'
Expand All @@ -9,7 +8,6 @@ import { TextButton } from '@/components/_buttons/Button'
import { FormField } from '@/components/_inputs/FormField'
import { Input } from '@/components/_inputs/Input'
import { ImageCropModal, ImageCropModalImperativeHandle } from '@/components/_overlays/ImageCropModal'
import { atlasConfig } from '@/config'
import { MEMBERSHIP_NAME_PATTERN } from '@/config/regex'
import { MemberFormData } from '@/hooks/useCreateMember'
import { useUniqueMemberHandle } from '@/hooks/useUniqueMemberHandle'
Expand All @@ -28,7 +26,6 @@ export const SignUpMembershipStep: FC<SignInModalMembershipStepProps> = ({
setPrimaryButtonProps,
onSubmit,
hasNavigatedBack,
dialogContentRef,
avatar,
handle,
}) => {
Expand All @@ -53,12 +50,9 @@ export const SignUpMembershipStep: FC<SignInModalMembershipStepProps> = ({
shallow
)
const handleInputRef = useRef<HTMLInputElement | null>(null)
const captchaInputRef = useRef<HTMLDivElement | null>(null)
const avatarDialogRef = useRef<ImageCropModalImperativeHandle>(null)

const [isHandleValidating, setIsHandleValidating] = useState(false)
// used to scroll the form to the bottom upon first handle field focus - this is done to let the user see Captcha form field
const hasDoneInitialScroll = useRef(false)

const { checkIfMemberIsAvailable } = useUniqueMemberHandle()

Expand Down Expand Up @@ -109,11 +103,7 @@ export const SignUpMembershipStep: FC<SignInModalMembershipStepProps> = ({
if (errors.handle) {
handleInputRef.current?.scrollIntoView({ behavior: 'smooth' })
}

if (errors.captchaToken) {
captchaInputRef.current?.scrollIntoView({ behavior: 'smooth' })
}
}, [errors.captchaToken, errors.handle])
}, [errors.handle])

return (
<AuthenticationModalStepTemplate
Expand Down Expand Up @@ -179,39 +169,8 @@ export const SignUpMembershipStep: FC<SignInModalMembershipStepProps> = ({
error={!!errors.handle}
processing={isHandleValidating || isSubmitting}
autoComplete="off"
onClick={() => {
if (hasDoneInitialScroll.current || !dialogContentRef?.current) return
hasDoneInitialScroll.current = true
dialogContentRef.current.scrollTo({ top: dialogContentRef.current.scrollHeight, behavior: 'smooth' })
}}
/>
</FormField>
{atlasConfig.features.members.hcaptchaSiteKey && (
<Controller
control={control}
name="captchaToken"
render={({ field: { onChange }, fieldState: { error } }) => (
<FormField error={error?.message} ref={captchaInputRef}>
<HCaptcha
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
sitekey={atlasConfig.features.members.hcaptchaSiteKey!}
theme="dark"
languageOverride="en"
onVerify={(token) => {
onChange(token)
trigger('captchaToken')
}}
/>
</FormField>
)}
rules={{
required: {
value: !!atlasConfig.features.members.hcaptchaSiteKey,
message: "Verify that you're not a robot.",
},
}}
/>
)}
</StyledForm>
}
/>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
import HCaptcha from '@hcaptcha/react-hcaptcha'
import { zodResolver } from '@hookform/resolvers/zod'
import { FC, RefObject, useCallback, useEffect, useRef } from 'react'
import { FormProvider, useForm } from 'react-hook-form'
import { Controller, FormProvider, useForm } from 'react-hook-form'

import { AuthenticationModalStepTemplate } from '@/components/_auth/AuthenticationModalStepTemplate'
import { PasswordCriterias } from '@/components/_auth/PasswordCriterias'
import { FormField } from '@/components/_inputs/FormField'
import { Input } from '@/components/_inputs/Input'
import { atlasConfig } from '@/config'
import { AccountFormData } from '@/hooks/useCreateMember'
import { useHidePasswordInInput } from '@/hooks/useHidePasswordInInput'
import { passwordAndRepeatPasswordSchema } from '@/utils/formValidationOptions'
Expand All @@ -16,6 +18,7 @@ import { SignUpStepsCommonProps } from '../SignUpSteps.types'
type PasswordStepForm = {
password: string
confirmPassword: string
captchaToken?: string
}

type SignUpPasswordStepProps = {
Expand All @@ -34,6 +37,7 @@ export const SignUpPasswordStep: FC<SignUpPasswordStepProps> = ({
}) => {
const form = useForm<PasswordStepForm>({
shouldFocusError: true,
reValidateMode: 'onSubmit',
defaultValues: {
password,
confirmPassword: password,
Expand All @@ -44,11 +48,17 @@ export const SignUpPasswordStep: FC<SignUpPasswordStepProps> = ({
handleSubmit,
register,
formState: { errors },
control,
trigger,
} = form
const [hidePasswordProps] = useHidePasswordInInput()
const [hideConfirmPasswordProps] = useHidePasswordInInput()

const captchaRef = useRef<HCaptcha | null>(null)
const captchaInputRef = useRef<HTMLDivElement | null>(null)

const handleGoToNextStep = useCallback(() => {
captchaRef.current?.resetCaptcha()
handleSubmit((data) => {
onPasswordSubmit(data.password)
})()
Expand All @@ -61,7 +71,13 @@ export const SignUpPasswordStep: FC<SignUpPasswordStepProps> = ({
})
}, [handleGoToNextStep, setPrimaryButtonProps])

// used to scroll the form to the bottom upon first handle field focus - this is done to let the user see password requirements
useEffect(() => {
if (errors.captchaToken) {
captchaInputRef.current?.scrollIntoView({ behavior: 'smooth' })
}
}, [errors.captchaToken])

// used to scroll the form to the bottom upon first handle field focus - this is done to let the user see password requirements & captcha
const hasDoneInitialScroll = useRef(false)

return (
Expand Down Expand Up @@ -96,6 +112,27 @@ export const SignUpPasswordStep: FC<SignUpPasswordStepProps> = ({
/>
</FormField>
<PasswordCriterias />
{atlasConfig.features.members.hcaptchaSiteKey && (
<Controller
control={control}
name="captchaToken"
render={({ field: { onChange }, fieldState: { error } }) => (
<FormField error={error?.message} ref={captchaInputRef}>
<HCaptcha
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
sitekey={atlasConfig.features.members.hcaptchaSiteKey!}
theme="dark"
languageOverride="en"
ref={captchaRef}
onVerify={(token) => {
onChange(token)
trigger('captchaToken')
}}
/>
</FormField>
)}
/>
)}
</StyledSignUpForm>
</AuthenticationModalStepTemplate>
</FormProvider>
Expand Down
7 changes: 7 additions & 0 deletions packages/atlas/src/utils/formValidationOptions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ import { isValid } from 'date-fns'
import { RegisterOptions, Validate } from 'react-hook-form'
import { z } from 'zod'

import { atlasConfig } from '@/config'

type TextValidationArgs = {
name: string
maxLength: number
Expand Down Expand Up @@ -67,6 +69,7 @@ export const passwordAndRepeatPasswordSchema = z
.min(9, { message: 'Password has to meet requirements.' })
.max(64, { message: 'Password has to meet requirements.' }),
confirmPassword: z.string(),
captchaToken: z.string().optional(),
})
.refine(
(data) => {
Expand All @@ -85,3 +88,7 @@ export const passwordAndRepeatPasswordSchema = z
message: 'Password has to meet requirements.',
}
)
.refine((data) => (atlasConfig.features.members.hcaptchaSiteKey ? !!data.captchaToken : true), {
path: ['captchaToken'],
message: "Verify that you're not a robot.",
})

0 comments on commit 6efbcf4

Please sign in to comment.