chore: custom connect button

This commit is contained in:
Fernando Falci 2024-04-23 14:55:24 +02:00
parent 8fe1ec0dda
commit 10321e6006
No known key found for this signature in database
GPG Key ID: AB787B833D90361D
10 changed files with 190 additions and 13 deletions

View File

@ -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...

View File

@ -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';

View File

@ -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,
}; };

View File

@ -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',
},
]; ];

View File

@ -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,
}; };

View 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>;
};

View File

@ -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';

View File

@ -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();

View File

@ -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';

View 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 };
};