parent
d4a563fe49
commit
fe651b21bc
2 changed files with 120 additions and 48 deletions
@ -0,0 +1,87 @@ |
|||||||
|
import { useState } from 'react'; |
||||||
|
import type { AllowedProfileVisibility } from '../../api/user'; |
||||||
|
import { httpGet, httpPost } from '../../lib/http'; |
||||||
|
import { useToast } from '../../hooks/use-toast'; |
||||||
|
import { CheckIcon, Loader2, X, XCircle } from 'lucide-react'; |
||||||
|
|
||||||
|
type ProfileUsernameProps = { |
||||||
|
username: string; |
||||||
|
setUsername: (username: string) => void; |
||||||
|
profileVisibility: AllowedProfileVisibility; |
||||||
|
currentUsername?: string; |
||||||
|
}; |
||||||
|
|
||||||
|
export function ProfileUsername(props: ProfileUsernameProps) { |
||||||
|
const { username, setUsername, profileVisibility, currentUsername } = props; |
||||||
|
|
||||||
|
const toast = useToast(); |
||||||
|
const [isLoading, setIsLoading] = useState(false); |
||||||
|
const [isUnique, setIsUnique] = useState<boolean | null>(null); |
||||||
|
|
||||||
|
const checkIsUnique = async (username: string) => { |
||||||
|
if (isLoading || username.length < 3) { |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
if (currentUsername && username === currentUsername && isUnique !== false) { |
||||||
|
setIsUnique(true); |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
setIsLoading(true); |
||||||
|
const { response, error } = await httpPost<{ |
||||||
|
isUnique: boolean; |
||||||
|
}>(`${import.meta.env.PUBLIC_API_URL}/v1-check-is-unique-username`, { |
||||||
|
username, |
||||||
|
}); |
||||||
|
|
||||||
|
if (error || !response) { |
||||||
|
setIsUnique(null); |
||||||
|
setIsLoading(false); |
||||||
|
toast.error(error?.message || 'Something went wrong. Please try again.'); |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
setIsUnique(response.isUnique); |
||||||
|
setIsLoading(false); |
||||||
|
}; |
||||||
|
|
||||||
|
return ( |
||||||
|
<div className="flex w-full flex-col"> |
||||||
|
<label htmlFor="username" className="text-sm leading-none text-slate-500"> |
||||||
|
Username |
||||||
|
</label> |
||||||
|
<div className="mt-2 flex items-center overflow-hidden rounded-lg border border-gray-300"> |
||||||
|
<span className="border-r border-gray-300 bg-gray-100 p-2"> |
||||||
|
roadmap.sh/u/ |
||||||
|
</span> |
||||||
|
|
||||||
|
<div className="relative grow"> |
||||||
|
<input |
||||||
|
type="text" |
||||||
|
name="username" |
||||||
|
id="username" |
||||||
|
className="w-full px-3 py-2 outline-none placeholder:text-gray-400" |
||||||
|
placeholder="johndoe" |
||||||
|
spellCheck={false} |
||||||
|
value={username} |
||||||
|
title="Username must be at least 3 characters long and can only contain letters, numbers, and underscores" |
||||||
|
onChange={(e) => setUsername((e.target as HTMLInputElement).value)} |
||||||
|
onBlur={(e) => checkIsUnique((e.target as HTMLInputElement).value)} |
||||||
|
required={profileVisibility === 'public'} |
||||||
|
/> |
||||||
|
|
||||||
|
<span className="absolute bottom-0 right-0 top-0 flex items-center px-2"> |
||||||
|
{isLoading ? ( |
||||||
|
<Loader2 className="h-4 w-4 animate-spin" /> |
||||||
|
) : isUnique === false ? ( |
||||||
|
<X className="h-4 w-4 text-red-500" /> |
||||||
|
) : isUnique === true ? ( |
||||||
|
<CheckIcon className="h-4 w-4 text-green-500" /> |
||||||
|
) : null} |
||||||
|
</span> |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
); |
||||||
|
} |
Loading…
Reference in new issue