import classNames from "classnames"; import { debounce, noop } from "lodash"; import { useSession } from "next-auth/react"; import type { RefCallback } from "react"; import { useEffect, useMemo, useState } from "react"; import { fetchUsername } from "@calcom/lib/fetchUsername"; import { useLocale } from "@calcom/lib/hooks/useLocale"; import type { TRPCClientErrorLike } from "@calcom/trpc/client"; import { trpc } from "@calcom/trpc/react"; import type { AppRouter } from "@calcom/trpc/server/routers/_app"; import { Button, Dialog, DialogClose, DialogContent, TextField, DialogFooter } from "@calcom/ui"; import { Check, Edit2 } from "@calcom/ui/components/icon"; interface ICustomUsernameProps { currentUsername: string | undefined; setCurrentUsername?: (newUsername: string) => void; inputUsernameValue: string | undefined; usernameRef: RefCallback; setInputUsernameValue: (value: string) => void; onSuccessMutation?: () => void; onErrorMutation?: (error: TRPCClientErrorLike) => void; } const UsernameTextfield = (props: ICustomUsernameProps & Partial>) => { const { t } = useLocale(); const { data: session, update } = useSession(); const { currentUsername, setCurrentUsername = noop, inputUsernameValue, setInputUsernameValue, usernameRef, onSuccessMutation, onErrorMutation, ...rest } = props; const [usernameIsAvailable, setUsernameIsAvailable] = useState(false); const [markAsError, setMarkAsError] = useState(false); const [openDialogSaveUsername, setOpenDialogSaveUsername] = useState(false); const debouncedApiCall = useMemo( () => debounce(async (username) => { const { data } = await fetchUsername(username); setMarkAsError(!data.available); setUsernameIsAvailable(data.available); }, 150), [] ); useEffect(() => { if (!inputUsernameValue) { debouncedApiCall.cancel(); setUsernameIsAvailable(false); setMarkAsError(false); return; } if (currentUsername !== inputUsernameValue) { debouncedApiCall(inputUsernameValue); } else { setUsernameIsAvailable(false); } }, [inputUsernameValue, debouncedApiCall, currentUsername]); const utils = trpc.useContext(); const updateUsernameMutation = trpc.viewer.updateProfile.useMutation({ onSuccess: async () => { onSuccessMutation && (await onSuccessMutation()); setOpenDialogSaveUsername(false); setCurrentUsername(inputUsernameValue); await update({ username: inputUsernameValue }); }, onError: (error) => { onErrorMutation && onErrorMutation(error); }, async onSettled() { await utils.viewer.public.i18n.invalidate(); }, }); const ActionButtons = () => { return usernameIsAvailable && currentUsername !== inputUsernameValue ? (
) : ( <> ); }; const updateUsername = async () => { updateUsernameMutation.mutate({ username: inputUsernameValue, }); }; return (
{ event.preventDefault(); setInputUsernameValue(event.target.value); }} data-testid="username-input" {...rest} /> {currentUsername !== inputUsernameValue && (
{usernameIsAvailable ? : <>}
)}
{markAsError &&

{t("username_already_taken")}

} {usernameIsAvailable && currentUsername !== inputUsernameValue && (
)}

{t("current_username")}

{currentUsername}

{t("new_username")}

{inputUsernameValue}

setOpenDialogSaveUsername(false)}> {t("cancel")}
); }; export { UsernameTextfield };