From e9d828639aa1348aa2ea5609af7ea0ac408af056 Mon Sep 17 00:00:00 2001 From: Atchyut Preetham Pulavarthi Date: Mon, 1 Jul 2024 21:15:09 +0530 Subject: [PATCH] fix(@cal.com/web): update debounce logic to work correctly and not show non-premium usernames as premium (#15629) * fix(@cal.com/web): update debounce logic to work correctly and not show non-premium usernames as premium - removes redundant useMemo hook from UsernameTextfield.tsx & PremiumTextField.tsx - cancles the debounceAPICall on every unmount of UsernameTextfield.tsx & PremiumTextfield.tsx via useEffect cleanup function Fixes #15628 * fix(@cal.com/web): update the debounce logic to use the pre-existing useDebounce hook - update the debounce logic in UsernameTextField.tsx & PremiumTextField.tsx to use the pre-existing useDebounce hook instead of loadsh debounce - update the debounce delay to 600ms to be consistent with the signup.tsx behavior Fixes #15628 * fix: signup debounce not working properly --------- Co-authored-by: Amit91848 --- .../UsernameAvailability/PremiumTextfield.tsx | 38 +++++++++-------- .../UsernameTextfield.tsx | 42 +++++++++---------- apps/web/pages/signup.tsx | 11 +---- 3 files changed, 42 insertions(+), 49 deletions(-) diff --git a/apps/web/components/ui/UsernameAvailability/PremiumTextfield.tsx b/apps/web/components/ui/UsernameAvailability/PremiumTextfield.tsx index 0137343522..da811b4db8 100644 --- a/apps/web/components/ui/UsernameAvailability/PremiumTextfield.tsx +++ b/apps/web/components/ui/UsernameAvailability/PremiumTextfield.tsx @@ -1,15 +1,16 @@ import classNames from "classnames"; // eslint-disable-next-line no-restricted-imports -import { debounce, noop } from "lodash"; +import { noop } from "lodash"; import { useSession } from "next-auth/react"; import { usePathname, useRouter, useSearchParams } from "next/navigation"; import type { RefCallback } from "react"; -import { useEffect, useMemo, useState } from "react"; +import { useEffect, useState } from "react"; import { getPremiumPlanPriceValue } from "@calcom/app-store/stripepayment/lib/utils"; import { WEBAPP_URL } from "@calcom/lib/constants"; import { fetchUsername } from "@calcom/lib/fetchUsername"; import hasKeyInMetadata from "@calcom/lib/hasKeyInMetadata"; +import { useDebounce } from "@calcom/lib/hooks/useDebounce"; import { useLocale } from "@calcom/lib/hooks/useLocale"; import type { TRPCClientErrorLike } from "@calcom/trpc/client"; import type { RouterOutputs } from "@calcom/trpc/react"; @@ -71,17 +72,8 @@ const PremiumTextfield = (props: ICustomUsernameProps) => { const isCurrentUsernamePremium = user && user.metadata && hasKeyInMetadata(user, "isPremium") ? !!user.metadata.isPremium : false; const [isInputUsernamePremium, setIsInputUsernamePremium] = useState(false); - const debouncedApiCall = useMemo( - () => - debounce(async (username: string) => { - // TODO: Support orgSlug - const { data } = await fetchUsername(username, null); - setMarkAsError(!data.available && !!currentUsername && username !== currentUsername); - setIsInputUsernamePremium(data.premium); - setUsernameIsAvailable(data.available); - }, 150), - [currentUsername] - ); + // debounce the username input, set the delay to 600ms to be consistent with signup form + const debouncedUsername = useDebounce(inputUsernameValue, 600); useEffect(() => { // Use the current username or if it's not set, use the one available from stripe @@ -89,12 +81,22 @@ const PremiumTextfield = (props: ICustomUsernameProps) => { }, [setInputUsernameValue, currentUsername, stripeCustomer?.username]); useEffect(() => { - if (!inputUsernameValue) { - debouncedApiCall.cancel(); - return; + async function checkUsername(username: string | undefined) { + if (!username) { + setUsernameIsAvailable(false); + setMarkAsError(false); + setIsInputUsernamePremium(false); + return; + } + + const { data } = await fetchUsername(username, null); + setMarkAsError(!data.available && !!currentUsername && username !== currentUsername); + setIsInputUsernamePremium(data.premium); + setUsernameIsAvailable(data.available); } - debouncedApiCall(inputUsernameValue); - }, [debouncedApiCall, inputUsernameValue]); + + checkUsername(debouncedUsername); + }, [debouncedUsername, currentUsername]); const updateUsername = trpc.viewer.updateProfile.useMutation({ onSuccess: async () => { diff --git a/apps/web/components/ui/UsernameAvailability/UsernameTextfield.tsx b/apps/web/components/ui/UsernameAvailability/UsernameTextfield.tsx index a1b44460a2..bf6b0d2eb5 100644 --- a/apps/web/components/ui/UsernameAvailability/UsernameTextfield.tsx +++ b/apps/web/components/ui/UsernameAvailability/UsernameTextfield.tsx @@ -1,11 +1,12 @@ import classNames from "classnames"; // eslint-disable-next-line no-restricted-imports -import { debounce, noop } from "lodash"; +import { noop } from "lodash"; import { useSession } from "next-auth/react"; import type { RefCallback } from "react"; -import { useEffect, useMemo, useState } from "react"; +import { useEffect, useState } from "react"; import { fetchUsername } from "@calcom/lib/fetchUsername"; +import { useDebounce } from "@calcom/lib/hooks/useDebounce"; import { useLocale } from "@calcom/lib/hooks/useLocale"; import type { TRPCClientErrorLike } from "@calcom/trpc/client"; import { trpc } from "@calcom/trpc/react"; @@ -41,31 +42,28 @@ const UsernameTextfield = (props: ICustomUsernameProps & Partial - debounce(async (username) => { - // TODO: Support orgSlug + // debounce the username input, set the delay to 600ms to be consistent with signup form + const debouncedUsername = useDebounce(inputUsernameValue, 600); + + useEffect(() => { + async function checkUsername(username: string | undefined) { + if (!username) { + setUsernameIsAvailable(false); + setMarkAsError(false); + return; + } + + if (currentUsername !== username) { const { data } = await fetchUsername(username, null); setMarkAsError(!data.available); setUsernameIsAvailable(data.available); - }, 150), - [] - ); - - useEffect(() => { - if (!inputUsernameValue) { - debouncedApiCall.cancel(); - setUsernameIsAvailable(false); - setMarkAsError(false); - return; + } else { + setUsernameIsAvailable(false); + } } - if (currentUsername !== inputUsernameValue) { - debouncedApiCall(inputUsernameValue); - } else { - setUsernameIsAvailable(false); - } - }, [inputUsernameValue, debouncedApiCall, currentUsername]); + checkUsername(debouncedUsername); + }, [debouncedUsername, currentUsername]); const updateUsernameMutation = trpc.viewer.updateProfile.useMutation({ onSuccess: async () => { diff --git a/apps/web/pages/signup.tsx b/apps/web/pages/signup.tsx index c3c929b223..6e06df7bcd 100644 --- a/apps/web/pages/signup.tsx +++ b/apps/web/pages/signup.tsx @@ -123,15 +123,8 @@ function UsernameField({ }); } checkUsername(); - }, [ - debouncedUsername, - setPremium, - disabled, - orgSlug, - setUsernameTaken, - formState.isSubmitting, - formState.isSubmitSuccessful, - ]); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [debouncedUsername, disabled, orgSlug, formState.isSubmitting, formState.isSubmitSuccessful]); return (