chore: custom connect button
This commit is contained in:
parent
8fe1ec0dda
commit
10321e6006
17
README.md
17
README.md
@ -16,7 +16,7 @@ The contracts addresses are also in the [constants.js](/src/constants.js#L17-L23
|
|||||||
|
|
||||||
This project can be deployed as static website. All the data needed comes from smart contracts.
|
This project can be deployed as static website. All the data needed comes from smart contracts.
|
||||||
|
|
||||||
Handling the communication with the contracts can be done with 2 React hooks:
|
Handling the communication with the contracts can be done with a few [React hooks](https://react.dev/reference/react/hooks):
|
||||||
|
|
||||||
### useDomainStatus()
|
### useDomainStatus()
|
||||||
|
|
||||||
@ -49,6 +49,21 @@ const register = useRegister({
|
|||||||
|
|
||||||
`register` is an async function that will invoke Metamask (or any other wallet the user choose) and will be resolved once the user signs the transaction.
|
`register` is an async function that will invoke Metamask (or any other wallet the user choose) and will be resolved once the user signs the transaction.
|
||||||
|
|
||||||
|
Tip: you can connect both hooks:
|
||||||
|
|
||||||
|
```jsx
|
||||||
|
const { data } = useDomainStatus({ label: 'my-sld' });
|
||||||
|
const register = useRegister(data);
|
||||||
|
```
|
||||||
|
|
||||||
|
### usePrimaryName()
|
||||||
|
|
||||||
|
```jsx
|
||||||
|
const { name, avatar } = usePrimaryName();
|
||||||
|
```
|
||||||
|
|
||||||
|
Both can be empty/null if the user hasn't defined a primary name a name yet.
|
||||||
|
|
||||||
## Running locally
|
## Running locally
|
||||||
|
|
||||||
It uses yarn, so...
|
It uses yarn, so...
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { ConnectButton } from '@rainbow-me/rainbowkit';
|
import { ConnectButton } from './components/button/ConnectButton';
|
||||||
import brandLogo from './assets/brand.png';
|
import brandLogo from './assets/brand.png';
|
||||||
import hnsLogo from './assets/hns.id.png';
|
import hnsLogo from './assets/hns.id.png';
|
||||||
import { Search } from './components/search/Search';
|
import { Search } from './components/search/Search';
|
||||||
|
@ -1,5 +1,13 @@
|
|||||||
import avatar from './assets/avatar.svg';
|
import PropTypes from 'prop-types';
|
||||||
|
import defaultAvatar from './assets/avatar.svg';
|
||||||
|
import { usePrimaryName } from './hooks/usePrimaryName';
|
||||||
|
|
||||||
export const CustomAvatar = () => {
|
export const CustomAvatar = ({ size }) => {
|
||||||
return <img src={avatar} className="w-full" />;
|
const { avatar } = usePrimaryName();
|
||||||
|
|
||||||
|
return <img src={avatar || defaultAvatar} width={size} height={size} />;
|
||||||
|
};
|
||||||
|
|
||||||
|
CustomAvatar.propTypes = {
|
||||||
|
size: PropTypes.number,
|
||||||
};
|
};
|
||||||
|
53
src/abi.js
53
src/abi.js
@ -95,4 +95,57 @@ export const abi = [
|
|||||||
name: 'registerWithSignature',
|
name: 'registerWithSignature',
|
||||||
outputs: [],
|
outputs: [],
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
inputs: [
|
||||||
|
{
|
||||||
|
internalType: 'address',
|
||||||
|
name: '_addr',
|
||||||
|
type: 'address',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
internalType: 'uint256',
|
||||||
|
name: '_coinType',
|
||||||
|
type: 'uint256',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
name: 'getName',
|
||||||
|
outputs: [
|
||||||
|
{
|
||||||
|
internalType: 'string',
|
||||||
|
name: '',
|
||||||
|
type: 'string',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
stateMutability: 'view',
|
||||||
|
type: 'function',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
inputs: [
|
||||||
|
{
|
||||||
|
internalType: 'address',
|
||||||
|
name: '_addr',
|
||||||
|
type: 'address',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
internalType: 'string',
|
||||||
|
name: '_key',
|
||||||
|
type: 'string',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
internalType: 'uint256',
|
||||||
|
name: '_coinType',
|
||||||
|
type: 'uint256',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
name: 'getText',
|
||||||
|
outputs: [
|
||||||
|
{
|
||||||
|
internalType: 'string',
|
||||||
|
name: '',
|
||||||
|
type: 'string',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
stateMutability: 'view',
|
||||||
|
type: 'function',
|
||||||
|
},
|
||||||
];
|
];
|
||||||
|
@ -1,13 +1,14 @@
|
|||||||
import cn from 'classnames';
|
import cn from 'classnames';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import icon from '../assets/loading.png';
|
import icon from '../../assets/loading.png';
|
||||||
|
|
||||||
export const Button = ({ children, onClick, loading }) => (
|
export const Button = ({ children, onClick, loading, className }) => (
|
||||||
<button
|
<button
|
||||||
className={cn(
|
className={cn(
|
||||||
'px-3 py-2 rounded-full justify-center flex',
|
'px-3 py-2 rounded-full justify-center flex',
|
||||||
'text-white text-sm font-semibold leading-none w-24 text-center',
|
'text-white text-sm font-semibold leading-none min-w-24 text-center',
|
||||||
loading ? 'bg-blue-400' : 'bg-blue-600'
|
loading ? 'bg-blue-400' : 'bg-blue-600',
|
||||||
|
className
|
||||||
)}
|
)}
|
||||||
onClick={onClick}
|
onClick={onClick}
|
||||||
disabled={loading}
|
disabled={loading}
|
||||||
@ -24,4 +25,5 @@ Button.propTypes = {
|
|||||||
children: PropTypes.node,
|
children: PropTypes.node,
|
||||||
onClick: PropTypes.func,
|
onClick: PropTypes.func,
|
||||||
loading: PropTypes.bool,
|
loading: PropTypes.bool,
|
||||||
|
className: PropTypes.string,
|
||||||
};
|
};
|
66
src/components/button/ConnectButton.jsx
Normal file
66
src/components/button/ConnectButton.jsx
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import { ConnectButton as RainbowBtn } from '@rainbow-me/rainbowkit';
|
||||||
|
import { Button } from './Button';
|
||||||
|
import defaultAvatar from '../../assets/avatar.svg';
|
||||||
|
import { usePrimaryName } from '../../hooks/usePrimaryName';
|
||||||
|
|
||||||
|
const Custom = ({
|
||||||
|
account,
|
||||||
|
chain,
|
||||||
|
openAccountModal,
|
||||||
|
openChainModal,
|
||||||
|
openConnectModal,
|
||||||
|
}) => {
|
||||||
|
const { name, avatar } = usePrimaryName();
|
||||||
|
const connected = account && chain;
|
||||||
|
|
||||||
|
if (!connected) {
|
||||||
|
return <Button onClick={openConnectModal}>Connect Wallet</Button>;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (chain.unsupported) {
|
||||||
|
return (
|
||||||
|
<Button
|
||||||
|
onClick={openChainModal}
|
||||||
|
type="button"
|
||||||
|
className="bg-red-600 whitespace-nowrap"
|
||||||
|
>
|
||||||
|
Wrong network
|
||||||
|
</Button>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<button
|
||||||
|
className="p-1 bg-white rounded-lg shadow justify-start items-center inline-flex gap-1 cursor-pointer hover:scale-105 transition duration-200"
|
||||||
|
onClick={openAccountModal}
|
||||||
|
>
|
||||||
|
<div className="text-sky-950 font-semibold hidden md:block pl-1">
|
||||||
|
{account.displayBalance}
|
||||||
|
</div>
|
||||||
|
<div className="px-1.5 py-1 bg-neutral-100 rounded-lg justify-end items-center gap-1.5 flex">
|
||||||
|
<img
|
||||||
|
alt={name}
|
||||||
|
src={avatar || account.ensAvatar || defaultAvatar}
|
||||||
|
className="w-6 h-6 rounded-full overflow-hidden"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<div className="text-neutral-900 font-semibold">
|
||||||
|
{name || account.ensName || account.displayName}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</button>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
Custom.propTypes = {
|
||||||
|
account: PropTypes.object,
|
||||||
|
chain: PropTypes.object,
|
||||||
|
openAccountModal: PropTypes.func,
|
||||||
|
openChainModal: PropTypes.func,
|
||||||
|
openConnectModal: PropTypes.func,
|
||||||
|
};
|
||||||
|
|
||||||
|
export const ConnectButton = () => {
|
||||||
|
return <RainbowBtn.Custom>{Custom}</RainbowBtn.Custom>;
|
||||||
|
};
|
@ -1,7 +1,7 @@
|
|||||||
import { useQueryClient } from '@tanstack/react-query';
|
import { useQueryClient } from '@tanstack/react-query';
|
||||||
import { toast } from 'react-toastify';
|
import { toast } from 'react-toastify';
|
||||||
import { useRegister } from '../hooks/useRegister';
|
import { useRegister } from '../../hooks/useRegister';
|
||||||
import { domainDetails } from '../types';
|
import { domainDetails } from '../../types';
|
||||||
import { Button } from './Button';
|
import { Button } from './Button';
|
||||||
import { useState } from 'react';
|
import { useState } from 'react';
|
||||||
|
|
@ -1,9 +1,9 @@
|
|||||||
import { ConnectButton } from '@rainbow-me/rainbowkit';
|
import { ConnectButton } from '../button/ConnectButton';
|
||||||
import Skeleton from 'react-loading-skeleton';
|
import Skeleton from 'react-loading-skeleton';
|
||||||
import { useAccount } from 'wagmi';
|
import { useAccount } from 'wagmi';
|
||||||
import { TLD } from '../../constants';
|
import { TLD } from '../../constants';
|
||||||
import { domainDetails } from '../../types';
|
import { domainDetails } from '../../types';
|
||||||
import { RegisterButton } from '../RegisterButton';
|
import { RegisterButton } from '../button/RegisterButton';
|
||||||
|
|
||||||
export const SearchCTA = ({ details }) => {
|
export const SearchCTA = ({ details }) => {
|
||||||
const { address, isConnected } = useAccount();
|
const { address, isConnected } = useAccount();
|
||||||
|
@ -21,3 +21,7 @@ export const STATUS_CONTRACT_ADDR = DEV_MODE
|
|||||||
export const REGISTER_CONTRACT_ADDR = DEV_MODE
|
export const REGISTER_CONTRACT_ADDR = DEV_MODE
|
||||||
? '0x529B2b5B576c27769Ae0aB811F1655012f756C00'
|
? '0x529B2b5B576c27769Ae0aB811F1655012f756C00'
|
||||||
: '0xfda87CC032cD641ac192027353e5B25261dfe6b3';
|
: '0xfda87CC032cD641ac192027353e5B25261dfe6b3';
|
||||||
|
|
||||||
|
export const PRIMARY_NAME_CONTRACT_ADDR = DEV_MODE
|
||||||
|
? '0x342d6524829bedfF5Ce9f56cd56d5baAcf3dbC58'
|
||||||
|
: '0xDDa56f06D80f3D8E3E35159701A63753f39c3BCB';
|
||||||
|
29
src/hooks/usePrimaryName.js
Normal file
29
src/hooks/usePrimaryName.js
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
import { useAccount, useReadContracts } from 'wagmi';
|
||||||
|
import { abi } from '../abi';
|
||||||
|
import { PRIMARY_NAME_CONTRACT_ADDR } from '../constants';
|
||||||
|
|
||||||
|
export const usePrimaryName = () => {
|
||||||
|
const { address } = useAccount();
|
||||||
|
|
||||||
|
const res = useReadContracts({
|
||||||
|
contracts: [
|
||||||
|
{
|
||||||
|
address: PRIMARY_NAME_CONTRACT_ADDR,
|
||||||
|
abi: abi,
|
||||||
|
functionName: 'getName',
|
||||||
|
args: [address, 614n],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
address: PRIMARY_NAME_CONTRACT_ADDR,
|
||||||
|
abi: abi,
|
||||||
|
functionName: 'getText',
|
||||||
|
args: [address, 'avatar', 614n],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
const name = res.data?.[0]?.result;
|
||||||
|
const avatar = res.data?.[1]?.result;
|
||||||
|
|
||||||
|
return { name, avatar, ...res };
|
||||||
|
};
|
Loading…
Reference in New Issue
Block a user