Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

use interchain-ui #30

Merged
merged 2 commits into from
Mar 31, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 2 additions & 3 deletions examples/vite-react/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,12 @@
},
"dependencies": {
"@chain-registry/v2": "^1.71.71",
"@chakra-ui/icons": "^2.1.1",
"@chakra-ui/react": "^2.8.2",
"@emotion/react": "^11.11.4",
"@emotion/styled": "^11.11.0",
"@hookform/resolvers": "^3.3.4",
"@interchain-kit/core": "0.2.206",
"@interchain-kit/keplr-extension": "0.2.206",
"@interchain-ui/react": "1.26.1",
"@tanstack/react-query": "5.68.0",
"buffer": "^6.0.3",
"framer-motion": "^11.0.8",
Expand All @@ -40,4 +39,4 @@
"vite": "^5.4.2"
},
"packageManager": "[email protected]"
}
}
84 changes: 37 additions & 47 deletions examples/vite-react/src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,16 +1,5 @@
import { useEffect } from 'react';
import {
Box,
Button,
Container,
VStack,
useColorMode,
IconButton,
Heading,
Card,
CardBody,
} from '@chakra-ui/react';
import { SunIcon, MoonIcon } from '@chakra-ui/icons';
import { Button, Container, Stack, Text, Box } from '@interchain-ui/react';
import { useForm } from 'react-hook-form';
import { zodResolver } from '@hookform/resolvers/zod';
import WalletDetails from './components/WalletDetails';
Expand All @@ -21,7 +10,6 @@ import { useBalance } from './hooks/useBalance';
import { useTransfer } from './hooks/useTransfer';

function App() {
const { colorMode, toggleColorMode } = useColorMode();
const { walletManager, address, connectWallet } = useWalletManager();
const { balance, refetchBalance } = useBalance(address, walletManager);
const transferMutation = useTransfer(address, walletManager, refetchBalance);
Expand All @@ -46,41 +34,43 @@ function App() {
}, [walletManager, connectWallet]);

return (
<Container maxW="container.sm" py={8}>
<VStack spacing={6} align="stretch">
<Box display="flex" justifyContent="flex-end">
<IconButton
aria-label="Toggle color mode"
icon={colorMode === 'light' ? <MoonIcon /> : <SunIcon />}
onClick={toggleColorMode}
/>
<Container
maxWidth='$containerSm'
attributes={{ paddingTop: '$8' }}
>
<Stack
direction='vertical'
space='$6'
align="stretch"
>
<Box>
<Stack
direction='vertical'
space='$6'
align="stretch"
>
<Text as='h1' fontSize='$10xl'>Cosmos Wallet</Text>
{!address ? (
<Button onClick={connectWallet}>Connect Keplr</Button>
) : (
<>
<WalletDetails
address={address}
balance={balance ?? '0'}
onRefresh={refetchBalance}
/>
<TransferForm
register={register}
errors={errors}
handleSubmit={handleSubmit}
onSubmit={onSubmit}
isLoading={transferMutation.isMutating}
/>
</>
)}
</Stack>
</Box>
<Card>
<CardBody>
<VStack spacing={4} align="stretch">
<Heading size="md">Cosmos Wallet</Heading>
{!address ? (
<Button onClick={connectWallet}>Connect Keplr</Button>
) : (
<>
<WalletDetails
address={address}
balance={balance ?? '0'}
onRefresh={refetchBalance}
/>
<TransferForm
register={register}
errors={errors}
handleSubmit={handleSubmit}
onSubmit={onSubmit}
isLoading={transferMutation.isMutating}
/>
</>
)}
</VStack>
</CardBody>
</Card>
</VStack>
</Stack>
</Container>
);
}
Expand Down
31 changes: 17 additions & 14 deletions examples/vite-react/src/components/TransferForm.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { FormControl, FormLabel, Input, Text, VStack, Button } from '@chakra-ui/react';
import { Text, Button, Stack, Box } from '@interchain-ui/react';
import { DENOM_DISPLAY } from '../utils/constants';
import { TransferFormData } from '../utils/validation';

