diff --git a/src/hooks/useValidateCanvasName.tsx b/src/hooks/useValidateCanvasName.tsx index 99b29d4ba..ad7ce0b74 100644 --- a/src/hooks/useValidateCanvasName.tsx +++ b/src/hooks/useValidateCanvasName.tsx @@ -1,12 +1,14 @@ -import { useEffect, useState } from "react" +import { useEffect, useRef, useState } from "react" import { usePrevious } from "react-use" import { useDebouncedCallback } from "use-debounce" +import { hexToBool } from "viem" import { CircularProgress, SvgIcon, Typography } from "@mui/material" import SensitiveWord from "@/assets/files/sensitive-word.json" import { ReactComponent as CheckSvg } from "@/assets/svgs/canvas/check.svg" import { ReactComponent as WarningSvg } from "@/assets/svgs/canvas/warning.svg" +import { RPC_URL } from "@/constants" import { useCanvasContext } from "@/contexts/CanvasContextProvider" import useCanvasStore from "@/stores/canvasStore" @@ -15,8 +17,9 @@ const useValidateName = value => { const { username } = useCanvasStore() const preValue = usePrevious(value) - const [helpText, setHelpText] = useState(null) + const [helpText, setHelpText] = useState(null) const [validating, setValidating] = useState(false) + const controller = useRef() useEffect(() => { if ((preValue && !value) || value !== username) { @@ -26,7 +29,7 @@ const useValidateName = value => { const handleValidateName = useDebouncedCallback(async value => { setValidating(true) - const nextHelpText = await validateName(value) + const nextHelpText: string | JSX.Element = await validateName(value) setHelpText(nextHelpText) setValidating(false) return nextHelpText @@ -37,7 +40,11 @@ const useValidateName = value => { } const validateName = async name => { - let nextHelpText + let nextHelpText: string | JSX.Element = "" + if (controller.current) { + controller.current.abort("user input") + } + if (!name) { nextHelpText = "Please enter your name" } else if (!/^[\dA-Za-z_]{4,15}$/g.test(name)) { @@ -49,11 +56,33 @@ const useValidateName = value => { } else if (SensitiveWord.some(word => name.toLowerCase().includes(word.toLowerCase()))) { nextHelpText = "This name is not allowed" } else { - const isUsernameUsed = await unsignedProfileRegistryContract.isUsernameUsed(name) + // const isUsernameUsedCallData = encodeFunctionData({ + // abi: ProfileRegistryABI, + // functionName: "isUsernameUsed", + // args: [name], + // }) + // const callParams = { + // to: requireEnv("REACT_APP_PROFILE_REGISTRY_ADDRESS"), + // data: isUsernameUsedCallData, + // } + // + // TODO: viem will implement request cancellation + const callParams = await unsignedProfileRegistryContract.isUsernameUsed.populateTransaction(name) + const data = { + jsonrpc: "2.0", + method: "eth_call", + params: [callParams, "latest"], + id: 123, + } + controller.current = new AbortController() + const result = await scrollRequest(RPC_URL.L2, { + method: "POST", + body: JSON.stringify(data), + signal: controller.current.signal, + }) + const isUsernameUsed = hexToBool(result.result) if (isUsernameUsed) { nextHelpText = "This name is already taken" - } else { - nextHelpText = "" } } return nextHelpText diff --git a/src/pages/canvas/Dashboard/BadgeDetailDialog/index.tsx b/src/pages/canvas/Dashboard/BadgeDetailDialog/index.tsx index a30c62e5e..a70fd1fff 100644 --- a/src/pages/canvas/Dashboard/BadgeDetailDialog/index.tsx +++ b/src/pages/canvas/Dashboard/BadgeDetailDialog/index.tsx @@ -29,6 +29,7 @@ const StyledScrollButton = styled(ScrollButton)(({ theme }) => ({ })) const ButtonContainer = styled(forwardRef((props, ref) => ))(({ theme }) => ({ + position: "relative", display: "flex", gap: "1.6rem", alignItems: "center", @@ -164,6 +165,15 @@ const BadgeDetailDialog = () => { } } + const renderButtonText = () => { + if (isMobile && selectedBadge.airdrop) { + return "Waiting for issuer to mint" + } else if (isBadgeMinting.get(selectedBadge.badgeContract)) { + return "Minting" + } + return "Mint badge" + } + return ( theme.zIndex.modal + 1 }} @@ -186,7 +196,7 @@ const BadgeDetailDialog = () => { direction="column" alignItems="center" justifyContent={isMobile ? "flex-start" : "center"} - sx={{ width: ["100%", "57.6rem"], height: [`calc(100% - ${actionHeight})`, "auto", "auto", "54.6rem"] }} + sx={{ width: ["100%", "57.6rem"], height: [`calc(100% - ${actionHeight})`, "54.6rem", "54.6rem", "54.6rem"] }} > img { {[BadgeDetailDialogType.MINT, BadgeDetailDialogType.MINT_WITH_BACK].includes(badgeDetailDialogVisible) && ( - - {isBadgeMinting.get(selectedBadge.badgeContract) ? "Minting" : "Mint badge"} + + {renderButtonText()} )} + {!isMobile && selectedBadge.airdrop && ( + + This is an airdrop-only badge and you will receive it once the issuer mint for you. + + )} {/* {[BadgeDetailDialogType.UPGRADE].includes(badgeDetailDialogVisible) && ( diff --git a/src/pages/canvas/badgeContract/index.tsx b/src/pages/canvas/badgeContract/index.tsx index 4bc111fe9..e8a53053e 100644 --- a/src/pages/canvas/badgeContract/index.tsx +++ b/src/pages/canvas/badgeContract/index.tsx @@ -124,7 +124,7 @@ const BadgeContractDetail = props => { ) - } else if (profileMinted && isOwned === false && isEligible) { + } else if (profileMinted && isOwned === false && isEligible && !badgeForMint.airdrop) { return ( <> @@ -133,6 +133,15 @@ const BadgeContractDetail = props => { ) + } else if (profileMinted && isOwned === false && isEligible && badgeForMint.airdrop) { + return ( + <> + + + This is an airdrop-only badge and you will receive it once the issuer mint for you. + + + ) } else if (profileMinted && isOwned === false && !isEligible) { return ( <> @@ -216,7 +225,7 @@ const BadgeContractDetail = props => { color="primary" onClick={handleMint} loading={isBadgeMinting.get(address)} - gloomy={!isEligible} + gloomy={!isEligible || badgeForMint.airdrop} > {isBadgeMinting.get(address) ? "Minting" : "Mint now"} diff --git a/src/pages/ecosystem/Protocols/index.tsx b/src/pages/ecosystem/Protocols/index.tsx index 66fb7a34e..7381e7129 100644 --- a/src/pages/ecosystem/Protocols/index.tsx +++ b/src/pages/ecosystem/Protocols/index.tsx @@ -7,7 +7,7 @@ import useScrollTrigger from "@mui/material/useScrollTrigger" import Button from "@/components/Button" import SectionWrapper from "@/components/SectionWrapper" -import { ECOSYSTEM_NETWORK_LIST, GET_IN_TOUCH_LINK, NORMAL_HEADER_HEIGHT } from "@/constants" +import { ECOSYSTEM_NETWORK_LIST, ECOSYSTEM_PAGE_SYMBOL, GET_IN_TOUCH_LINK, NORMAL_HEADER_HEIGHT } from "@/constants" import useCheckViewport from "@/hooks/useCheckViewport" import Category from "./Category" @@ -114,7 +114,7 @@ const Protocols = () => { )} - +