Skip to content

Commit cd12e71

Browse files
authored
Merge pull request #30 from hyperweb-io/vite-react
use interchain-ui and react 19
2 parents 5cd5850 + 206ee0a commit cd12e71

File tree

11 files changed

+208
-143
lines changed

11 files changed

+208
-143
lines changed

examples/vite-react/package.json

+2-3
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,12 @@
1010
},
1111
"dependencies": {
1212
"@chain-registry/v2": "^1.71.71",
13-
"@chakra-ui/icons": "^2.1.1",
14-
"@chakra-ui/react": "^2.8.2",
1513
"@emotion/react": "^11.11.4",
1614
"@emotion/styled": "^11.11.0",
1715
"@hookform/resolvers": "^3.3.4",
1816
"@interchain-kit/core": "0.2.206",
1917
"@interchain-kit/keplr-extension": "0.2.206",
18+
"@interchain-ui/react": "1.26.1",
2019
"@tanstack/react-query": "5.68.0",
2120
"buffer": "^6.0.3",
2221
"framer-motion": "^11.0.8",
@@ -40,4 +39,4 @@
4039
"vite": "^5.4.2"
4140
},
4241
"packageManager": "[email protected]"
43-
}
42+
}

examples/vite-react/src/App.tsx

+37-47
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,5 @@
11
import { useEffect } from 'react';
2-
import {
3-
Box,
4-
Button,
5-
Container,
6-
VStack,
7-
useColorMode,
8-
IconButton,
9-
Heading,
10-
Card,
11-
CardBody,
12-
} from '@chakra-ui/react';
13-
import { SunIcon, MoonIcon } from '@chakra-ui/icons';
2+
import { Button, Container, Stack, Text, Box } from '@interchain-ui/react';
143
import { useForm } from 'react-hook-form';
154
import { zodResolver } from '@hookform/resolvers/zod';
165
import WalletDetails from './components/WalletDetails';
@@ -21,7 +10,6 @@ import { useBalance } from './hooks/useBalance';
2110
import { useTransfer } from './hooks/useTransfer';
2211

2312
function App() {
24-
const { colorMode, toggleColorMode } = useColorMode();
2513
const { walletManager, address, connectWallet } = useWalletManager();
2614
const { balance, refetchBalance } = useBalance(address, walletManager);
2715
const transferMutation = useTransfer(address, walletManager, refetchBalance);
@@ -46,41 +34,43 @@ function App() {
4634
}, [walletManager, connectWallet]);
4735

4836
return (
49-
<Container maxW="container.sm" py={8}>
50-
<VStack spacing={6} align="stretch">
51-
<Box display="flex" justifyContent="flex-end">
52-
<IconButton
53-
aria-label="Toggle color mode"
54-
icon={colorMode === 'light' ? <MoonIcon /> : <SunIcon />}
55-
onClick={toggleColorMode}
56-
/>
37+
<Container
38+
maxWidth='$containerSm'
39+
attributes={{ paddingTop: '$8' }}
40+
>
41+
<Stack
42+
direction='vertical'
43+
space='$6'
44+
align="stretch"
45+
>
46+
<Box>
47+
<Stack
48+
direction='vertical'
49+
space='$6'
50+
align="stretch"
51+
>
52+
<Text as='h1' fontSize='$10xl'>Cosmos Wallet</Text>
53+
{!address ? (
54+
<Button onClick={connectWallet}>Connect Keplr</Button>
55+
) : (
56+
<>
57+
<WalletDetails
58+
address={address}
59+
balance={balance ?? '0'}
60+
onRefresh={refetchBalance}
61+
/>
62+
<TransferForm
63+
register={register}
64+
errors={errors}
65+
handleSubmit={handleSubmit}
66+
onSubmit={onSubmit}
67+
isLoading={transferMutation.isMutating}
68+
/>
69+
</>
70+
)}
71+
</Stack>
5772
</Box>
58-
<Card>
59-
<CardBody>
60-
<VStack spacing={4} align="stretch">
61-
<Heading size="md">Cosmos Wallet</Heading>
62-
{!address ? (
63-
<Button onClick={connectWallet}>Connect Keplr</Button>
64-
) : (
65-
<>
66-
<WalletDetails
67-
address={address}
68-
balance={balance ?? '0'}
69-
onRefresh={refetchBalance}
70-
/>
71-
<TransferForm
72-
register={register}
73-
errors={errors}
74-
handleSubmit={handleSubmit}
75-
onSubmit={onSubmit}
76-
isLoading={transferMutation.isMutating}
77-
/>
78-
</>
79-
)}
80-
</VStack>
81-
</CardBody>
82-
</Card>
83-
</VStack>
73+
</Stack>
8474
</Container>
8575
);
8676
}

