import classNames from "classnames"; import { debounce, noop } from "lodash"; 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, DialogHeader, TextField } from "@calcom/ui"; import { FiCheck, FiEdit2 } 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) => { const { t } = useLocale(); const { currentUsername, setCurrentUsername = noop, inputUsernameValue, setInputUsernameValue, usernameRef, onSuccessMutation, onErrorMutation, } = 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); }, 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 (
{process.env.NEXT_PUBLIC_WEBSITE_URL.replace("https://", "").replace("http://", "")}/ } autoComplete="none" autoCapitalize="none" autoCorrect="none" className={classNames( "mb-0 mt-0 rounded-md ltr:rounded-l-none rtl:rounded-r-none", markAsError ? "focus:shadow-0 focus:ring-shadow-0 border-red-500 focus:border-red-500 focus:outline-none focus:ring-0" : "" )} onChange={(event) => { event.preventDefault(); setInputUsernameValue(event.target.value); }} data-testid="username-input" /> {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 };