import { FormEvent, useEffect, useState } from "react"; import { Link } from "react-router-dom"; import { useRecoilValue } from 'recoil' import { sessionStore } from '../../../stores' import { createDID, prepareUsername, register, USERNAME_STORAGE_KEY, } from "../../../lib/auth/account"; import FilesystemActivity from '../../common/FilesystemActivity' const Register = () => { const { program: { components: { crypto, storage }, }, } = useRecoilValue(sessionStore); const [initializingFilesystem, setInitializingFilesystem] = useState(false); const [registrationSuccess, setRegistrationSuccess] = useState(true); const [username, setUsername] = useState('') const [encodedUsername, setEncodedUsername] = useState(''); const [usernameValid] = useState(true); const [usernameAvailable] = useState(true); const [checkingUsername, setCheckingUsername] = useState(false) const [existingAccount, setExistingAccount] = useState(false) const [buttonDisabled, setButtonDisabled] = useState( username.length === 0 || !usernameValid || !usernameAvailable || checkingUsername ); const handleCheckUsername = async ( event: FormEvent<HTMLInputElement> ) => { const { value } = event.target as HTMLInputElement; setUsername(value); setCheckingUsername(true); /** * Create a new DID for the user, which will be appended to their username, concatenated * via a `#`, hashed and encoded to ensure uniqueness */ const did = await createDID(crypto); const fullUsername = `${value}#${did}`; await storage.setItem(USERNAME_STORAGE_KEY, fullUsername); const encodedUsernameLocal = await prepareUsername(fullUsername); setEncodedUsername(encodedUsernameLocal); setCheckingUsername(false); }; const handleRegisterUser = async (event: FormEvent<HTMLFormElement>) => { event.preventDefault(); if (checkingUsername) { return; } setInitializingFilesystem(true); const registrationSuccessLocal = await register(encodedUsername); setRegistrationSuccess(registrationSuccessLocal); if (!registrationSuccessLocal) setInitializingFilesystem(false); }; useEffect(() => { setButtonDisabled( username.length === 0 || !usernameValid || !usernameAvailable || checkingUsername ); }, [username, usernameValid, usernameAvailable, checkingUsername]); if (initializingFilesystem) { return <FilesystemActivity activity="Initializing" />; } return ( <div className="flex flex-col items-center justify-center gap-8 min-h-[calc(100vh-128px)] md:min-h-[calc(100vh-160px)] max-w-[352px] m-auto"> <h1 className="text-base">Connect this device</h1> {/* Registration Form */} <form onSubmit={handleRegisterUser} className="w-full p-6 rounded bg-base-content text-base-100" > <h2 className="mb-2 text-sm font-semibold">Choose a username</h2> <div className="relative"> <input id="registration" type="text" placeholder="Type here" className={`input input-bordered bg-neutral-50 !text-neutral-900 dark:border-neutral-900 rounded-lg focus:outline-none w-full px-3 block ${ !(username.length === 0) && usernameAvailable && usernameValid && !checkingUsername ? "!border-green-300" : "" } ${ username.length !== 0 && (!usernameValid || !usernameAvailable) ? "!border-red-400" : "" }`} onInput={handleCheckUsername} /> {checkingUsername && ( <span className="rounded-lg border-t-2 border-l-2 border-base-content w-4 h-4 block absolute top-4 right-4 animate-spin" /> )} </div> {!registrationSuccess && ( // Error when registration fails <label htmlFor="registration" className="label"> <span className="text-xxs !p-0 text-error text-left"> There was an issue registering your account. Please try again. </span> </label> )} <div className="text-left mt-4"> <input type="checkbox" id="shared-computer" className="peer checkbox checkbox-primary hover:border-base-100 rounded-lg border-2 border-base-100 transition-colors duration-250 ease-in-out inline-grid align-bottom checked:bg-base-100" /> {/* Warning when "This is a shared computer" is checked */} <label htmlFor="shared-computer" className="cursor-pointer ml-1 text-sm grid-inline" > This is a public or shared computer </label> <label htmlFor="registration" className="label mt-4 !p-0 hidden peer-checked:block" > <span className="text-red-400 text-left"> In order to ensure the security of your private data, the ODD SDK does not recommend creating an account from a public or shared computer. </span> </label> </div> <div className="flex items-center mt-4"> <Link className="!h-[52px] btn btn-outline !text-neutral-900 !border-neutral-900 !bg-neutral-50" to="/connect" > Cancel </Link> <button className="ml-2 !h-[52px] flex-1 btn btn-primary disabled:opacity-50 disabled:border-neutral-900 disabled:text-neutral-900" disabled={buttonDisabled} type="submit" > Create your account </button> </div> </form> {/* Existing Account */} <div className="flex flex-col gap-5 w-full"> <button className={`btn btn-outline !h-[52px] w-full ${existingAccount ? '!bg-base-content !text-base-100 !border-base-content' : ''}`} onClick={() => setExistingAccount(!existingAccount)} > I have an existing account </button> {existingAccount && ( <div className="flex flex-col gap-4 p-6 rounded bg-neutral-200 text-neutral-900"> <h3 className="text-sm text-center"> Which device are you connected on? </h3> <p>To connect your existing account, you'll need to:</p> <ol className="pl-6 list-decimal"> <li>Find a device the account is already connected on</li> <li>Navigate to your Account Settings</li> <li>Click “Connect a new device”</li> </ol> </div> )} </div> {/* Recovery Link */} <Link to="/recover" className="underline"> Recover an account </Link> </div> ); }; export default Register;