examples/vite-react/src/components/TransferForm.tsx

+17-14
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { FormControl, FormLabel, Input, Text, VStack, Button } from '@chakra-ui/react';
1+
import { Text, Button, Stack, Box } from '@interchain-ui/react';
22
import { DENOM_DISPLAY } from '../utils/constants';
33
import { TransferFormData } from '../utils/validation';
44

@@ -13,21 +13,24 @@ interface TransferFormProps {
1313
const TransferForm = ({ register, errors, handleSubmit, onSubmit, isLoading }: TransferFormProps) => {
1414
return (
1515
<form onSubmit={handleSubmit(onSubmit)}>
16-
<VStack spacing={4}>
17-
<FormControl isInvalid={!!errors.recipient}>
18-
<FormLabel>Recipient Address</FormLabel>
19-
<Input {...register('recipient')} />
20-
{errors.recipient && <Text color="red.500">{errors.recipient.message}</Text>}
21-
</FormControl>
22-
<FormControl isInvalid={!!errors.amount}>
23-
<FormLabel>Amount ({DENOM_DISPLAY})</FormLabel>
24-
<Input {...register('amount')} type="number" step="0.000001" />
25-
{errors.amount && <Text color="red.500">{errors.amount.message}</Text>}
26-
</FormControl>
27-
<Button type="submit" colorScheme="blue" isLoading={isLoading} width="100%">
16+
<Stack space='$4' direction='vertical'>
17+
<Box>
18+
<Text>Recipient Address</Text>
19+
<input {...register('recipient')} style={{ padding: '10px', width: '100%' }} />
20+
{errors.recipient && <Text color='$red500'>{errors.recipient.message}</Text>}
21+
</Box>
22+
<Box>
23+
<Text>Amount ({DENOM_DISPLAY})</Text>
24+
<input {...register('amount')} type="number" step="0.000001" style={{ padding: '10px', width: '100%' }} />
25+
{errors.amount && <Text color='$red500'>{errors.amount.message}</Text>}
26+
</Box>
27+
<Button
28+
isLoading={isLoading}
29+
fluidWidth
30+
>
2831
Transfer
2932
</Button>
30-
</VStack>
33+
</Stack>
3134
</form>
3235
);
3336
};

examples/vite-react/src/components/WalletDetails.tsx

+15-6
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
import { Box, Text, HStack, IconButton } from '@chakra-ui/react';
2-
import { RepeatIcon } from '@chakra-ui/icons';
1+
import { Box, Text, IconButton } from '@interchain-ui/react';
32
import { DENOM_DISPLAY } from '../utils/constants';
3+
import { useState } from 'react';
44

55
interface WalletDetailsProps {
66
address: string;
@@ -9,20 +9,29 @@ interface WalletDetailsProps {
99
}
1010

1111
const WalletDetails = ({ address, balance, onRefresh }: WalletDetailsProps) => {
12+
const [refreshing, setRefreshing] = useState(false);
1213
return (
1314
<Box>
1415
<Text>Address: {address}</Text>
15-
<HStack>
16+
<div style={{ display: 'flex', alignItems: 'center' }}>
1617
<Text>
1718
Balance: {balance ?? '0'} {DENOM_DISPLAY}
1819
</Text>
1920
<IconButton
2021
aria-label="Refresh balance"
21-
icon={<RepeatIcon />}
2222
size="sm"
23-
onClick={onRefresh}
23+
onClick={() => {
24+
if (refreshing) return;
25+
setRefreshing(true);
26+
setTimeout(() => {
27+
setRefreshing(false);
28+
}, 10000);
29+
onRefresh()
30+
}}
31+
icon='restart'
32+
attributes={{ marginLeft: '$4' }}
2433
/>
25-
</HStack>
34+
</div>
2635
</Box>
2736
);
2837
};

examples/vite-react/src/hooks/useBalance.ts

+3-10
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,9 @@
11
import { useQuery } from '@tanstack/react-query';
2-
import { useToast } from '@chakra-ui/react';
2+
import { toast } from '@interchain-ui/react';
33
import { createGetBalance } from 'interchainjs/cosmos/bank/v1beta1/query.rpc.func';
44
import { DENOM, DECIMAL, RPC_ENDPOINT } from '../utils/constants';
55

66
export const useBalance = (address: string, walletManager: any) => {
7-
const toast = useToast();
87
const { data, refetch } = useQuery({
98
queryKey: ['balance', address],
109
queryFn: async () => {
@@ -16,15 +15,9 @@ export const useBalance = (address: string, walletManager: any) => {
1615
denom: DENOM,
1716
});
1817
return Number(atomBalance?.amount || 0) / Math.pow(10, DECIMAL);
19-
} catch (error) {
18+
} catch (error: any) {
2019
console.error('Error fetching balance:', error);
21-
toast({
22-
title: 'Error fetching balance',
23-
description: (error as Error).message,
24-
status: 'error',
25-
duration: 5000,
26-
isClosable: true,
27-
});
20+
toast.error(error.message)
2821
return null;
2922
}
3023
},

examples/vite-react/src/hooks/useTransfer.tsx

+16-28
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,11 @@
11
import { useMutation } from '@tanstack/react-query';
2-
import { useToast, Link } from '@chakra-ui/react';
2+
import { Link, toast, Text } from '@interchain-ui/react';
33
import { createSend } from 'interchainjs/cosmos/bank/v1beta1/tx.rpc.func';
44
import { DENOM, DECIMAL } from '../utils/constants';
55
import { keplrWallet } from '@interchain-kit/keplr-extension';
6-
import { chain as cosmoshubChain } from '@chain-registry/v2/mainnet/cosmoshub';
6+
import { chain } from '@chain-registry/v2/mainnet/cosmoshub';
77

88
export const useTransfer = (address: string, walletManager: any, refetchBalance: () => void) => {
9-
const toast = useToast();
10-
119
const transferMutation = useMutation({
1210
mutationFn: async (data: { recipient: string; amount: string }) => {
1311
if (!window.keplr || !address) throw new Error('Keplr not connected');
@@ -30,41 +28,31 @@ export const useTransfer = (address: string, walletManager: any, refetchBalance:
3028

3129
const signingClient = await walletManager?.getSigningClient(
3230
keplrWallet.info?.name as string,
33-
cosmoshubChain.chainName
31+
chain.chainName
3432
);
3533
const txSend = createSend(signingClient);
3634
const res = await txSend(address, message, fee, '');
37-
// 等待链上确认
3835
await new Promise((resolve) => setTimeout(resolve, 6000));
3936
return (res as any).hash;
4037
},
4138
onSuccess: (txHash) => {
42-
toast({
43-
title: 'Transfer successful',
44-
description: (
45-
<Link
46-
href={`https://www.mintscan.io/cosmos/txs/${txHash}`}
47-
isExternal
48-
color="white"
49-
>
50-
<u>View transaction details</u>
51-
</Link>
52-
),
53-
status: 'success',
54-
duration: null,
55-
isClosable: true,
56-
});
39+
toast.success(
40+
<Link
41+
href={`https://www.mintscan.io/cosmos/txs/${txHash}`}
42+
target='_blank'
43+
>
44+
<u>View transaction details</u>
45+
</Link>
46+
)
5747
refetchBalance();
5848
},
5949
onError: (error: Error) => {
6050
console.error('Error transferring funds:', error);
61-
toast({
62-
title: 'Transfer failed',
63-
description: error.message,
64-
status: 'error',
65-
duration: 5000,
66-
isClosable: true,
67-
});
51+
toast.error(
52+
<Text>
53+
{error.message}
54+
</Text>
55+
)
6856
},
6957
});
7058

examples/vite-react/src/hooks/useWalletManager.ts

+5-18
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,13 @@
11
import { useState, useEffect, useCallback } from 'react';
2-
import { useToast } from '@chakra-ui/react';
32
import { WalletManager } from '@interchain-kit/core';
43
import { keplrWallet } from '@interchain-kit/keplr-extension';
54
import { chain as cosmoshubChain, assetList as cosmoshubAssetList } from '@chain-registry/v2/mainnet/cosmoshub';
65
import { RPC_ENDPOINT } from '../utils/constants';
6+
import { toast } from '@interchain-ui/react';
77

88
export const useWalletManager = () => {
99
const [walletManager, setWalletManager] = useState<WalletManager | null>(null);
1010
const [address, setAddress] = useState<string>('');
11-
const toast = useToast();
1211

1312
useEffect(() => {
1413
(async () => {
@@ -27,15 +26,9 @@ export const useWalletManager = () => {
2726
}
2827
);
2928
setWalletManager(manager);
30-
} catch (error) {
29+
} catch (error: any) {
3130
console.error('Error initializing wallet manager:', error);
32-
toast({
33-
title: 'Wallet initialization failed',
34-
description: (error as Error).message,
35-
status: 'error',
36-
duration: 5000,
37-
isClosable: true,
38-
});
31+
toast.error(error.message)
3932
}
4033
})();
4134
}, [toast]);
@@ -51,15 +44,9 @@ export const useWalletManager = () => {
5144
cosmoshubChain.chainName
5245
);
5346
setAddress(account?.address as string);
54-
} catch (error) {
47+
} catch (error: any) {
5548
console.error('Error connecting wallet:', error);
56-
toast({
57-
title: 'Connection failed',
58-
description: (error as Error).message,
59-
status: 'error',
60-
duration: 5000,
61-
isClosable: true,
62-
});
49+
toast.error(error.message)
6350
}
6451
}, [walletManager, toast]);
6552

examples/vite-react/src/main.tsx

+10-7
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,22 @@
1+
import './styles/globals.css';
2+
import '@interchain-ui/react/styles';
3+
import { ThemeProvider, Toaster } from '@interchain-ui/react';
4+
15
import { StrictMode } from 'react'
26
import { createRoot } from 'react-dom/client'
3-
import { ChakraProvider, ColorModeScript } from '@chakra-ui/react'
47
import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
58
import App from './App.tsx'
6-
import theme from './theme'
9+
710

811
const queryClient = new QueryClient()
912

1013
createRoot(document.getElementById('root')!).render(
1114
<StrictMode>
12-
<QueryClientProvider client={queryClient}>
13-
<ChakraProvider theme={theme}>
14-
<ColorModeScript initialColorMode={theme.config.initialColorMode} />
15+
<ThemeProvider>
16+
<QueryClientProvider client={queryClient}>
1517
<App />
16-
</ChakraProvider>
17-
</QueryClientProvider>
18+
</QueryClientProvider>
19+
<Toaster position={'top-right'} closeButton={true} />
20+
</ThemeProvider>
1821
</StrictMode>,
1922
)

0 commit comments

Comments
 (0)