Compare commits

..
Author SHA1 Message Date
Thunnini da89c51b4c Show warning message if the account is not owner of name 2022-12-18 23:10:23 +09:00
Thunnini 68dfe40ee5 Merge branch 'Thunnini/final-modal-redegisn' 2022-12-18 22:47:52 +09:00
Thunnini 28a44a9195 Redesign FinalCheckModal component 2022-12-18 22:47:24 +09:00
delivan d23090f9f8 Update style of home page 2022-12-18 22:14:10 +09:00
delivan 6eb3eb885c Update background color 2022-12-18 21:58:39 +09:00
Thunnini 6a49b69a02 Merge branch 'Thunnini/ledger-ethereum-warning' 2022-12-18 21:40:12 +09:00
Thunnini 53ecf825a0 Minor fix 2022-12-18 21:30:37 +09:00
Thunnini 5d48012133 Add DisabledChainItemType type and prevent registration from ethermint chain with ledger 2022-12-18 21:01:34 +09:00
delivan 5fac2f9156 Add loading state to register button on final check modal 2022-12-18 20:24:33 +09:00
Thunnini 3de363af0f Merge branch 'main' into Thunnini/ledger-ethereum-warning
# Conflicts:
#	.pnp.cjs
#	package.json
#	yarn.lock
2022-12-18 19:42:16 +09:00
delivan eead90d4b9 Change empty chain image to icns logo 2022-12-18 18:45:49 +09:00
delivan a61856557c Add loading state on primary button 2022-12-18 18:38:56 +09:00
delivan 7e72ab82b6 Update primary button style 2022-12-18 17:59:29 +09:00
HeesungB 68d8ba31aa Add final check modal 2022-12-18 01:51:29 +09:00
delivan e0151666b2 Merge branch 'main' of https://github.com/interchain-name/icns-frontend into main 2022-12-17 23:46:02 +09:00
delivan b41ef751fb Track registration flow 2022-12-17 23:45:50 +09:00
delivan e884fc1d86 Add Amplitude 2022-12-17 22:40:07 +09:00
HeesungB 537e650c5e Fix register button disable condition 2022-12-17 22:23:22 +09:00
HeesungB 278ff37129 Fix all chains logic 2022-12-17 22:10:38 +09:00
HeesungB 76b74d2c96 Remove chain image warning 2022-12-17 21:34:14 +09:00
HeesungB 7b8f4910bb Change chain list when changed keplr account 2022-12-17 21:01:27 +09:00
delivan 1abc475354 Add Sentry 2022-12-17 20:42:56 +09:00
Thunnini d488d31345 Change typing of interface Wallet 2022-12-17 20:24:17 +09:00
Thunnini 80907781ad Bump @keplr-wallet/* 2022-12-17 20:22:03 +09:00
delivan 11a17e64b8 Add og data and favicons 2022-12-17 18:37:14 +09:00
delivan 75c281fea5 Add bg asset on verification page 2022-12-17 18:21:36 +09:00
delivan 70a58e9bdf Merge branch 'main' of https://github.com/interchain-name/icns-frontend into main 2022-12-17 18:11:00 +09:00
delivan 1194ccb375 Add home page bg assets 2022-12-17 18:10:53 +09:00
Thunnini 02b0cde9b1 Minor styling fix to search input component and add transition 2022-12-17 18:04:21 +09:00
Thunnini 618442b58e Add checkbox component and improve styling of checkbox 2022-12-17 17:50:59 +09:00
HeesungB 913e5d2193 Change search input height 2022-12-17 17:00:25 +09:00
HeesungB 148a94e4ea Change skeleton color 2022-12-17 16:48:35 +09:00
HeesungB d1221475a7 Hide share button in complete page 2022-12-17 02:07:58 +09:00
HeesungB 52e585f83c Fix referral link 2022-12-17 02:01:38 +09:00
HeesungB a35e7bb9af Fix complete page, Fix Osmo fee in index page 2022-12-17 01:57:10 +09:00
HeesungB 75656943a1 Merge branch 'main' of github.com:interchain-name/icns-frontend 2022-12-17 01:25:28 +09:00
HeesungB 357b65b587 Fix verification check logic 2022-12-17 01:25:10 +09:00
delivan 2efe20f0a0 Merge branch 'main' of https://github.com/interchain-name/icns-frontend into main 2022-12-17 00:53:45 +09:00
delivan 11b79b7c40 Update chain-image component 2022-12-17 00:53:40 +09:00
HeesungB 0944e5c175 Fix all chains bug 2022-12-17 00:52:37 +09:00
HeesungB 0eba7c4cf9 Add .evn settings, StyledComponent Server Side Setting 2022-12-17 00:36:07 +09:00
Thunnini dd8f108cb7 Minor fix 2022-12-17 00:29:56 +09:00
HeesungB b4bf1f4ee8 Fix styles(background, skeleton) 2022-12-16 18:21:35 +09:00
delivan 41c4ed39ec Update theme on global styles 2022-12-16 18:14:47 +09:00
delivan 3fc581aad9 Merge branch 'main' of https://github.com/interchain-name/icns-frontend into main 2022-12-16 17:33:32 +09:00
delivan 3d72c4d959 Add ThemeProvider for page background style 2022-12-16 17:32:08 +09:00
HeesungB 4cc4213f8c [WIP] design QA 2022-12-16 15:55:31 +09:00
HeesungB 0dd50d218b Fix style 2022-12-16 00:45:55 +09:00
HeesungB bcf8827186 [WIP] Fix QA 2022-12-16 00:43:18 +09:00
HeesungB bb58fb54ea Add disabled chain list 2022-12-15 19:06:44 +09:00
HeesungB 7de3767d03 [WIP] Refactoring Logics 2022-12-15 15:02:50 +09:00
HeesungB 13de62028d Remove next image warning 2022-12-15 13:25:07 +09:00
delivan a3dc47b5eb Modify to throw error on request promise 2022-12-15 01:07:58 +09:00
delivan d14eead3ba Merge branch 'main' of https://github.com/interchain-name/icns-frontend into main 2022-12-15 00:41:57 +09:00
delivan d0ba550eaf Add error handling 2022-12-15 00:39:51 +09:00
HeesungB 8897eac973 [WIP] : ICNS Query 2022-12-14 20:36:08 +09:00
HeesungB 2655c35c32 Remove unused file 2022-12-14 16:54:08 +09:00
HeesungB 75d50bbe89 Refactor repository 2022-12-14 16:52:05 +09:00
HeesungB 01be54ee28 Change the order of Adr36 and twitter verifier 2022-12-14 14:50:07 +09:00
HeesungB 8504c84d3c [WIP] Simulate 2022-12-13 23:46:32 +09:00
HeesungB febde1e7c4 Remove config.ts 2022-12-13 22:11:28 +09:00
HeesungB 1602ef92c8 Add search input 2022-12-13 22:10:31 +09:00
HeesungB 1dd9e31911 Add all chains item, Fix checkbox logic 2022-12-13 21:21:16 +09:00
HeesungB 10e129baad [WIP] Error fix 2022-12-12 22:58:47 +09:00
HeesungB 921ec5d7e6 [WIP] Add checkbox handler, chain image error handler, keplr init logic 2022-12-12 22:45:24 +09:00
Thunnini 8319467125 Fix unknown problem 2022-12-12 18:58:07 +09:00
Thunnini 71abd3560e Change some typings 2022-12-12 17:51:52 +09:00
Thunnini 97161e0a48 Use Buffer package instead of Buffer on nodejs 2022-12-12 17:51:48 +09:00
HeesungB b80273c1cc Remove unused source file 2022-12-12 17:46:50 +09:00
HeesungB cdd7c797f5 Add text animation in complete page 2022-12-12 17:40:36 +09:00
HeesungB b6926ecf3b Add complete page 2022-12-12 17:16:16 +09:00
delivan 07de65b265 Fix lint 2022-12-12 16:10:11 +09:00
delivan b47c09fb6f Merge remote-tracking branch 'origin/Thunnini/wallet-utils'
# Conflicts:
#	yarn.lock
2022-12-12 16:02:13 +09:00
HeesungB 9db9a0478b Merge branch 'main' of github.com:interchain-name/icns-frontend 2022-12-12 15:51:41 +09:00
HeesungB 564b401331 Add select wallet modal component 2022-12-12 15:51:25 +09:00
delivan f16159d189 Add session destruction when re-signin 2022-12-12 15:48:34 +09:00
HeesungB b33309493a Merge branch 'main' of github.com:interchain-name/icns-frontend
# Conflicts:
#	pages/verification/index.tsx
2022-12-12 15:42:13 +09:00
delivan 8771fd1880 Update README.md 2022-12-12 15:33:39 +09:00
delivan cd81a19f00 Update to get verification list 2022-12-12 15:31:52 +09:00
HeesungB fc58cad6a8 Divided into several components 2022-12-09 20:59:52 +09:00
189 changed files with 5006 additions and 776 deletions
+3 -1
View File
@@ -49,4 +49,6 @@ build
.sentryclirc
# Intelij files
.idea
.idea
# Sentry
.sentryclirc
Generated
+891 -33
View File
File diff suppressed because it is too large Load Diff
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+2 -1
View File
@@ -12,7 +12,8 @@ TWITTER_AUTH_CALLBACK_URI=...
IRON_PASSWORD=...
ICNS_VERIFIER_URI=...
# must be separated by commas
ICNS_VERIFIER_ORIGIN_LIST=http:localhost:8080,http://localhost:8081
```
Run the development server:
+61
View File
@@ -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 (
<Container
onClick={() => {
location.href = "/";
}}
>
<ContentContainer>
<IconContainer>
<Image
src={ArrowLeftIcon}
fill={true}
sizes="1rem"
alt="arrow left icon"
/>
</IconContainer>
<div>BACK</div>
</ContentContainer>
</Container>
);
};
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;
`;
+78
View File
@@ -0,0 +1,78 @@
import { ChainItemType } from "../../types";
import {
Dispatch,
FunctionComponent,
SetStateAction,
useEffect,
useState,
} from "react";
import { ChainImage } from "./chain-image";
import { Flex1 } from "../../styles/flex-1";
import {
ChainInfoContainer,
ChainItemContainer,
ChainName,
WalletAddress,
} from "./chain-item";
import color from "../../styles/color";
import styled from "styled-components";
import { Checkbox } from "../checkbox";
import AllChainsIcon from "../../public/images/svg/all-chains-icon.svg";
interface Props {
chainList: ChainItemType[];
checkedItems: Set<unknown>;
setCheckedItems: Dispatch<SetStateAction<Set<unknown>>>;
}
export const AllChainsItem: FunctionComponent<Props> = (props) => {
const { chainList, checkedItems, setCheckedItems } = props;
const [checked, setChecked] = useState(false);
const checkHandler = () => {
if (checked) {
setCheckedItems(new Set());
} else if (chainList.length !== checkedItems.size) {
setCheckedItems(new Set(chainList));
}
};
useEffect(() => {
if (chainList.length === checkedItems.size && checkedItems.size !== 0) {
setChecked(true);
} else {
setChecked(false);
}
}, [checkedItems]);
return (
<AllChainsContainer>
<ChainItemContainer
key="all chains"
isLoading={false}
checked={checked}
onClick={checkHandler}
>
<ChainImage src={AllChainsIcon} fill={true} alt={`all chain images`} />
<ChainInfoContainer>
<ChainName>{`.all chains(${chainList.length})`}</ChainName>
<WalletAddress>
{chainList.map((chain) => chain.chainName).join(", ")}
</WalletAddress>
</ChainInfoContainer>
<Flex1 />
<Checkbox checked={checked} />
</ChainItemContainer>
</AllChainsContainer>
);
};
const AllChainsContainer = styled.div`
width: 100%;
background-color: ${color.grey["900"]};
`;
+31
View File
@@ -0,0 +1,31 @@
import { useState } from "react";
import Image, { ImageProps } from "next/image";
import ICNSLogo from "../../public/images/icns-logo-120x120.png";
import styled from "styled-components";
export const ChainImage = ({ src, ...props }: ImageProps) => {
const [srcState, setSrcState] = useState(src);
return (
<ImageWrapper>
<Image
{...props}
src={srcState}
alt="chain image"
sizes="3rem"
onError={() => setSrcState(ICNSLogo)}
/>
</ImageWrapper>
);
};
const ImageWrapper = styled.div`
position: relative;
width: 3rem;
height: 3rem;
img {
border-radius: 50%;
}
`;
+147
View File
@@ -0,0 +1,147 @@
import { ChainItemType, DisabledChainItemType } from "../../types";
import { FunctionComponent, useEffect, useState } from "react";
import color from "../../styles/color";
import { Flex1 } from "../../styles/flex-1";
import styled from "styled-components";
import { ChainImage } from "./chain-image";
import { Checkbox } from "../checkbox";
interface Props {
chainItem: ChainItemType | DisabledChainItemType;
checkedItemHandler: (chainItem: ChainItemType, isChecked: boolean) => void;
checkedItems: Set<unknown>;
}
export const ChainItem: FunctionComponent<Props> = (props) => {
const { chainItem, checkedItemHandler, checkedItems } = props;
const disabled = "disabled" in chainItem && chainItem.disabled;
// XXX: Currently, this component can't handle `checked` state well,
// If chain is disabled, it should be disabled in general.
// However, if it is disabled due to the limitation of ethermint and ledger,
// it should be not checked.
// To solve this problem, for now, just use dumb way.
// If chain is disabled with explicit reason, it should be unchecked.
const [checked, setChecked] = useState(!!disabled && !chainItem.reason);
useEffect(() => {
if (disabled) {
if (chainItem.reason) {
setChecked(false);
} else {
setChecked(true);
}
}
}, [chainItem, disabled]);
const checkHandler = () => {
if (!disabled) {
setChecked(!checked);
checkedItemHandler(chainItem, !checked);
}
};
useEffect(() => {
if (!disabled) {
setChecked(checkedItems.has(chainItem));
}
}, [checkedItems]);
return (
<ChainItemContainer
key={chainItem.prefix}
isLoading={false}
disabled={disabled}
checked={checked}
onClick={checkHandler}
>
<ChainImage
src={chainItem.chainImageUrl}
fill={true}
alt={`${chainItem.prefix} chain image`}
/>
<ChainInfoContainer>
<ChainName>{`.${chainItem.prefix}`}</ChainName>
{chainItem.address ? (
<WalletAddress>{chainItem.address}</WalletAddress>
) : null}
{disabled && chainItem.reason ? (
<DisabledReason>{chainItem.reason.message}</DisabledReason>
) : null}
</ChainInfoContainer>
<Flex1 />
<Checkbox checked={checked} />
</ChainItemContainer>
);
};
export const ChainItemContainer = styled.div<{
isLoading: boolean;
checked?: boolean;
disabled?: boolean;
isSkeleton?: boolean;
}>`
display: flex;
flex-direction: row;
align-items: center;
gap: 1rem;
padding: 1.5rem;
cursor: pointer;
opacity: ${(props) => (props.disabled ? "0.5" : "1")};
background-color: ${(props) =>
props.disabled
? color.black
: props.checked
? color.grey["800"]
: props.isSkeleton
? color.grey["800"]
: color.grey["900"]};
&:hover {
background: ${(props) => (props.isLoading ? null : color.grey["700"])};
}
`;
export const ChainInfoContainer = styled.div`
display: flex;
flex-direction: column;
gap: 0.5rem;
`;
export const ChainName = styled.div`
font-weight: 600;
font-size: 0.8rem;
line-height: 1rem;
color: ${color.white};
`;
export const WalletAddress = styled.div`
font-weight: 500;
font-size: 0.8rem;
line-height: 1rem;
max-height: 2rem;
max-width: 27rem;
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
overflow: hidden;
text-overflow: ellipsis;
color: ${color.grey["400"]};
`;
export const DisabledReason = styled.div`
color: ${color.grey["200"]};
font-weight: 500;
font-size: 14px;
line-height: 17px;
`;
+61
View File
@@ -0,0 +1,61 @@
import { Dispatch, FunctionComponent, SetStateAction, useEffect } from "react";
import { ChainItemType, DisabledChainItemType } from "../../types";
import color from "../../styles/color";
import styled from "styled-components";
import { ChainItem } from "./chain-item";
interface Props {
chainList: ChainItemType[];
disabledChainList: DisabledChainItemType[];
checkedItems: Set<unknown>;
setCheckedItems: Dispatch<SetStateAction<Set<unknown>>>;
}
export const ChainList: FunctionComponent<Props> = (props) => {
const { chainList, disabledChainList, checkedItems, setCheckedItems } = props;
const checkedItemHandler = (chainItem: ChainItemType, isChecked: boolean) => {
const tempSet = new Set(checkedItems);
if (isChecked) {
tempSet.add(chainItem);
} else if (!isChecked && checkedItems.has(chainItem)) {
tempSet.delete(chainItem);
}
setCheckedItems(tempSet);
};
return (
<ChainContainer color={color.grey["900"]}>
{chainList.map((chainItem) => (
<ChainItem
key={chainItem.chainId}
chainItem={chainItem}
checkedItemHandler={checkedItemHandler}
checkedItems={checkedItems}
/>
))}
{disabledChainList.map((chainItem) => (
<ChainItem
key={chainItem.chainId}
chainItem={chainItem}
checkedItemHandler={checkedItemHandler}
checkedItems={checkedItems}
/>
))}
</ChainContainer>
);
};
export const ChainContainer = styled.div`
display: flex;
flex-direction: column;
width: 100%;
//max-height: 33rem;
overflow: scroll;
flex: 1;
background-color: ${(props) => props.color};
`;
+2
View File
@@ -0,0 +1,2 @@
export * from "./chain-list";
export * from "./chain-item";
+70
View File
@@ -0,0 +1,70 @@
import { FunctionComponent } from "react";
import color from "../../styles/color";
import styled from "styled-components";
export const Checkbox: FunctionComponent<{ checked: boolean }> = ({
checked,
}) => {
return (
<ChainCheckBoxContainer>
<ChainCheckBoxHidden checked={checked} readOnly={true} />
<ChainCheckBoxStyled checked={checked}>
<Center>
<Icon width="18" height="14" viewBox="0 0 15 12">
<path
strokeLinecap="square"
strokeWidth="2"
d="M2.25 4.426l4.083 5.011 6.417-7.874"
/>
</Icon>
</Center>
</ChainCheckBoxStyled>
</ChainCheckBoxContainer>
);
};
const Center = styled.div`
width: 100%;
height: 100%;
display: flex;
align-items: center;
justify-content: center;
`;
const Icon = styled.svg`
fill: none;
stroke: ${color.orange["50"]};
`;
const ChainCheckBoxContainer = styled.div`
position: relative;
display: inline-block;
vertical-align: middle;
`;
const ChainCheckBoxStyled = styled.div<{ checked: boolean }>`
display: inline-block;
width: 1.5rem;
height: 1.5rem;
border: ${(props) => (props.checked ? "none" : "2px solid #868686")};
background: ${(props) =>
props.checked ? color.orange["200"] : color.grey["400"]};
${Icon} {
visibility: ${(props) => (props.checked ? "visible" : "hidden")};
}
`;
const ChainCheckBoxHidden = styled.input.attrs({ type: "checkbox" })`
border: 0;
clip: rect(0 0 0 0);
clip-path: inset(50%);
height: 1px;
margin: -1px;
overflow: hidden;
padding: 0;
position: absolute;
white-space: nowrap;
width: 1px;
`;
+2
View File
@@ -0,0 +1,2 @@
export * from "./modal";
export * from "./wallet-item";
+77
View File
@@ -0,0 +1,77 @@
import { FunctionComponent } from "react";
import ReactModal from "react-modal";
import styled from "styled-components";
import color from "../../styles/color";
import { WalletList } from "../../constants/wallet";
import { WalletItem } from "./wallet-item";
interface Props {
isModalOpen: boolean;
onCloseModal: () => void;
}
export const ConnectWalletModal: FunctionComponent<Props> = (props) => {
const { isModalOpen, onCloseModal } = props;
return (
<ReactModal
isOpen={isModalOpen}
onRequestClose={onCloseModal}
ariaHideApp={false}
style={{
overlay: { background: "#121212cc" },
content: {
top: "50%",
left: "50%",
right: "auto",
bottom: "auto",
padding: 0,
marginRight: "-50%",
transform: "translate(-50%, -50%)",
background: color.grey["800"],
border: 0,
},
}}
>
<ModalContainer>
<ModalTitle>Connect Wallet</ModalTitle>
<ModalDescription>
Plz check which account is selected after you connect it
</ModalDescription>
{WalletList.map((walletItem) => {
return <WalletItem wallet={walletItem} key={walletItem.name} />;
})}
</ModalContainer>
</ReactModal>
);
};
const ModalContainer = styled.div`
display: flex;
flex-direction: column;
gap: 0.9rem;
padding: 2.2rem;
min-width: 28rem;
`;
const ModalTitle = styled.div`
font-family: "Inter", serif;
font-style: normal;
font-weight: 600;
font-size: 1.5rem;
line-height: 1.8rem;
color: ${color.white};
`;
const ModalDescription = styled.div`
font-family: "Inter", serif;
font-style: normal;
font-weight: 500;
font-size: 0.8rem;
line-height: 1.1rem;
color: ${color.grey["400"]};
`;
@@ -0,0 +1,144 @@
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;
}
export const WalletItem: FunctionComponent<Props> = (props: Props) => {
const { wallet } = props;
const [isInstalled, setIsInstalled] = useState<boolean>();
useEffect(() => {
setIsInstalled(!!window.keplr);
}, []);
const onClickWalletItem = async () => {
try {
if (wallet.name === "Keplr") {
await connectKeplr();
localStorage.setItem(SELECTED_WALLET_KEY, wallet.name);
}
await loginWithTwitter();
} catch (e) {
console.log(e);
}
};
const connectKeplr = async () => {
const keplr = await getKeplrFromWindow();
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) {
const wallet = new KeplrWallet(keplr);
const chainIds = (await wallet.getChainInfosWithoutEndpoints()).map(
(c) => c.chainId,
);
await wallet.init(chainIds);
}
};
return (
<WalletContainer isReady={wallet.isReady} onClick={onClickWalletItem}>
<WalletIcon>
<Image
src={wallet.image}
fill={true}
sizes="3.75rem"
alt="wallet Icon"
/>
</WalletIcon>
<WalletContentContainer>
<WalletName>{wallet.name}</WalletName>
{wallet.isReady ? (
isInstalled ? null : (
<WalletDescription>Go to install Keplr Extension</WalletDescription>
)
) : (
<WalletDescription>Comming soon</WalletDescription>
)}
</WalletContentContainer>
<Flex1 />
<Image src={ArrowRightIcon} alt="arrow right icon" />
</WalletContainer>
);
};
const WalletContainer = styled.div<{ isReady: boolean }>`
display: flex;
flex-direction: row;
align-items: center;
height: 5.8rem;
padding: 1rem;
background-color: ${color.grey["600"]};
opacity: ${(props) => (props.isReady ? 1 : 0.3)};
cursor: ${(props) => (props.isReady ? "pointer" : "default")};
`;
const WalletIcon = styled.div`
position: relative;
width: 3.75rem;
height: 3.75rem;
`;
const WalletContentContainer = styled.div`
display: flex;
flex-direction: column;
align-items: flex-start;
padding-left: 1rem;
`;
const WalletName = styled.div`
font-family: "Inter", serif;
font-style: normal;
font-weight: 600;
font-size: 1.3rem;
line-height: 1.5rem;
text-align: center;
color: ${color.white};
`;
const WalletDescription = styled.div`
font-family: "Inter", serif;
font-style: normal;
font-weight: 600;
font-size: 0.8rem;
line-height: 1.1rem;
color: ${color.grey["300"]};
`;
+43
View File
@@ -0,0 +1,43 @@
import { captureException } from "@sentry/nextjs";
import Link from "next/link";
import { Component, ErrorInfo, ReactNode } from "react";
interface Props {
children?: ReactNode;
}
interface State {
hasError: boolean;
}
class ErrorBoundary extends Component<Props, State> {
public state: State = {
hasError: false,
};
public static getDerivedStateFromError(): State {
// Update state so the next render will show the fallback UI.
return { hasError: true };
}
public componentDidCatch(error: Error, errorInfo: ErrorInfo) {
console.error("Uncaught error:", error, errorInfo);
captureException(error);
}
public render() {
if (this.state.hasError) {
return (
<h1>
Oops.. there is something wrong.
<br /> Please try again. <Link href="/">Go to home</Link>
</h1>
);
}
return this.props.children;
}
}
export default ErrorBoundary;
+300
View File
@@ -0,0 +1,300 @@
import { FunctionComponent } from "react";
import color from "../../styles/color";
import ReactModal from "react-modal";
import styled from "styled-components";
import TwitterIcon from "../../public/images/svg/twitter-modal-icon.svg";
import Image from "next/image";
import { PrimaryButton } from "../primary-button";
import { SecondaryButton } from "../secondary-button";
import { MINIMUM_OSMO_FEE } from "../../constants/wallet";
import { useRouter } from "next/router";
import { Bech32Address } from "@keplr-wallet/cosmos";
interface Props {
twitterUserName: string | undefined;
walletInfo:
| { name: string; pubKey: Uint8Array; bech32Address: string }
| undefined;
isModalOpen: boolean;
onCloseModal: () => void;
onClickRegisterButton: () => Promise<void>;
isLoadingRegistration?: boolean;
}
export const FinalCheckModal: FunctionComponent<Props> = (props) => {
const {
twitterUserName,
walletInfo,
isModalOpen,
onCloseModal,
onClickRegisterButton,
isLoadingRegistration,
} = props;
const router = useRouter();
return (
<ReactModal
isOpen={isModalOpen}
onRequestClose={onCloseModal}
ariaHideApp={false}
style={{
overlay: { background: "#121212cc" },
content: {
top: "50%",
left: "50%",
right: "auto",
bottom: "auto",
padding: 0,
marginRight: "-50%",
transform: "translate(-50%, -50%)",
background: color.grey["800"],
border: 0,
},
}}
>
<ModalContainer>
<ModalTitle>Final Checks</ModalTitle>
<ModalDescription>{`You are claiming the ICNS name ${twitterUserName} on main keplr account`}</ModalDescription>
<NameBox
marginTop="3.875rem"
icon={
<Image
src={require("../../public/images/icons/twitter-small.png")}
alt="twitter"
width={24}
height={24}
style={{
marginRight: "4px",
}}
/>
}
title="Your Twitter ID"
content={`@${twitterUserName}`}
/>
<div
style={{
width: "100%",
height: "3.625rem",
display: "flex",
alignItems: "center",
justifyContent: "center",
}}
>
<svg
xmlns="http://www.w3.org/2000/svg"
width="48"
height="48"
fill="none"
viewBox="0 0 48 48"
>
<path
stroke="#EBEBEB"
strokeLinecap="round"
strokeWidth="2"
d="M18.44 19.008h-2.656a5.162 5.162 0 00-5.162 5.162v0a5.162 5.162 0 005.162 5.162h2.655M29.305 28.988h2.655a5.162 5.162 0 005.162-5.162v0a5.162 5.162 0 00-5.162-5.162h-2.655M19.295 24.242h9.155"
/>
</svg>
</div>
<NameBox
marginTop="0"
icon={
<Image
src={require("../../public/images/icons/keplr-small.png")}
alt="twitter"
width={20}
height={20}
style={{
marginRight: "8px",
}}
/>
}
title="Main Keplr Account"
content={Bech32Address.shortenAddress(
walletInfo?.bech32Address || "",
28,
)}
/>
<SubTextsContainer>
<SubText>
ICNS name can only be claimed once per Twitter account.
<br />
ICNS name cant be transferred at this time.
<br />
Please make sure youve selected the right account on your
wallet.
</SubText>
<br />
<SubText>
<SubBoldText>{MINIMUM_OSMO_FEE}</SubBoldText> will be spent as a
spam-prevention fee.
</SubText>
</SubTextsContainer>
<ButtonContainer>
<RegisterButton>
<PrimaryButton
onClick={onClickRegisterButton}
isLoading={isLoadingRegistration}
>
Register
</PrimaryButton>
</RegisterButton>
<CancelButton>
<SecondaryButton
onClick={async () => {
await router.push("/");
}}
>
Use a different account
</SecondaryButton>
</CancelButton>
</ButtonContainer>
</ModalContainer>
</ReactModal>
);
};
const ModalContainer = styled.div`
display: flex;
flex-direction: column;
max-width: 43.5rem;
padding: 2rem 2.25rem;
font-family: "Inter", serif;
font-style: normal;
`;
const ModalTitle = styled.div`
font-weight: 600;
font-size: 1.625rem;
line-height: 1.94rem;
color: ${color.white};
margin-bottom: 1.75rem;
`;
const ModalDescription = styled.div`
font-weight: 500;
font-size: 1rem;
line-height: 1.18rem;
color: ${color.grey["100"]};
`;
const SubTextsContainer = styled.div`
margin-top: 1.75rem;
padding: 2rem 1.5rem;
background-color: ${color.grey["700"]};
`;
const SubText = styled.div`
font-weight: 500;
font-size: 1rem;
line-height: 1.5rem;
color: ${color.grey["300"]};
`;
const SubBoldText = styled.span`
color: ${color.grey["100"]};
`;
const ButtonContainer = styled.div`
display: flex;
flex-direction: column;
align-items: center;
margin-top: 1.75rem;
`;
const RegisterButton = styled.div`
width: 60%;
height: 4.125rem;
`;
const CancelButton = styled.div`
width: 80%;
height: 3.8rem;
`;
const NameBox: FunctionComponent<{
title: string;
content: string;
icon?: React.ReactElement;
marginTop: string;
}> = ({ icon, title, content, marginTop }) => {
return (
<NameBoxContainer
style={{
marginTop,
}}
>
<NameBoxTitleContainer>
{icon ? <NameBoxIconContainer>{icon}</NameBoxIconContainer> : null}
<NameBoxTitle>{title}</NameBoxTitle>
</NameBoxTitleContainer>
<NameBoxContentContainer>{content}</NameBoxContentContainer>
</NameBoxContainer>
);
};
const NameBoxContainer = styled.div`
position: relative;
display: flex;
flex-direction: column;
font-family: "Inter", serif;
font-style: normal;
`;
const NameBoxTitleContainer = styled.div`
position: absolute;
top: -1.9rem;
display: flex;
flex-direction: row;
align-items: center;
`;
const NameBoxIconContainer = styled.div`
display: flex;
align-items: center;
height: 1px;
`;
const NameBoxTitle = styled.div`
font-weight: 700;
font-size: 1rem;
line-height: 1.18rem;
color: ${color.grey["400"]};
`;
const NameBoxContentContainer = styled.div`
display: flex;
align-items: center;
justify-content: center;
padding: 2.125rem 0;
background-color ${color.grey["700"]};
border: 1px solid ${color.grey["300"]};
font-weight: 600;
font-size: 1.5rem;
line-height: 1.81rem;
color: ${color.white}
`;
+21 -2
View File
@@ -6,15 +6,34 @@ import Link from "next/link";
import LogoIcon from "../../public/images/svg/logo.svg";
// Styles
import { LogoContainer } from "./styled";
import styled from "styled-components";
import { FunctionComponent } from "react";
export const Logo: FunctionComponent = () => {
return (
<Link href="/">
<LogoContainer>
<Image src={LogoIcon} fill={true} alt="Home Logo" />
<Image
src={LogoIcon}
fill={true}
sizes="10rem"
alt="Home Logo"
priority
/>
</LogoContainer>
</Link>
);
};
export const LogoContainer = styled.div`
display: flex;
align-items: center;
justify-content: center;
position: absolute;
width: 10rem;
height: 5rem;
margin-top: 5rem;
margin-left: 5rem;
`;
-16
View File
@@ -1,16 +0,0 @@
// Styles
import styled from "styled-components";
export const LogoContainer = styled.div`
display: flex;
align-items: center;
justify-content: center;
position: absolute;
width: 160px;
height: 80px;
margin-top: 80px;
margin-left: 80px;
`;
+112
View File
@@ -0,0 +1,112 @@
import { ButtonHTMLAttributes, FunctionComponent } from "react";
import styled, { keyframes } from "styled-components";
import color from "../../styles/color";
interface PrimaryButtonProps extends ButtonHTMLAttributes<HTMLButtonElement> {
isLoading?: boolean;
}
export const PrimaryButton: FunctionComponent<PrimaryButtonProps> = ({
children,
isLoading,
disabled,
...props
}) => {
return (
<StyledPrimaryButton {...props} disabled={disabled || isLoading}>
{isLoading ? (
<SpinnerWrapper>
<Spinner />
<Spinner />
<Spinner />
<Spinner />
</SpinnerWrapper>
) : (
<span>{children}</span>
)}
</StyledPrimaryButton>
);
};
const StyledPrimaryButton = styled.button`
display: flex;
align-items center;
justify-content: center;
width: 100%;
height: 100%;
border: none;
padding: 11px 30px;
font-family: "Inter", serif;
font-style: normal;
font-weight: 600;
font-size: 1.25rem;
line-height: 1.25rem;
letter-spacing: 0.07em;
text-transform: uppercase;
background-color: ${color.orange["100"]};
cursor: pointer;
&:hover {
transition-duration: 0.5s;
background-color: ${color.orange["200"]};
span {
opacity: 0.5;
}
}
&:disabled {
background-color: ${color.orange["300"]};
cursor: not-allowed;
span {
opacity: 0.5;
}
}
span {
transition-duration: 0.5s;
color: ${color.orange["50"]};
}
`;
const SpinnerWrapper = styled.div`
display: flex;
position: relative;
width: 20px;
height: 20px;
`;
const spinAnimation = keyframes`
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
`;
const Spinner = styled.div<{ animationDelay?: string }>`
display: block;
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
animation: ${spinAnimation} 1.2s cubic-bezier(0.5, 0, 0.5, 1) infinite;
${({ animationDelay }) =>
animationDelay ? `animation-delay: ${animationDelay};` : ""}
border-radius: 100%;
border-style: solid;
border-width: 3px;
border-color: white transparent transparent transparent;
`;
+81
View File
@@ -0,0 +1,81 @@
import { Dispatch, FunctionComponent, SetStateAction } from "react";
import Image from "next/image";
import styled from "styled-components";
import color from "../../styles/color";
import SearchIcon from "../../public/images/svg/search-icon.svg";
interface Props {
searchValue: string;
setSearchValue: Dispatch<SetStateAction<string>>;
}
export const SearchInput: FunctionComponent<Props> = (props) => {
const { searchValue, setSearchValue } = props;
return (
<SearchContainer>
<SearchIconContainer>
<Image src={SearchIcon} fill={true} sizes="1.3rem" alt="search icon" />
</SearchIconContainer>
<SearchText
type="text"
placeholder="search"
value={searchValue}
onChange={(event) => {
setSearchValue(event.target.value);
}}
/>
</SearchContainer>
);
};
const SearchContainer = styled.div`
display: flex;
align-items: center;
gap: 0.625rem;
height: 2.3rem;
padding: 0.6rem 1.4rem;
border-radius: 3rem;
background-color: ${color.grey["700"]};
`;
const SearchIconContainer = styled.div`
position: relative;
width: 1.3rem;
height: 1.3rem;
`;
const SearchText = styled.input`
border: none;
color: ${color.white};
background-color: ${color.grey["700"]};
font-family: "Inter", serif;
font-style: normal;
font-weight: 500;
font-size: 1rem;
line-height: 1.2rem;
width: 5.5rem;
transition: width 0.2s ease-out;
::placeholder,
::-webkit-input-placeholder {
color: ${color.grey["400"]};
}
&:focus {
outline: none;
width: 10rem;
}
`;
@@ -1,22 +1,24 @@
import styled from "styled-components";
import color from "../../styles/color";
export const PrimaryButton = styled.button`
export const SecondaryButton = styled.button`
width: 100%;
height: 100%;
border: none;
background-color: ${color.primary};
padding: 11px 30px;
font-family: "Inter", serif;
font-style: normal;
font-weight: 600;
font-size: 1.5rem;
line-height: 20px;
font-weight: 400;
font-size: 1rem;
line-height: 1.025rem;
letter-spacing: 0.07em;
text-transform: uppercase;
color: ${color.orange};
color: ${color.grey["200"]};
background-color: transparent;
cursor: pointer;
`;
+2
View File
@@ -1,3 +1,5 @@
export * from "./skeleton-animation";
export * from "./skeleton-circle";
export * from "./skeleton-text";
export * from "./skeleton-chain-list";
+3 -3
View File
@@ -9,13 +9,13 @@ export const SkeletonAnimation = styled.div`
${color.grey["400"]},
transparent
),
linear-gradient(${color.grey["500"]}, ${color.grey["500"]}),
linear-gradient(${color.grey["600"]}, ${color.grey["600"]}),
radial-gradient(
38px circle at 19px 19px,
${color.grey["500"]} 50%,
${color.grey["600"]} 50%,
transparent 51%
),
linear-gradient(${color.grey["500"]}, ${color.grey["500"]});
linear-gradient(${color.grey["600"]}, ${color.grey["600"]});
background-repeat: no-repeat;
background-size: 315px 250px, 315px 180px, 100px 100px, 225px 30px;
background-position: -315px 0, 0 0, 0px 190px, 50px 195px;
+117
View File
@@ -0,0 +1,117 @@
import styled from "styled-components";
import color from "../../styles/color";
import { FunctionComponent } from "react";
import { SkeletonAnimation } from "./skeleton-animation";
import { SkeletonCircle } from "./skeleton-circle";
import { SkeletonText } from "./skeleton-text";
import {
ChainListTitleContainer,
ContentContainer,
ButtonContainer,
} from "../../pages/verification";
import {
ProfileContainer,
ProfileContentContainer,
ProfileFollowContainer,
ProfileNameContainer,
ProfileUserNameContainer,
} from "../twitter-profile";
import {
ChainContainer,
ChainInfoContainer,
ChainItemContainer,
} from "../chain-list";
export const SkeletonChainList: FunctionComponent = () => (
<ContentContainer>
<SkeletonContainer>
<ProfileContainer color={color.grey["800"]}>
<SkeletonCircle width="5.5rem" height="5.5rem" />
<ProfileContentContainer>
<ProfileNameContainer>
<SkeletonText width="5rem" height="1.5rem" />
</ProfileNameContainer>
<ProfileUserNameContainer>
<SkeletonText width="5rem" height="1rem" />
</ProfileUserNameContainer>
<ProfileFollowContainer>
<SkeletonText width="8rem" height="1rem" />
<SkeletonText width="8rem" height="1rem" />
</ProfileFollowContainer>
<SkeletonText width="20rem" height="1rem" />
</ProfileContentContainer>
</ProfileContainer>
</SkeletonContainer>
<ChainListTitleContainer>
<SkeletonTitle />
</ChainListTitleContainer>
<ChainContainer color={color.grey["800"]}>
{Array(10)
.fill("")
.map((_, index) => (
<div key={index}>
<ChainItemContainer isLoading={true} isSkeleton={true}>
<SkeletonImageContainer>
<SkeletonCircle width="3rem" height="3rem" />
</SkeletonImageContainer>
<ChainInfoContainer>
<SkeletonText width="4rem" height="1rem" />
<SkeletonText width="12rem" height="1rem" />
</ChainInfoContainer>
</ChainItemContainer>
<SkeletonDivider />
</div>
))}
</ChainContainer>
<SkeletonButtonContainer>
<ButtonContainer>
<SkeletonButton />
</ButtonContainer>
</SkeletonButtonContainer>
</ContentContainer>
);
const SkeletonContainer = styled.div`
width: 100%;
padding-top: 2.4rem;
`;
const SkeletonTitle = styled.div`
width: 8rem;
height: 1.5rem;
background-color: ${color.grey["800"]};
`;
const SkeletonImageContainer = styled.div`
width: 3rem;
height: 3rem;
position: relative;
`;
const SkeletonButtonContainer = styled.div`
margin-top: 1.5rem;
`;
const SkeletonButton = styled.div`
width: 12rem;
height: 4rem;
background-color: ${color.grey["800"]};
`;
const SkeletonDivider = styled(SkeletonAnimation)`
width: 100%;
height: 1px;
background-color: ${color.grey["600"]};
`;

Some files were not shown because too many files have changed in this diff Show More