Expand All @@ -13,21 +13,24 @@ interface TransferFormProps {
const TransferForm = ({ register, errors, handleSubmit, onSubmit, isLoading }: TransferFormProps) => {
return (
<form onSubmit={handleSubmit(onSubmit)}>
<VStack spacing={4}>
<FormControl isInvalid={!!errors.recipient}>
<FormLabel>Recipient Address</FormLabel>
<Input {...register('recipient')} />
{errors.recipient && <Text color="red.500">{errors.recipient.message}</Text>}
</FormControl>
<FormControl isInvalid={!!errors.amount}>
<FormLabel>Amount ({DENOM_DISPLAY})</FormLabel>
<Input {...register('amount')} type="number" step="0.000001" />
{errors.amount && <Text color="red.500">{errors.amount.message}</Text>}
</FormControl>
<Button type="submit" colorScheme="blue" isLoading={isLoading} width="100%">
<Stack space='$4' direction='vertical'>
<Box>
<Text>Recipient Address</Text>
<input {...register('recipient')} style={{ padding: '10px', width: '100%' }} />
{errors.recipient && <Text color='$red500'>{errors.recipient.message}</Text>}
</Box>
<Box>
<Text>Amount ({DENOM_DISPLAY})</Text>
<input {...register('amount')} type="number" step="0.000001" style={{ padding: '10px', width: '100%' }} />
{errors.amount && <Text color='$red500'>{errors.amount.message}</Text>}
</Box>
<Button
isLoading={isLoading}
fluidWidth
>
Transfer
</Button>
</VStack>
</Stack>
</form>
);
};
Expand Down
21 changes: 15 additions & 6 deletions examples/vite-react/src/components/WalletDetails.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Box, Text, HStack, IconButton } from '@chakra-ui/react';
import { RepeatIcon } from '@chakra-ui/icons';
import { Box, Text, IconButton } from '@interchain-ui/react';
import { DENOM_DISPLAY } from '../utils/constants';
import { useState } from 'react';

