1
0
mirror of https://github.com/kremalicious/blog.git synced 2024-12-22 17:23:50 +01:00

refactor, fetch abort

This commit is contained in:
Matthias Kretschmann 2023-10-29 14:36:48 +00:00
parent b9b0093a2e
commit 74f89bcb7e
Signed by: m
GPG Key ID: 606EEEF3C479A91F
11 changed files with 85 additions and 43 deletions

View File

@ -1,6 +1,6 @@
import { type ReactElement } from 'react'
import { useEffect, type ReactElement, useState } from 'react'
import styles from './Conversion.module.css'
import type { GetToken } from '../../api/getTokens'
import type { GetToken } from '../../hooks/useTokens'
export function Conversion({
amount,
@ -9,12 +9,25 @@ export function Conversion({
amount: string
token: GetToken | undefined
}): ReactElement {
const dollar = token?.price?.usd
? (Number(amount) * token?.price?.usd).toFixed(2)
: '0.00'
const euro = token?.price?.eur
? (Number(amount) * token?.price?.eur).toFixed(2)
: '0.00'
const [dollar, setDollar] = useState('0.00')
const [euro, setEuro] = useState('0.00')
useEffect(() => {
if (!token?.price || !amount) {
setDollar('0.00')
setEuro('0.00')
return
}
const dollar = token?.price?.usd
? (Number(amount) * token?.price?.usd).toFixed(2)
: '0.00'
const euro = token?.price?.eur
? (Number(amount) * token?.price?.eur).toFixed(2)
: '0.00'
setDollar(dollar)
setEuro(euro)
}, [token?.price, amount])
return (
<div className={styles.conversion}>

View File

@ -4,7 +4,7 @@ import { Conversion } from '../Conversion'
import styles from './InputGroup.module.css'
import { TokenSelect } from '../Tokens'
import config from '@config/blog.config'
import type { GetToken } from '../../api/getTokens'
import type { GetToken } from '../../hooks/useTokens'
export function InputGroup({
amount,
@ -23,7 +23,10 @@ export function InputGroup({
<>
<div className={styles.inputGroup}>
<div className={styles.token}>
<TokenSelect setTokenSelected={setTokenSelected} />
<TokenSelect
selectedToken={token}
setTokenSelected={setTokenSelected}
/>
</div>
<Input
type="text"

View File

@ -1,7 +1,7 @@
import { parseEther } from 'viem'
import { useContractWrite, usePrepareContractWrite } from 'wagmi'
import siteConfig from '@config/blog.config'
import { abi } from './abi'
import { abiErc20Transfer } from './abiErc20Transfer'
import { useEffect } from 'react'
export function SendErc20({
@ -15,7 +15,7 @@ export function SendErc20({
}) {
const { config } = usePrepareContractWrite({
address: tokenAddress,
abi,
abi: abiErc20Transfer,
functionName: 'transfer',
args: [siteConfig.author.ether, parseEther(amount)]
})

View File

@ -1,4 +1,4 @@
export const abi = [
export const abiErc20Transfer = [
{
constant: false,
inputs: [

View File

@ -3,7 +3,7 @@ import * as Select from '@radix-ui/react-select'
import { formatCurrency } from '@coingecko/cryptoformat'
import './Token.css'
import { Check } from '@images/components/react'
import type { GetToken } from '../../api/getTokens'
import type { GetToken } from '../../hooks/useTokens'
interface SelectItemProps extends HTMLAttributes<HTMLDivElement> {
token: GetToken | undefined

View File

@ -2,17 +2,22 @@ import * as Select from '@radix-ui/react-select'
import './TokenSelect.css'
import { Token } from './Token'
import { ChevronDown, ChevronsDown, ChevronsUp } from '@images/components/react'
import { useTokens } from '../../hooks/useTokens'
import { useTokens } from '../../hooks/useTokens/useTokens'
import { TokenLoading } from './TokenLoading'
import type { GetToken } from '../../api/getTokens'
import { useEffect } from 'react'
import type { GetToken } from '../../hooks/useTokens'
import { useAccount, useNetwork } from 'wagmi'
export function TokenSelect({
selectedToken,
setTokenSelected
}: {
selectedToken: GetToken | undefined
setTokenSelected: React.Dispatch<React.SetStateAction<GetToken>>
}) {
const { data: tokens, isLoading } = useTokens()
const { chain } = useNetwork()
const { address } = useAccount()
const items = tokens?.map((token) => (
<Token key={token.address} token={token} />
@ -24,12 +29,16 @@ export function TokenSelect({
setTokenSelected(token)
}
// set default token data
useEffect(() => handleValueChange('0x0'), [])
// Set default token data to native token
useEffect(() => {
if (!chain?.id || !address || !tokens) return
return tokens ? (
handleValueChange('0x0')
}, [chain?.id, address, tokens])
return (
<Select.Root
defaultValue={tokens?.[0].address}
defaultValue={selectedToken?.address}
onValueChange={(value: `0x${string}`) => handleValueChange(value)}
disabled={!tokens || isLoading}
>
@ -63,5 +72,5 @@ export function TokenSelect({
</Select.Content>
</Select.Portal>
</Select.Root>
) : null
)
}

View File

@ -1,26 +1,15 @@
export type GetToken = {
address: `0x${string}`
balance: number | undefined
chainId: number
name: string | null
symbol: string | null
decimals: number | null
logo: string | null
price: {
usd: number | null
eur: number | null
}
}
import type { GetToken } from './types'
export async function getTokens(
address: `0x${string}`,
chainId: number
chainId: number,
signal?: AbortSignal
): Promise<GetToken[]> {
if (!address || !chainId) return []
// const url = `http://localhost:3000/api/balance?address=${address}&chainId=${chainId}`
const url = `https://web3.kremalicious.com/api/balance?address=${address}&chainId=${chainId}`
const response = await fetch(url)
const response = await fetch(url, { signal })
const json: GetToken[] = await response.json()
if (!json) console.error(response.statusText)

View File

@ -0,0 +1,2 @@
export * from './useTokens'
export * from './types'

View File

@ -0,0 +1,13 @@
export type GetToken = {
address: `0x${string}`
balance: number | undefined
chainId: number
name: string | null
symbol: string | null
decimals: number | null
logo: string | null
price: {
usd: number | null
eur: number | null
}
}

View File

@ -1,6 +1,7 @@
import { useState, useEffect } from 'react'
import { useAccount, useNetwork } from 'wagmi'
import { getTokens, type GetToken } from '../api/getTokens'
import { getTokens } from './getTokens'
import type { GetToken } from './types'
export function useTokens() {
const { address } = useAccount()
@ -11,23 +12,35 @@ export function useTokens() {
const [isError, setIsError] = useState<boolean>()
useEffect(() => {
const abortController = new AbortController()
const { signal } = abortController
async function init() {
if (!address || !chain) return
if (!address || !chain?.id) return
setIsLoading(true)
try {
const tokens = await getTokens(address, chain.id)
const tokens = await getTokens(address, chain.id, signal)
setData(tokens)
setIsLoading(false)
} catch (error) {
} catch (error: any) {
setIsError(true)
setIsLoading(false)
console.error((error as Error).message)
if ((error as Error).name !== 'AbortError') {
console.error((error as Error).message)
}
}
}
init()
}, [address, chain])
return () => {
abortController.abort()
setData(undefined)
setIsLoading(undefined)
setIsError(undefined)
}
}, [address, chain?.id])
return { data, isLoading, isError }
}

View File

@ -6,7 +6,7 @@ import Alert, { getTransactionMessage } from './components/Alert/Alert'
import { InputGroup } from './components/Input'
import styles from './index.module.css'
import { SendNative, SendErc20 } from './components/Send'
import type { GetToken } from './api/getTokens'
import type { GetToken } from './hooks/useTokens'
export default function Web3Donation(): ReactElement {
const { address: account } = useAccount()