1
0
mirror of https://github.com/kremalicious/blog.git synced 2024-11-26 11:49:04 +01:00

make all the little interactions to work reliably

This commit is contained in:
Matthias Kretschmann 2023-11-01 19:17:24 +00:00
parent e74c1d7cb5
commit 15b4cbd8ac
Signed by: m
GPG Key ID: 606EEEF3C479A91F
15 changed files with 74 additions and 48 deletions

View File

@ -9,8 +9,6 @@ export function Conversion({ amount }: { amount: string }): ReactElement {
const [dollar, setDollar] = useState('0.00') const [dollar, setDollar] = useState('0.00')
const [euro, setEuro] = useState('0.00') const [euro, setEuro] = useState('0.00')
console.log(selectedToken?.price)
useEffect(() => { useEffect(() => {
if (!selectedToken?.price || !amount) { if (!selectedToken?.price || !amount) {
setDollar('0.00') setDollar('0.00')

View File

@ -3,6 +3,8 @@
margin-right: auto; margin-right: auto;
max-width: 25rem; max-width: 25rem;
width: 100%; width: 100%;
text-align: center;
min-height: 165px;
} }
.rainbowkit button > div { .rainbowkit button > div {
@ -37,8 +39,8 @@
} }
.disclaimer { .disclaimer {
font-size: var(--font-size-mini); font-size: var(--font-size-small);
margin-top: calc(var(--spacer) / 1.5); margin-top: calc(var(--spacer) / 3);
margin-bottom: calc(var(--spacer) / 6); margin-bottom: calc(var(--spacer) / 6);
} }

View File

@ -10,6 +10,7 @@ import { useSend } from '../../hooks/useSend'
import type { SendFormData } from './types' import type { SendFormData } from './types'
import { useStore } from '@nanostores/react' import { useStore } from '@nanostores/react'
import { $selectedToken } from '@features/Web3/stores/selectedToken' import { $selectedToken } from '@features/Web3/stores/selectedToken'
import siteConfig from '@config/blog.config'
export default function Web3Form(): ReactElement { export default function Web3Form(): ReactElement {
const { address: account } = useAccount() const { address: account } = useAccount()
@ -38,24 +39,25 @@ export default function Web3Form(): ReactElement {
await send() await send()
}} }}
> >
<div className={styles.rainbowkit}>
<ConnectButton chainStatus="full" showBalance={false} />
</div>
{message && message.status !== 'error' ? ( {message && message.status !== 'error' ? (
<Alert message={message} transactionHash={data?.hash} /> <Alert message={message} transactionHash={data?.hash} />
) : ( ) : (
<>
<div className={styles.rainbowkit}>
<ConnectButton chainStatus="full" showBalance={false} />
</div>
<InputGroup <InputGroup
amount={amount} amount={amount}
setAmount={setAmount} setAmount={setAmount}
isDisabled={isDisabled} isDisabled={isDisabled}
/> />
<div className={styles.disclaimer}>
Sends tokens to my account{' '}
<code>{siteConfig.author.ether.ens}</code>
</div>
</>
)} )}
{message && message?.status === 'error' ? (
<Alert message={message} />
) : null}
{selectedToken?.address === '0x0' ? ( {selectedToken?.address === '0x0' ? (
<SendPrepareNative <SendPrepareNative
amount={debouncedAmount} amount={debouncedAmount}
@ -67,10 +69,6 @@ export default function Web3Form(): ReactElement {
setSendFormData={setSendFormData} setSendFormData={setSendFormData}
/> />
)} )}
<div className={styles.disclaimer}>
Sends tokens to my account, suitable for any ERC-20 token.
</div>
</form> </form>
) )
} }

View File

@ -4,6 +4,7 @@
animation: fadeIn 0.8s ease-out backwards; animation: fadeIn 0.8s ease-out backwards;
margin-top: calc(var(--spacer) / 3); margin-top: calc(var(--spacer) / 3);
display: flex; display: flex;
min-height: 54px;
} }
.token { .token {

View File

@ -27,7 +27,6 @@
margin-right: calc(var(--spacer) / 4); margin-right: calc(var(--spacer) / 4);
border: 1px solid var(--border-color); border: 1px solid var(--border-color);
background: var(--brand-light); background: var(--brand-light);
overflow: hidden;
display: inline-flex; display: inline-flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
@ -36,6 +35,9 @@
.TokenLogo img { .TokenLogo img {
margin: 0; margin: 0;
width: 32px;
height: 32px;
border-radius: 50%;
} }
.TokenName, .TokenName,

View File

@ -4,6 +4,7 @@
--gap: 5deg; --gap: 5deg;
--color: var(--text-color); --color: var(--text-color);
display: block;
width: 100px; width: 100px;
aspect-ratio: 1; aspect-ratio: 1;
border-radius: 50%; border-radius: 50%;

View File

@ -3,15 +3,17 @@ import './TokenSelect.css'
import { Token } from './Token' import { Token } from './Token'
import { ChevronDown, ChevronsDown, ChevronsUp } from '@images/components/react' import { ChevronDown, ChevronsDown, ChevronsUp } from '@images/components/react'
import { TokenLoading } from './TokenLoading' import { TokenLoading } from './TokenLoading'
import { useTokens } from '@features/Web3/hooks/useTokens' import { useFetchTokens } from '@features/Web3/hooks/useFetchTokens'
import { useStore } from '@nanostores/react' import { useStore } from '@nanostores/react'
import { $tokens } from '@features/Web3/stores/tokens'
import { import {
$selectedToken, $selectedToken,
$setSelectedToken $setSelectedToken
} from '@features/Web3/stores/selectedToken' } from '@features/Web3/stores/selectedToken'
export function TokenSelect() { export function TokenSelect() {
const { data: tokens, isLoading } = useTokens() const { isLoading } = useFetchTokens()
const tokens = useStore($tokens)
const selectedToken = useStore($selectedToken) const selectedToken = useStore($selectedToken)
const items = tokens?.map((token) => ( const items = tokens?.map((token) => (
@ -24,16 +26,15 @@ export function TokenSelect() {
$setSelectedToken(token) $setSelectedToken(token)
} }
return ( return tokens ? (
<Select.Root <Select.Root
defaultValue={selectedToken?.address} defaultValue={selectedToken?.address}
onValueChange={(value: `0x${string}`) => handleValueChange(value)} onValueChange={(value: `0x${string}`) => handleValueChange(value)}
disabled={!tokens || isLoading} disabled={isLoading}
value={selectedToken?.address}
> >
<Select.Trigger <Select.Trigger
className="SelectTrigger" className="SelectTrigger"
disabled={!tokens || isLoading} disabled={isLoading}
aria-label="Token" aria-label="Token"
> >
{isLoading ? <TokenLoading /> : <Select.Value />} {isLoading ? <TokenLoading /> : <Select.Value />}
@ -61,5 +62,11 @@ export function TokenSelect() {
</Select.Content> </Select.Content>
</Select.Portal> </Select.Portal>
</Select.Root> </Select.Root>
) ) : isLoading ? (
<>
<div className="Token">
<TokenLoading />
</div>
</>
) : null
} }

View File

@ -0,0 +1 @@
export * from './useFetchTokens'

View File

@ -1,21 +1,22 @@
import { useEffect, useState } from 'react' import { useEffect, useState } from 'react'
import useSWR from 'swr' import useSWR from 'swr'
import type { GetToken } from './types' import type { GetToken } from '@features/Web3/stores/tokens'
import { useNetwork, useAccount } from 'wagmi' import { useNetwork, useAccount } from 'wagmi'
import { fetcher } from '@stores/fetcher' import { fetcher } from '@stores/fetcher'
import { $setTokens } from '@features/Web3/stores/tokens'
import { $setSelectedToken } from '@features/Web3/stores/selectedToken' import { $setSelectedToken } from '@features/Web3/stores/selectedToken'
// //
// Wrapper for fetching user tokens with swr. // Wrapper for fetching user tokens with swr.
// //
export function useTokens() { export function useFetchTokens() {
const { chain } = useNetwork() const { chain } = useNetwork()
const { address } = useAccount() const { address } = useAccount()
const [url, setUrl] = useState<string | undefined>(undefined) const [url, setUrl] = useState<string | undefined>()
const fetchResults = useSWR<GetToken[] | undefined>(url, fetcher) const fetchResults = useSWR<GetToken[] | undefined>(url, fetcher)
const { data: tokens } = fetchResults const { data } = fetchResults
// Set url only after we have all data loaded on client, // Set url only after we have all data loaded on client,
// preventing initial fetch. // preventing initial fetch.
@ -26,13 +27,18 @@ export function useTokens() {
setUrl(url) setUrl(url)
}, [address, chain?.id]) }, [address, chain?.id])
// Sync with $tokens store
useEffect(() => {
if (!data) return
$setTokens(data)
}, [data])
// Set default token data to first item, // Set default token data to first item,
// which most of time is native token // which most of time is native token
useEffect(() => { useEffect(() => {
if (!tokens?.[0]?.chainId) return if (!data?.[0]?.chainId) return
$setSelectedToken(data?.[0])
$setSelectedToken(tokens?.[0]) }, [data?.[0]?.chainId])
}, [tokens?.[0]?.chainId])
return fetchResults return fetchResults
} }

View File

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

View File

@ -1,6 +1,6 @@
import { action } from 'nanostores' import { action } from 'nanostores'
import { persistentAtom } from '@nanostores/persistent' import { persistentAtom } from '@nanostores/persistent'
import type { GetToken } from '../hooks/useTokens' import type { GetToken } from './tokens'
export const $selectedToken = persistentAtom<GetToken | undefined>( export const $selectedToken = persistentAtom<GetToken | undefined>(
'@kremalicious/selectedToken', '@kremalicious/selectedToken',

View File

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

View File

@ -0,0 +1,13 @@
import { action, atom } from 'nanostores'
import type { GetToken } from './types'
export const $tokens = atom<GetToken[] | undefined>()
export const $setTokens = action(
$tokens,
'setTokens',
(store, tokens: GetToken[]) => {
store.set(tokens)
return store.get()
}
)

View File

@ -67,21 +67,20 @@ import CodeCopy from '@components/CopyCode.astro'
} }
</style> </style>
<LayoutBase title="Say Thanks"> <LayoutBase title="Say Thanks" pageTitle="Say Thanks">
<!-- <BackButton /> --> <!-- <BackButton /> -->
<div class="grid"> <div class="grid">
<div class="box"> <div class="box">
<h3 class="subTitle">Send Magic Internet Money</h3> <h3 class="subTitle">Magic Internet Money</h3>
<section class="section"> <section class="section">
If you like what I do and want to support me in a decentralized way, If you like what I do and want to support me in a decentralized way, you
send me some Ether, ERC-20 token, or Bitcoin. can send me some Ether, ERC-20 token, or Bitcoin.
</section> </section>
<section class="section highlight"> <section class="section highlight">
<h4 class="titleCoin"><Wallet /> Web3 Wallet</h4> <h4 class="titleCoin"><Wallet /> Web3 Wallet</h4>
<Web3 client:load /> <Web3 client:load />
<CodeCopy address={config.author.ether.ens} />
</section> </section>
<section class="section"> <section class="section">
@ -92,9 +91,7 @@ import CodeCopy from '@components/CopyCode.astro'
<div> <div>
<h3 class="subTitle">Sponsor</h3> <h3 class="subTitle">Sponsor</h3>
<section class="section"> <section class="section">You can also sponsor me on GitHub.</section>
If you want to support me the old way, you can sponsor me on GitHub.
</section>
<a href="https://github.com/sponsors/kremalicious/"> <a href="https://github.com/sponsors/kremalicious/">
<img <img
src="https://img.shields.io/static/v1?label=Sponsor%20On%20GitHub&labelColor=%2343a699&message=%E2%9D%A4&logo=GitHub&color=%23fe8e86&style=for-the-badge" src="https://img.shields.io/static/v1?label=Sponsor%20On%20GitHub&labelColor=%2343a699&message=%E2%9D%A4&logo=GitHub&color=%23fe8e86&style=for-the-badge"