interface WalletDetailsProps {
address: string;
Expand All @@ -9,20 +9,29 @@ interface WalletDetailsProps {
}

const WalletDetails = ({ address, balance, onRefresh }: WalletDetailsProps) => {
const [refreshing, setRefreshing] = useState(false);
return (
<Box>
<Text>Address: {address}</Text>
<HStack>
<div style={{ display: 'flex', alignItems: 'center' }}>
<Text>
Balance: {balance ?? '0'} {DENOM_DISPLAY}
</Text>
<IconButton
aria-label="Refresh balance"
icon={<RepeatIcon />}
size="sm"
onClick={onRefresh}
onClick={() => {
if (refreshing) return;
setRefreshing(true);
setTimeout(() => {
setRefreshing(false);
}, 10000);
onRefresh()
}}
icon='restart'
attributes={{ marginLeft: '$4' }}
/>
</HStack>
</div>
</Box>
);
};
Expand Down
13 changes: 3 additions & 10 deletions examples/vite-react/src/hooks/useBalance.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
import { useQuery } from '@tanstack/react-query';
import { useToast } from '@chakra-ui/react';
import { toast } from '@interchain-ui/react';
import { createGetBalance } from 'interchainjs/cosmos/bank/v1beta1/query.rpc.func';
import { DENOM, DECIMAL, RPC_ENDPOINT } from '../utils/constants';

export const useBalance = (address: string, walletManager: any) => {
const toast = useToast();
const { data, refetch } = useQuery({
queryKey: ['balance', address],
queryFn: async () => {
Expand All @@ -16,15 +15,9 @@ export const useBalance = (address: string, walletManager: any) => {
denom: DENOM,
});
return Number(atomBalance?.amount || 0) / Math.pow(10, DECIMAL);
} catch (error) {
} catch (error: any) {
console.error('Error fetching balance:', error);
toast({
title: 'Error fetching balance',
description: (error as Error).message,
status: 'error',
duration: 5000,
isClosable: true,
});
toast.error(error.message)
return null;
}
},
Expand Down
44 changes: 16 additions & 28 deletions examples/vite-react/src/hooks/useTransfer.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,11 @@
import { useMutation } from '@tanstack/react-query';
import { useToast, Link } from '@chakra-ui/react';
import { Link, toast, Text } from '@interchain-ui/react';
import { createSend } from 'interchainjs/cosmos/bank/v1beta1/tx.rpc.func';
import { DENOM, DECIMAL } from '../utils/constants';
import { keplrWallet } from '@interchain-kit/keplr-extension';
import { chain as cosmoshubChain } from '@chain-registry/v2/mainnet/cosmoshub';
import { chain } from '@chain-registry/v2/mainnet/cosmoshub';

export const useTransfer = (address: string, walletManager: any, refetchBalance: () => void) => {
const toast = useToast();

const transferMutation = useMutation({
mutationFn: async (data: { recipient: string; amount: string }) => {
if (!window.keplr || !address) throw new Error('Keplr not connected');
Expand All @@ -30,41 +28,31 @@ export const useTransfer = (address: string, walletManager: any, refetchBalance:

const signingClient = await walletManager?.getSigningClient(
keplrWallet.info?.name as string,
cosmoshubChain.chainName
chain.chainName
);
const txSend = createSend(signingClient);
const res = await txSend(address, message, fee, '');
// 等待链上确认
await new Promise((resolve) => setTimeout(resolve, 6000));
return (res as any).hash;
},
onSuccess: (txHash) => {
toast({
title: 'Transfer successful',
description: (
<Link
href={`https://www.mintscan.io/cosmos/txs/${txHash}`}
isExternal
color="white"
>
<u>View transaction details</u>
</Link>
),
status: 'success',
duration: null,
isClosable: true,
});
toast.success(
<Link
href={`https://www.mintscan.io/cosmos/txs/${txHash}`}
target='_blank'
>
<u>View transaction details</u>
</Link>
)
refetchBalance();
},
onError: (error: Error) => {
console.error('Error transferring funds:', error);
toast({
title: 'Transfer failed',
description: error.message,
status: 'error',
duration: 5000,
isClosable: true,
});
toast.error(
<Text>
{error.message}
</Text>
)
},
});

Expand Down
23 changes: 5 additions & 18 deletions examples/vite-react/src/hooks/useWalletManager.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,13 @@
import { useState, useEffect, useCallback } from 'react';
import { useToast } from '@chakra-ui/react';
import { WalletManager } from '@interchain-kit/core';
import { keplrWallet } from '@interchain-kit/keplr-extension';
import { chain as cosmoshubChain, assetList as cosmoshubAssetList } from '@chain-registry/v2/mainnet/cosmoshub';
import { RPC_ENDPOINT } from '../utils/constants';
import { toast } from '@interchain-ui/react';

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

useEffect(() => {
(async () => {
Expand All @@ -27,15 +26,9 @@ export const useWalletManager = () => {
}
);
setWalletManager(manager);
} catch (error) {
} catch (error: any) {
console.error('Error initializing wallet manager:', error);
toast({
title: 'Wallet initialization failed',
description: (error as Error).message,
status: 'error',
duration: 5000,
isClosable: true,
});
toast.error(error.message)
}
})();
}, [toast]);
Expand All @@ -51,15 +44,9 @@ export const useWalletManager = () => {
cosmoshubChain.chainName
);
setAddress(account?.address as string);
} catch (error) {
} catch (error: any) {
console.error('Error connecting wallet:', error);
toast({
title: 'Connection failed',
description: (error as Error).message,
status: 'error',
duration: 5000,
isClosable: true,
});
toast.error(error.message)
}
}, [walletManager, toast]);

Expand Down
17 changes: 10 additions & 7 deletions examples/vite-react/src/main.tsx
Original file line number Diff line number Diff line change
@@ -1,19 +1,22 @@
import './styles/globals.css';
import '@interchain-ui/react/styles';
import { ThemeProvider, Toaster } from '@interchain-ui/react';

import { StrictMode } from 'react'
import { createRoot } from 'react-dom/client'
import { ChakraProvider, ColorModeScript } from '@chakra-ui/react'
import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
import App from './App.tsx'
import theme from './theme'


const queryClient = new QueryClient()

createRoot(document.getElementById('root')!).render(
<StrictMode>
<QueryClientProvider client={queryClient}>
<ChakraProvider theme={theme}>
<ColorModeScript initialColorMode={theme.config.initialColorMode} />
<ThemeProvider>
<QueryClientProvider client={queryClient}>
<App />
</ChakraProvider>
</QueryClientProvider>
</QueryClientProvider>
<Toaster position={'top-right'} closeButton={true} />
</ThemeProvider>
</StrictMode>,
)
Loading
Loading