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 <samit91848@gmail.com>
This commit is contained in:
Atchyut Preetham Pulavarthi
2024-07-01 15:45:09 +00:00
committed by GitHub
co-authored by Amit91848
parent 726c938b6d
commit e9d828639a
3 changed files with 42 additions and 49 deletions
@@ -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 () => {
@@ -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<React.Component
const [markAsError, setMarkAsError] = useState(false);
const [openDialogSaveUsername, setOpenDialogSaveUsername] = useState(false);
const debouncedApiCall = useMemo(
() =>
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 () => {
+2 -9
View File
@@ -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 (
<div>