From bcf88271867b8700b579409e398aed5a1df132c1 Mon Sep 17 00:00:00 2001 From: HeesungB Date: Fri, 16 Dec 2022 00:43:18 +0900 Subject: [PATCH] [WIP] Fix QA --- .pnp.cjs | 4 + components/back-button/index.tsx | 61 +++++ components/chain-list/all-chains-item.tsx | 3 +- components/chain-list/chain-item.tsx | 14 +- components/chain-list/chain-list.tsx | 6 +- .../connect-wallet-modal/wallet-item.tsx | 25 +- components/logo/index.tsx | 5 +- components/primary-button/index.ts | 6 +- components/skeleton/skeleton-chain-list.tsx | 158 ++++++------ components/twitter-profile/index.tsx | 2 +- constants/error-message.ts | 1 + constants/icns.ts | 4 + constants/twitter.ts | 2 + constants/wallet.ts | 2 + messages/icns.ts | 2 + package.json | 2 + pages/complete/index.tsx | 90 +++++-- pages/index.tsx | 27 ++- pages/verification/index.tsx | 225 ++++++++++++------ public/images/svg/arrow-left.svg | 3 + queries/icns.ts | 22 ++ styles/color.ts | 4 +- types/api-response.ts | 6 + utils/url.ts | 8 +- yarn.lock | 4 +- 25 files changed, 490 insertions(+), 196 deletions(-) create mode 100644 components/back-button/index.tsx create mode 100644 public/images/svg/arrow-left.svg diff --git a/.pnp.cjs b/.pnp.cjs index ce20aef..7d9d170 100755 --- a/.pnp.cjs +++ b/.pnp.cjs @@ -38,6 +38,7 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { ["@types/react", "npm:18.0.25"],\ ["@types/react-dom", "npm:18.0.9"],\ ["@types/react-modal", "npm:3.13.1"],\ + ["@types/semver", "npm:7.3.13"],\ ["@types/styled-components", "npm:5.1.26"],\ ["@typescript-eslint/eslint-plugin", "virtual:4b77e00d446246df1ed27001550885fbf1b51be18c660c1b5c357d3d763078ecef2a676194291a120f149b87573081e5af0621dc83cf1f83383639f39ac133c7#npm:5.45.0"],\ ["@typescript-eslint/parser", "virtual:4b77e00d446246df1ed27001550885fbf1b51be18c660c1b5c357d3d763078ecef2a676194291a120f149b87573081e5af0621dc83cf1f83383639f39ac133c7#npm:5.45.0"],\ @@ -62,6 +63,7 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { ["react-is", "npm:18.2.0"],\ ["react-modal", "virtual:4b77e00d446246df1ed27001550885fbf1b51be18c660c1b5c357d3d763078ecef2a676194291a120f149b87573081e5af0621dc83cf1f83383639f39ac133c7#npm:3.16.1"],\ ["react-typed", "virtual:4b77e00d446246df1ed27001550885fbf1b51be18c660c1b5c357d3d763078ecef2a676194291a120f149b87573081e5af0621dc83cf1f83383639f39ac133c7#npm:1.2.0"],\ + ["semver", "npm:7.3.8"],\ ["styled-components", "virtual:4b77e00d446246df1ed27001550885fbf1b51be18c660c1b5c357d3d763078ecef2a676194291a120f149b87573081e5af0621dc83cf1f83383639f39ac133c7#npm:5.3.6"],\ ["typescript", "patch:typescript@npm%3A4.9.3#~builtin::version=4.9.3&hash=d73830"]\ ],\ @@ -3985,6 +3987,7 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { ["@types/react", "npm:18.0.25"],\ ["@types/react-dom", "npm:18.0.9"],\ ["@types/react-modal", "npm:3.13.1"],\ + ["@types/semver", "npm:7.3.13"],\ ["@types/styled-components", "npm:5.1.26"],\ ["@typescript-eslint/eslint-plugin", "virtual:4b77e00d446246df1ed27001550885fbf1b51be18c660c1b5c357d3d763078ecef2a676194291a120f149b87573081e5af0621dc83cf1f83383639f39ac133c7#npm:5.45.0"],\ ["@typescript-eslint/parser", "virtual:4b77e00d446246df1ed27001550885fbf1b51be18c660c1b5c357d3d763078ecef2a676194291a120f149b87573081e5af0621dc83cf1f83383639f39ac133c7#npm:5.45.0"],\ @@ -4009,6 +4012,7 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { ["react-is", "npm:18.2.0"],\ ["react-modal", "virtual:4b77e00d446246df1ed27001550885fbf1b51be18c660c1b5c357d3d763078ecef2a676194291a120f149b87573081e5af0621dc83cf1f83383639f39ac133c7#npm:3.16.1"],\ ["react-typed", "virtual:4b77e00d446246df1ed27001550885fbf1b51be18c660c1b5c357d3d763078ecef2a676194291a120f149b87573081e5af0621dc83cf1f83383639f39ac133c7#npm:1.2.0"],\ + ["semver", "npm:7.3.8"],\ ["styled-components", "virtual:4b77e00d446246df1ed27001550885fbf1b51be18c660c1b5c357d3d763078ecef2a676194291a120f149b87573081e5af0621dc83cf1f83383639f39ac133c7#npm:5.3.6"],\ ["typescript", "patch:typescript@npm%3A4.9.3#~builtin::version=4.9.3&hash=d73830"]\ ],\ diff --git a/components/back-button/index.tsx b/components/back-button/index.tsx new file mode 100644 index 0000000..f323e69 --- /dev/null +++ b/components/back-button/index.tsx @@ -0,0 +1,61 @@ +import { FunctionComponent } from "react"; +import styled from "styled-components"; +import color from "../../styles/color"; + +import ArrowLeftIcon from "../../public/images/svg/arrow-left.svg"; +import Image from "next/image"; + +export const BackButton: FunctionComponent = () => { + return ( + { + location.href = "/"; + }} + > + + + arrow left icon + +
BACK
+
+
+ ); +}; + +const Container = styled.div` + display: flex; + justify-content: flex-end; + + width: 100%; + padding: 0.7rem 0.5rem; + + font-family: "Inter", serif; + font-style: normal; + font-weight: 500; + font-size: 0.8rem; + line-height: 0.8rem; + + color: ${color.grey["400"]}; + + cursor: pointer; +`; + +const ContentContainer = styled.div` + display: flex; + flex-direction: row; + align-items: center; + + gap: 0.25rem; +`; + +const IconContainer = styled.div` + position: relative; + + width: 1rem; + height: 1rem; +`; diff --git a/components/chain-list/all-chains-item.tsx b/components/chain-list/all-chains-item.tsx index 8a64eee..409d3d5 100644 --- a/components/chain-list/all-chains-item.tsx +++ b/components/chain-list/all-chains-item.tsx @@ -32,6 +32,7 @@ export const AllChainsItem: FunctionComponent = (props) => { @@ -57,5 +58,5 @@ export const AllChainsItem: FunctionComponent = (props) => { const AllChainsContainer = styled.div` width: 100%; - background-color: ${color.grey["800"]}; + background-color: ${color.grey["900"]}; `; diff --git a/components/chain-list/chain-item.tsx b/components/chain-list/chain-item.tsx index c1eb37f..f69311e 100644 --- a/components/chain-list/chain-item.tsx +++ b/components/chain-list/chain-item.tsx @@ -15,7 +15,7 @@ interface Props { export const ChainItem: FunctionComponent = (props) => { const { chainItem, checkedItemHandler, checkedItems, disabled } = props; - const [checked, setChecked] = useState(disabled); + const [checked, setChecked] = useState(!!disabled); const checkHandler = () => { if (!disabled) { @@ -35,6 +35,7 @@ export const ChainItem: FunctionComponent = (props) => { key={chainItem.prefix} isLoading={false} disabled={disabled} + checked={checked} onClick={checkHandler} > @@ -71,10 +72,17 @@ export const ChainItemContainer = styled.div<{ cursor: pointer; - opacity: ${(props) => (props.disabled ? "0.3" : "1")}; + opacity: ${(props) => (props.disabled ? "0.5" : "1")}; + + background-color: ${(props) => + props.disabled + ? color.black + : props.checked + ? color.grey["800"] + : color.grey["900"]}; &:hover { - background: ${(props) => (props.isLoading ? null : color.grey["600"])}; + background: ${(props) => (props.isLoading ? null : color.grey["700"])}; } `; diff --git a/components/chain-list/chain-list.tsx b/components/chain-list/chain-list.tsx index b8bc785..9796425 100644 --- a/components/chain-list/chain-list.tsx +++ b/components/chain-list/chain-list.tsx @@ -52,7 +52,7 @@ export const ChainList: FunctionComponent = (props) => { }, [checkedItems]); return ( - + {chainList.map((chainItem) => ( props.color}; `; diff --git a/components/connect-wallet-modal/wallet-item.tsx b/components/connect-wallet-modal/wallet-item.tsx index 07cf8db..1f851d8 100644 --- a/components/connect-wallet-modal/wallet-item.tsx +++ b/components/connect-wallet-modal/wallet-item.tsx @@ -1,24 +1,34 @@ -import { FunctionComponent } from "react"; +import { FunctionComponent, useEffect, useState } from "react"; import ArrowRightIcon from "../../public/images/svg/arrow-right.svg"; import color from "../../styles/color"; import { Flex1 } from "../../styles/flex-1"; import styled from "styled-components"; import Image from "next/image"; import { + MINIMUM_VERSION, SELECTED_WALLET_KEY, WALLET_INSTALL_URL, WalletType, } from "../../constants/wallet"; import { getKeplrFromWindow, KeplrWallet } from "../../wallets"; import { loginWithTwitter } from "../../queries"; +import { + KEPLR_NOT_FOUND_ERROR, + KEPLR_VERSION_ERROR, +} from "../../constants/error-message"; +import semver from "semver/preload"; interface Props { wallet: WalletType; } -// Todo: Wallet 관련된 부분을 Context로 빼는 부분 export const WalletItem: FunctionComponent = (props: Props) => { const { wallet } = props; + const [isInstalled, setIsInstalled] = useState(); + + useEffect(() => { + setIsInstalled(!!window.keplr); + }, []); const onClickWalletItem = async () => { try { @@ -38,6 +48,11 @@ export const WalletItem: FunctionComponent = (props: Props) => { if (keplr === undefined) { window.location.href = WALLET_INSTALL_URL; + throw new Error(KEPLR_NOT_FOUND_ERROR); + } + + if (semver.lt(keplr.version, MINIMUM_VERSION)) { + throw new Error(KEPLR_VERSION_ERROR); } if (keplr) { @@ -62,7 +77,11 @@ export const WalletItem: FunctionComponent = (props: Props) => { {wallet.name} - {wallet.isReady ? null : ( + {wallet.isReady ? ( + isInstalled ? null : ( + Go to install Keplr Extension + ) + ) : ( Comming soon )} diff --git a/components/logo/index.tsx b/components/logo/index.tsx index 3f96f86..dbca3be 100644 --- a/components/logo/index.tsx +++ b/components/logo/index.tsx @@ -31,10 +31,9 @@ export const LogoContainer = styled.div` justify-content: center; position: absolute; - width: 10rem; height: 5rem; - margin-top: 80px; - margin-left: 80px; + margin-top: 5rem; + margin-left: 5rem; `; diff --git a/components/primary-button/index.ts b/components/primary-button/index.ts index 8f651f1..ef81430 100644 --- a/components/primary-button/index.ts +++ b/components/primary-button/index.ts @@ -7,7 +7,6 @@ export const PrimaryButton = styled.button` border: none; - background-color: ${color.orange["100"]}; padding: 11px 30px; font-family: "Inter", serif; @@ -17,6 +16,7 @@ export const PrimaryButton = styled.button` line-height: 20px; color: ${color.orange["50"]}; + background-color: ${color.orange["100"]}; cursor: pointer; @@ -26,6 +26,8 @@ export const PrimaryButton = styled.button` } &:disabled { - background-color: ${color.orange["200"]}; + opacity: 0.5; + + background-color: ${color.orange["300"]}; } `; diff --git a/components/skeleton/skeleton-chain-list.tsx b/components/skeleton/skeleton-chain-list.tsx index 26db68b..61fa32c 100644 --- a/components/skeleton/skeleton-chain-list.tsx +++ b/components/skeleton/skeleton-chain-list.tsx @@ -27,99 +27,105 @@ import { } from "../chain-list"; export const SkeletonChainList: FunctionComponent = () => ( - - - + + + + - - - - - - - + + + + + + + - - - - + + + + - - - + + + - - - + + + - - - - - - - - - - + + + + + + + + + + - + - - - - - - - - - + + + + + + + + + - + - - - - - - - - - + + + + + + + + + - + - - - - - - - - - + + + + + + + + + - + - - - - - - - - - + + + + + + + + + - - + + - - - - + + + + + ); +const SkeletonContainer = styled.div` + padding: 2.4rem 0; +`; + const SkeletonTitle = styled.div` width: 8rem; height: 1.5rem; diff --git a/components/twitter-profile/index.tsx b/components/twitter-profile/index.tsx index 3be22df..f2375c1 100644 --- a/components/twitter-profile/index.tsx +++ b/components/twitter-profile/index.tsx @@ -12,7 +12,7 @@ export const TwitterProfile: FunctionComponent = (props) => { const { twitterProfileInformation } = props; return ( - + { return makeCosmwasmExecMsg( senderAddress, @@ -26,6 +27,7 @@ export const makeClaimMessage = ( }; } }), + referral, }, }, [ContractFee], diff --git a/package.json b/package.json index 0966d8c..d99e666 100644 --- a/package.json +++ b/package.json @@ -24,6 +24,7 @@ "react-is": "^18.2.0", "react-modal": "^3.16.1", "react-typed": "^1.2.0", + "semver": "^7.3.8", "styled-components": "^5.3.6" }, "devDependencies": { @@ -33,6 +34,7 @@ "@types/react": "18.0.25", "@types/react-dom": "18.0.9", "@types/react-modal": "^3", + "@types/semver": "^7", "@types/styled-components": "^5", "@typescript-eslint/eslint-plugin": "^5.45.0", "@typescript-eslint/parser": "^5.45.0", diff --git a/pages/complete/index.tsx b/pages/complete/index.tsx index 59c99ad..7bad0d5 100644 --- a/pages/complete/index.tsx +++ b/pages/complete/index.tsx @@ -9,31 +9,56 @@ import color from "../../styles/color"; import AlertCircleOutlineIcon from "../../public/images/svg/alert-circle-outline.svg"; import TwitterIcon from "../../public/images/svg/twitter-icon.svg"; import { useRouter } from "next/router"; -import { useEffect } from "react"; +import { useEffect, useState } from "react"; import { TendermintTxTracer } from "@keplr-wallet/cosmos"; +import { queryAddressesFromTwitterName } from "../../queries"; +import { RegisteredAddresses } from "../../types"; +import { SHARE_URL } from "../../constants/twitter"; export default function CompletePage() { const router = useRouter(); + const [registeredAddressed, setRegisteredAddressed] = + useState(); + + const [availableAddress, setAvailableAddress] = useState(""); + useEffect(() => { - const { txHash } = router.query; + const { txHash, twitterUsername } = router.query; - if (txHash) { - traceTX(txHash as string); + if (txHash && twitterUsername) { + initialize(txHash as string, twitterUsername as string); } - }, []); + }, [router.query]); - const traceTX = async (txHash: string) => { + const initialize = async (txHash: string, twitterUserName: string) => { const txTracer = new TendermintTxTracer( "https://rpc.testnet.osmosis.zone", "/websocket", ); - const result = await txTracer.traceTx(Buffer.from(txHash, "hex")); + const result: { code?: number } = await txTracer.traceTx( + Buffer.from(txHash, "hex"), + ); - console.log(result); + if (result.code || result.code === 0) { + const addresses = await queryAddressesFromTwitterName(twitterUserName); + setRegisteredAddressed(addresses.data.addresses); + } + }; - // Todo rsult => 확인 후에 확인 + const onClickShareButton = () => { + const { twitterUsername } = router.query; + + const width = 500; + const height = 700; + window.open( + `${SHARE_URL}?url=https://www.icns.xyz/&text=${twitterUsername}`, + "Share Twitter", + `top=${(window.screen.height - height) / 2}, left=${ + (window.screen.width - width) / 2 + }, width=${width}, height=${height}, status=no, menubar=no, toolbar=no, resizable=no`, + ); }; return ( @@ -41,22 +66,31 @@ export default function CompletePage() { - Your Name is Active Now! + +
Your Name is Active Now!
+
Recipent - kingstarcookies. - + {`${router.query.twitterUsername}.`} + {registeredAddressed && ( + address.bech32_prefix, + )} + typeSpeed={150} + backSpeed={150} + backDelay={1000} + loop + smartBackspace + onStringTyped={(arrayPos: number) => { + setAvailableAddress(registeredAddressed[arrayPos].address); + }} + /> + )} - available address + {availableAddress} @@ -70,7 +104,7 @@ export default function CompletePage() { - + SHARE MY NAME twitter icon @@ -95,19 +129,22 @@ const MainContainer = styled.div` `; const MainTitle = styled.div` + display: flex; + align-items: center; + justify-content: center; + font-family: "Inter", serif; font-style: normal; font-weight: 700; font-size: 2rem; line-height: 2rem; - padding: 1rem; + height: 5rem; `; const ContentContainer = styled.div` width: 30rem; - margin-top: 1rem; padding: 2rem 2rem; background-color: ${color.grey["900"]}; @@ -152,19 +189,22 @@ const AvailableAddressText = styled.div` font-size: 0.75rem; line-height: 0.75rem; + min-height: 0.75rem; + color: ${color.blue}; `; const DescriptionContainer = styled.div` display: flex; flex-direction: row; + align-items: center; gap: 1rem; width: 30rem; margin-top: 1.5rem; - padding: 1.5rem 2rem; + padding: 1.25rem 2rem; background-color: ${color.grey["900"]}; `; @@ -183,7 +223,7 @@ const DescriptionText = styled.div` font-style: normal; font-weight: 400; font-size: 0.8rem; - line-height: 0.8rem; + line-height: 140%; color: ${color.grey["400"]}; `; diff --git a/pages/index.tsx b/pages/index.tsx index cdff9c5..72b3a8c 100644 --- a/pages/index.tsx +++ b/pages/index.tsx @@ -16,6 +16,8 @@ import CheckIcon from "../public/images/svg/check-icon.svg"; import { Logo } from "../components/logo"; import { useEffect, useState } from "react"; import { SELECTED_WALLET_KEY } from "../constants/wallet"; +import { replaceToInstallPage } from "../utils/url"; +import { REFERRAL_KEY } from "../constants/icns"; export default function Home() { const [isModalOpen, setModalOpen] = useState(false); @@ -25,6 +27,17 @@ export default function Home() { }; useEffect(() => { + localStorage.removeItem(REFERRAL_KEY); + + if (window.location.search) { + const [, referral] = + window.location.search.match(/^(?=.*referral=([^&]+)|).+$/) || []; + + if (referral) { + localStorage.setItem(REFERRAL_KEY, referral); + } + } + localStorage.removeItem(SELECTED_WALLET_KEY); }, []); @@ -62,7 +75,8 @@ export default function Home() { /> You are a  keplr  user. - if not, you can install here + if not, you can install  + HERE @@ -182,7 +196,16 @@ const CheckContainer = styled.div` text-transform: uppercase; - color: ${color.grey["300"]}; + padding-left: 0.75rem; + + color: ${color.grey["400"]}; +`; + +const InstallLInk = styled.a` + color: ${color.grey["400"]}; + text-decoration: underline; + + cursor: pointer; `; const CheckBoldText = styled.span` diff --git a/pages/verification/index.tsx b/pages/verification/index.tsx index 9ebdad7..76d7d1e 100644 --- a/pages/verification/index.tsx +++ b/pages/verification/index.tsx @@ -4,6 +4,7 @@ import { useEffect, useState } from "react"; // Types import { ChainItemType, + QueryError, RegisteredAddresses, TwitterProfileType, } from "../../types"; @@ -32,11 +33,17 @@ import { ChainIdHelper } from "@keplr-wallet/cosmos"; import AllChainsIcon from "../../public/images/svg/all-chains-icon.svg"; import { AllChainsItem } from "../../components/chain-list/all-chains-item"; import { SearchInput } from "../../components/search-input"; -import { MainChainId, RESOLVER_ADDRESS, REST_URL } from "../../constants/icns"; +import { + MainChainId, + REFERRAL_KEY, + RESOLVER_ADDRESS, + REST_URL, +} from "../../constants/icns"; import { fetchTwitterInfo, queryAddressesFromTwitterName, + queryOwnerOfTwitterName, queryRegisteredTwitterId, verifyTwitterAccount, } from "../../queries"; @@ -46,6 +53,8 @@ import { TWITTER_LOGIN_ERROR, } from "../../constants/error-message"; import { makeClaimMessage, makeSetRecordMessage } from "../../messages"; +import Axios, { AxiosError } from "axios"; +import { BackButton } from "../../components/back-button"; export default function VerificationPage() { const router = useRouter(); @@ -69,6 +78,9 @@ export default function VerificationPage() { const [searchValue, setSearchValue] = useState(""); + const [isOwner, setIsOwner] = useState(false); + const [isAgree, setIsAgree] = useState(false); + useEffect(() => { const init = async () => { if (window.location.search) { @@ -78,7 +90,7 @@ export default function VerificationPage() { ); // Initialize Wallet - await initWallet(); + const keplrWallet = await initWallet(); // Fetch Twitter Profile const twitterInfo = await fetchTwitterInfo(state, code); @@ -94,10 +106,19 @@ export default function VerificationPage() { }); if ("data" in registeredQueryResponse) { + const ownerOfQueryResponse = await queryOwnerOfTwitterName( + registeredQueryResponse.data.name, + ); + const addressesQueryResponse = await queryAddressesFromTwitterName( registeredQueryResponse.data.name, ); + if (keplrWallet) { + const key = await keplrWallet.getKey(MainChainId); + setIsOwner(ownerOfQueryResponse.data.owner === key.bech32Address); + } + setRegisteredChainList(addressesQueryResponse.data.addresses); } } catch (error) { @@ -153,6 +174,8 @@ export default function VerificationPage() { await fetchChainList(keplrWallet); setWallet(keplrWallet); + + return keplrWallet; } else { ErrorHandler(KEPLR_NOT_FOUND_ERROR); } @@ -227,84 +250,99 @@ export default function VerificationPage() { }; const onClickRegistration = async () => { - const { state, code } = checkTwitterAuthQueryParameter( - window.location.search, - ); - const twitterInfo = await fetchTwitterInfo(state, code); - - const adr36Infos = await checkAdr36(); - - if (wallet && adr36Infos) { - const key = await wallet.getKey(MainChainId); - - const icnsVerificationList = await verifyTwitterAccount( - key.bech32Address, - twitterInfo.accessToken, + try { + const { state, code } = checkTwitterAuthQueryParameter( + window.location.search, ); + const twitterInfo = await fetchTwitterInfo(state, code); - const registerMsg = makeClaimMessage( - key.bech32Address, - twitterInfo.username, - icnsVerificationList, - ); + const adr36Infos = await checkAdr36(); - const addressMsgs = adr36Infos.map((adr36Info) => { - return makeSetRecordMessage( + if (wallet && adr36Infos) { + const key = await wallet.getKey(MainChainId); + + const icnsVerificationList = await verifyTwitterAccount( + key.bech32Address, + twitterInfo.accessToken, + ); + + const registerMsg = makeClaimMessage( key.bech32Address, twitterInfo.username, - adr36Info, + icnsVerificationList, + localStorage.getItem(REFERRAL_KEY) ?? undefined, ); - }); - const aminoMsgs = twitterAuthInfo?.isRegistered - ? [] - : [registerMsg.amino]; - const protoMsgs = twitterAuthInfo?.isRegistered - ? [] - : [registerMsg.proto]; + const addressMsgs = adr36Infos.map((adr36Info) => { + return makeSetRecordMessage( + key.bech32Address, + twitterInfo.username, + adr36Info, + ); + }); - for (const addressMsg of addressMsgs) { - aminoMsgs.push(addressMsg.amino); - protoMsgs.push(addressMsg.proto); + const aminoMsgs = twitterAuthInfo?.isRegistered + ? [] + : [registerMsg.amino]; + const protoMsgs = twitterAuthInfo?.isRegistered + ? [] + : [registerMsg.proto]; + + for (const addressMsg of addressMsgs) { + aminoMsgs.push(addressMsg.amino); + protoMsgs.push(addressMsg.proto); + } + + const chainInfo = { + chainId: MainChainId, + rest: REST_URL, + }; + + const simulated = await simulateMsgs( + chainInfo, + key.bech32Address, + { + proto: protoMsgs, + }, + { + amount: [], + }, + ); + + const txHash = await sendMsgs( + wallet, + chainInfo, + key.bech32Address, + { + amino: aminoMsgs, + proto: protoMsgs, + }, + { + amount: [], + gas: Math.floor(simulated.gasUsed * 1.5).toString(), + }, + ); + + await router.push({ + pathname: "complete", + query: { + txHash: Buffer.from(txHash).toString("hex"), + twitterUsername: twitterInfo.username, + }, + }); + } + } catch (error) { + if (Axios.isAxiosError(error)) { + console.error((error?.response?.data as QueryError).message); } - - const chainInfo = { - chainId: MainChainId, - rest: REST_URL, - }; - - const simulated = await simulateMsgs( - chainInfo, - key.bech32Address, - { - proto: protoMsgs, - }, - { - amount: [], - }, - ); - - const txHash = await sendMsgs( - wallet, - chainInfo, - key.bech32Address, - { - amino: aminoMsgs, - proto: protoMsgs, - }, - { - amount: [], - gas: Math.floor(simulated.gasUsed * 1.5).toString(), - }, - ); - - await router.push({ - pathname: "complete", - query: { txHash: Buffer.from(txHash).toString("hex") }, - }); } }; + const isRegisterButtonDisable = + checkedItems.size < 1 || + (!isOwner && registeredChainList.length > 0) || + !isAgree; + return ( @@ -314,6 +352,7 @@ export default function VerificationPage() { ) : ( + @@ -351,9 +390,21 @@ export default function VerificationPage() { setCheckedItems={setCheckedItems} /> - + + { + setIsAgree(!isAgree); + }} + readOnly + /> + I check that Osmo is required for this transaction + + + Register @@ -375,6 +426,10 @@ const MainContainer = styled.div` display: flex; justify-content: center; + height: 100vh; + + padding: 2.7rem 0; + color: white; `; @@ -384,15 +439,14 @@ export const ContentContainer = styled.div` align-items: center; width: 40rem; - - margin-top: 5rem; `; -export const ButtonContainer = styled.div` +export const ButtonContainer = styled.div<{ disabled?: boolean }>` width: 12rem; height: 4rem; - margin-top: 2rem; + background-color: ${(props) => + props.disabled ? color.orange["300"] : color.orange["100"]}; `; export const ChainListTitleContainer = styled.div` @@ -414,3 +468,26 @@ const ChainListTitle = styled.div` color: ${color.white}; `; + +const AgreeContainer = styled.div` + display: flex; + align-items: center; + gap: 0.5rem; + + font-family: "Inter", serif; + font-style: normal; + font-weight: 500; + font-size: 0.8rem; + line-height: 0.8rem; + + text-transform: uppercase; + + color: ${color.grey["400"]}; + + padding: 2rem 0; +`; + +const AgreeCheckBox = styled.input.attrs({ type: "checkbox" })` + width: 1.2rem; + height: 1.2rem; +`; diff --git a/public/images/svg/arrow-left.svg b/public/images/svg/arrow-left.svg new file mode 100644 index 0000000..dbe776e --- /dev/null +++ b/public/images/svg/arrow-left.svg @@ -0,0 +1,3 @@ + + + diff --git a/queries/icns.ts b/queries/icns.ts index f1b0ae3..afedfc2 100644 --- a/queries/icns.ts +++ b/queries/icns.ts @@ -1,5 +1,6 @@ import { request } from "../utils/url"; import { + NAME_NFT_ADDRESS, REGISTRAR_ADDRESS, RESOLVER_ADDRESS, REST_URL, @@ -8,6 +9,7 @@ import { Buffer } from "buffer/"; import { AddressesQueryResponse, NameByTwitterIdQueryResponse, + OwnerOfQueryResponse, QueryError, } from "../types"; @@ -20,11 +22,14 @@ export const queryRegisteredTwitterId = async ( const msg = { name_by_twitter_id: { twitter_id: twitterId }, }; + return request( getCosmwasmQueryUrl( REGISTRAR_ADDRESS, Buffer.from(JSON.stringify(msg)).toString("base64"), ), + {}, + true, ); }; @@ -42,3 +47,20 @@ export const queryAddressesFromTwitterName = async ( ), ); }; + +export const queryOwnerOfTwitterName = async ( + twitterUsername: string, +): Promise => { + const msg = { + owner_of: { token_id: twitterUsername }, + }; + + return request( + getCosmwasmQueryUrl( + NAME_NFT_ADDRESS, + Buffer.from(JSON.stringify(msg)).toString("base64"), + ), + {}, + true, + ); +}; diff --git a/styles/color.ts b/styles/color.ts index 02e86fa..a43ba82 100644 --- a/styles/color.ts +++ b/styles/color.ts @@ -16,8 +16,8 @@ const grey = { 400: "#5B5B5B", 500: "#424242", 600: "#333333", - 700: "#2B2B2B", - 800: "#242424", + 700: "#222222", + 800: "#1D1D1D", 900: "#181818", }; const black = "#121212"; diff --git a/types/api-response.ts b/types/api-response.ts index 19f9117..5e23327 100644 --- a/types/api-response.ts +++ b/types/api-response.ts @@ -59,3 +59,9 @@ export interface QueryError { code: number; message: string; } + +export interface OwnerOfQueryResponse { + data: { + owner: string; + }; +} diff --git a/utils/url.ts b/utils/url.ts index f89d962..074cfd3 100644 --- a/utils/url.ts +++ b/utils/url.ts @@ -1,13 +1,15 @@ import { TwitterLoginSuccess } from "../types"; import { TWITTER_LOGIN_ERROR } from "../constants/error-message"; +import { WALLET_INSTALL_URL } from "../constants/wallet"; export function request( url: string, config: RequestInit = {}, + isIgnore?: boolean, ): Promise { return fetch(url, config) .then((response) => { - if (!response.ok) { + if (!response.ok && !isIgnore) { throw new Error( `This is an HTTP error: The status is ${response.status} ${response.statusText}`, ); @@ -44,3 +46,7 @@ export const checkTwitterAuthQueryParameter = ( code, }; }; + +export const replaceToInstallPage = () => { + window.location.href = WALLET_INSTALL_URL; +}; diff --git a/yarn.lock b/yarn.lock index 9b57b36..cee0709 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1109,7 +1109,7 @@ __metadata: languageName: node linkType: hard -"@types/semver@npm:^7.3.12": +"@types/semver@npm:^7, @types/semver@npm:^7.3.12": version: 7.3.13 resolution: "@types/semver@npm:7.3.13" checksum: 00c0724d54757c2f4bc60b5032fe91cda6410e48689633d5f35ece8a0a66445e3e57fa1d6e07eb780f792e82ac542948ec4d0b76eb3484297b79bd18b8cf1cb0 @@ -3175,6 +3175,7 @@ __metadata: "@types/react": 18.0.25 "@types/react-dom": 18.0.9 "@types/react-modal": ^3 + "@types/semver": ^7 "@types/styled-components": ^5 "@typescript-eslint/eslint-plugin": ^5.45.0 "@typescript-eslint/parser": ^5.45.0 @@ -3199,6 +3200,7 @@ __metadata: react-is: ^18.2.0 react-modal: ^3.16.1 react-typed: ^1.2.0 + semver: ^7.3.8 styled-components: ^5.3.6 typescript: 4.9.3 languageName: unknown