144 lines
4.1 KiB
TypeScript
144 lines
4.1 KiB
TypeScript
import { zodResolver } from "@hookform/resolvers/zod"
|
|
import { SelfServiceRegistrationFlow } from "@ory/client"
|
|
import { useRouter } from "next/router"
|
|
import { useMemo } from "react"
|
|
import { FC, useEffect, useState } from "react"
|
|
import { SubmitHandler, useForm } from "react-hook-form"
|
|
import { z } from "zod"
|
|
import ory from "../services/ory"
|
|
|
|
import FormField from "./FormField"
|
|
import InlineLink from "./InlineLink"
|
|
import Input from "./Input"
|
|
import SubmitButton from "./SubmitButton"
|
|
|
|
const PASSWORD_REGEX =
|
|
/^(?=.*?[A-Z])(?=.*?[a-z])(?=.*?[0-9])(?=.*?[#?!@$ %^&*-]).{8,64}$/
|
|
|
|
const SignupFormSchema = z
|
|
.object({
|
|
csrfToken: z.string(),
|
|
username: z
|
|
.string()
|
|
.trim()
|
|
.min(1, { message: "Username must be at least 1 character long." })
|
|
.max(16, { message: "Username can't be longer than 16 characters.." }),
|
|
password: z
|
|
.string()
|
|
.trim()
|
|
.regex(
|
|
PASSWORD_REGEX,
|
|
"Password must be 8-64 long and must contain a number, uppercase, lowercase and special character.",
|
|
),
|
|
passwordRepeat: z.string().trim(),
|
|
email: z.string().email({ message: "Not a valid email address" }).trim(),
|
|
})
|
|
.refine((data) => data.password === data.passwordRepeat, {
|
|
message: "Passwords do not match.",
|
|
path: ["passwordRepeat"],
|
|
})
|
|
|
|
type SignupFormValues = z.infer<typeof SignupFormSchema>
|
|
|
|
const formFields = [
|
|
{ id: "username", label: "Username", type: "text" },
|
|
{ id: "password", label: "Password", type: "password" },
|
|
{ id: "passwordRepeat", label: "Confirm Password", type: "password" },
|
|
{ id: "email", label: "Email", type: "email" },
|
|
]
|
|
|
|
const SignupForm: FC = () => {
|
|
const router = useRouter()
|
|
const [flow, setFlow] = useState<SelfServiceRegistrationFlow>()
|
|
const { flow: flowId, return_to: returnTo } = router.query
|
|
const { register, handleSubmit, formState } = useForm<SignupFormValues>({
|
|
resolver: zodResolver(SignupFormSchema),
|
|
})
|
|
|
|
useEffect(() => {
|
|
const func = async () => {
|
|
if (!router.isReady || flow) {
|
|
return
|
|
}
|
|
|
|
let serviceFlow
|
|
if (flowId) {
|
|
serviceFlow = await ory.getSelfServiceRegistrationFlow(String(flowId))
|
|
} else {
|
|
serviceFlow =
|
|
await ory.initializeSelfServiceRegistrationFlowForBrowsers(
|
|
returnTo ? String(returnTo) : undefined,
|
|
)
|
|
}
|
|
|
|
setFlow(serviceFlow.data)
|
|
}
|
|
|
|
func()
|
|
}, [flowId, router, router.isReady, returnTo, flow])
|
|
|
|
const onSubmit: SubmitHandler<SignupFormValues> = async (data) => {
|
|
await router.push(`/signup?flow=${flow?.id}`, undefined, {
|
|
shallow: true,
|
|
})
|
|
try {
|
|
const resp = await ory.submitSelfServiceRegistrationFlow(
|
|
String(flow?.id),
|
|
{
|
|
csrf_token: data.csrfToken,
|
|
method: "password",
|
|
password: data.password,
|
|
traits: {
|
|
email: data.email,
|
|
// username: data.username,
|
|
},
|
|
},
|
|
)
|
|
console.log(resp)
|
|
} catch (e) {
|
|
console.log(e)
|
|
}
|
|
}
|
|
|
|
return (
|
|
<form onSubmit={handleSubmit(onSubmit)} className="space-y-4">
|
|
{flow && (
|
|
<Input
|
|
hidden={true}
|
|
value={flow.ui.nodes[0].attributes.value}
|
|
{...register("csrfToken")}
|
|
/>
|
|
)}
|
|
<p className="text-sm">
|
|
Creating an account allows you to participate in chat, follow your
|
|
favorite channels, and broadcast from your own channel.
|
|
</p>
|
|
{formFields.map((field) => (
|
|
<FormField
|
|
key={field.id}
|
|
id={field.id}
|
|
type={field.type}
|
|
label={field.label}
|
|
{...register(field.id as any)}
|
|
className="py-2 px-2 outline-2 w-full"
|
|
bottomElement={
|
|
<p className="text-xs">
|
|
{formState.errors[(field.id as any) || ""]?.message}
|
|
</p>
|
|
}
|
|
/>
|
|
))}
|
|
<p className="text-sm text-center">
|
|
By clicking Sign Up, you are agreeing to twitch-clone's{" "}
|
|
<InlineLink to="https://tosdr.org/en/service/200">
|
|
Terms of Service
|
|
</InlineLink>
|
|
.
|
|
</p>
|
|
<SubmitButton className="w-full" value="Sign Up" />
|
|
</form>
|
|
)
|
|
}
|
|
|
|
export default SignupForm
|