Skip to content
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
4 changes: 3 additions & 1 deletion .github/workflows/demo-build-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,5 +24,7 @@ jobs:
node-version: ${{ matrix.node-version }}
cache: 'yarn'
- run: yarn install --immutable
- run: yarn workspace demo build
- run: yarn workspace demo-general build
- run: yarn workspace demo-inkv5 build
- run: yarn workspace demo-inkv6 build
- run: yarn workspace demo-subconnect build
64 changes: 0 additions & 64 deletions examples/dapp-general/src/components/AccountSelection.tsx

This file was deleted.

File renamed without changes.
File renamed without changes.
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"name": "dapp-general",
"name": "demo-general",
"private": true,
"version": "0.0.1",
"type": "module",
Expand Down
File renamed without changes.
93 changes: 93 additions & 0 deletions examples/demo-general/src/components/AccountManager.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
import {
Box,
Button,
Flex,
Menu,
MenuButton,
MenuDivider,
MenuItem,
MenuList,
Text,
useDisclosure,
} from '@chakra-ui/react';
import { useMemo } from 'react';
import AccountAvatar from '@/components/shared/AccountAvatar.tsx';
import WalletSelectionModal from '@/components/dialog/WalletSelectionModal.tsx';
import { shortenAddress } from '@/utils/string.ts';
import { formatBalance, useBalances, useTypink } from 'typink';

export default function AccountManager() {
const { accounts, connectedAccount, setConnectedAccount, disconnect, network, connectedWallets } = useTypink();
const addresses = useMemo(() => accounts.map((a) => a.address), [accounts]);
const balances = useBalances(addresses);
const { isOpen, onOpen, onClose } = useDisclosure();

const hasConnectedWallets = connectedWallets.length > 0;

return (
<>
{!hasConnectedWallets ? (
<Button size='md' variant='outline' onClick={onOpen}>
Connect Wallet
</Button>
) : (
<Box>
<Menu autoSelect={false}>
<MenuButton as={Button} variant='outline'>
{connectedAccount ? (
<Flex align='center' gap={4}>
<AccountAvatar account={connectedAccount} size={24} />
<Flex direction='column' align='start'>
<Text fontWeight='semi-bold' fontSize='md'>
{connectedAccount.name}
</Text>
<Text fontSize='xs' fontWeight='400' color='gray.600'>
{shortenAddress(connectedAccount.address)}
</Text>
</Flex>
</Flex>
) : (
<Text fontWeight='semi-bold' fontSize='md'>
Select Account
</Text>
)}
</MenuButton>

<MenuList>
<Box maxHeight='300px' overflowY='auto'>
{accounts.map((one) => (
<MenuItem
backgroundColor={
one.address === connectedAccount?.address && one.source === connectedAccount?.source
? 'gray.200'
: ''
}
gap={3}
key={`${one.address}-${one.source}`}
onClick={() => setConnectedAccount(one)}>
<AccountAvatar account={one} size={32} />
<Flex direction='column' flex='1'>
<Text fontWeight='500'>{one.name}</Text>
<Text fontSize='xs'>Address: {shortenAddress(one.address)}</Text>
<Text fontSize='xs'>Balance: {formatBalance(balances[one.address]?.free, network)}</Text>
</Flex>
</MenuItem>
))}
</Box>

<MenuDivider />
<MenuItem onClick={onOpen} color='blue.500'>
Connect Wallet
</MenuItem>
<MenuItem onClick={() => disconnect()} color='red.500'>
Disconnect
</MenuItem>
</MenuList>
</Menu>
</Box>
)}

<WalletSelectionModal isOpen={isOpen} onClose={onClose} />
</>
);
}
Original file line number Diff line number Diff line change
@@ -1,22 +1,23 @@
import {
Box,
Input,
Menu,
MenuButton,
MenuItem,
MenuList,
Text,
import {
Box,
Input,
Menu,
MenuButton,
MenuItem,
MenuList,
Text,
Flex,
VStack,
RadioGroup,
Radio,
Stack
Stack,
} from '@chakra-ui/react';
import { useState, useMemo } from 'react';
import { ChevronDownIcon } from '@chakra-ui/icons';
import { useTypink, useBalances, formatBalance } from 'typink';
import { shortenAddress } from '@/utils/string.ts';
import { PolkadotApi } from '@dedot/chaintypes';
import AccountAvatar from './shared/AccountAvatar';

interface RecipientSelectorProps {
value: string;
Expand All @@ -33,32 +34,32 @@ const isValidPolkadotAddress = (address: string): boolean => {

type SelectionMode = 'accounts' | 'custom';

export default function RecipientSelector({
value,
onChange,
isDisabled = false,
isInvalid = false
export default function RecipientSelector({
value,
onChange,
isDisabled = false,
isInvalid = false,
}: RecipientSelectorProps) {
const { accounts, connectedAccount, network } = useTypink<PolkadotApi>();

// Get all accounts except the connected one
const availableAccounts = useMemo(() => {
return accounts.filter(acc => acc.address !== connectedAccount?.address);
return accounts.filter((acc) => acc.address !== connectedAccount?.address);
}, [accounts, connectedAccount]);

// Get balances for all available accounts
const accountAddresses = useMemo(() => availableAccounts.map(acc => acc.address), [availableAccounts]);
const accountAddresses = useMemo(() => availableAccounts.map((acc) => acc.address), [availableAccounts]);
const balances = useBalances(accountAddresses);

// Find if current value matches an available account
const selectedAccount = useMemo(() => {
return availableAccounts.find(acc => acc.address === value);
return availableAccounts.find((acc) => acc.address === value);
}, [availableAccounts, value]);

// Initialize mode - default to 'accounts' if available accounts exist, otherwise 'custom'
const [mode, setMode] = useState<SelectionMode>('accounts');

const handleAccountSelect = (account: typeof availableAccounts[0]) => {
const handleAccountSelect = (account: (typeof availableAccounts)[0]) => {
onChange(account.address);
};

Expand Down Expand Up @@ -102,14 +103,17 @@ export default function RecipientSelector({
{mode === 'custom' || availableAccounts.length === 0 ? (
<Box>
<Input
placeholder={availableAccounts.length === 0
? 'Enter recipient address (no other accounts available)'
: 'Enter recipient address (1abc...)'
placeholder={
availableAccounts.length === 0
? 'Enter recipient address (no other accounts available)'
: 'Enter recipient address (1abc...)'
}
value={value}
onChange={(e) => handleCustomInput(e.target.value)}
isDisabled={isDisabled}
isInvalid={isInvalid || (value.length > 0 && !isValidPolkadotAddress(value))}
minH='60px'
py={3}
/>
{value.length > 0 && !isValidPolkadotAddress(value) && (
<Text fontSize='xs' color='red.500' mt={1}>
Expand All @@ -119,27 +123,32 @@ export default function RecipientSelector({
</Box>
) : (
<Menu>
<MenuButton
<MenuButton
as={Box}
cursor='pointer'
p={3}
minH='60px'
display='flex'
alignItems='center'
border='1px solid'
borderColor={isInvalid ? 'red.500' : 'gray.200'}
borderRadius='md'
_hover={{ borderColor: 'gray.300' }}
_disabled={{ opacity: 0.6, cursor: 'not-allowed' }}
aria-disabled={isDisabled}
>
aria-disabled={isDisabled}>
{selectedAccount ? (
<Flex align='center' justify='space-between' width='100%'>
<Box>
<Text fontSize='sm' fontWeight='medium'>
{selectedAccount.name}
</Text>
<Text fontSize='xs' color='gray.600'>
{shortenAddress(selectedAccount.address)}
</Text>
</Box>
<Flex align='center' gap={3}>
<AccountAvatar account={selectedAccount} size={24} />
<Box>
<Text fontSize='sm' fontWeight='medium'>
{selectedAccount.name}
</Text>
<Text fontSize='xs' color='gray.600'>
{shortenAddress(selectedAccount.address)}
</Text>
</Box>
</Flex>
<Flex align='center' gap={2}>
<Text fontSize='xs' color='gray.500'>
{formatBalance(balances[selectedAccount.address]?.free, network)}
Expand All @@ -158,23 +167,25 @@ export default function RecipientSelector({
<MenuList maxH='300px' overflowY='auto'>
{availableAccounts.map((account) => (
<MenuItem
key={account.address}
key={`${account.address}-${account.source}`}
onClick={() => handleAccountSelect(account)}
bg={selectedAccount?.address === account.address ? 'gray.100' : undefined}
isDisabled={isDisabled}
>
<Flex direction='column' width='100%'>
<Flex justify='space-between' align='center' width='100%'>
<Text fontWeight='medium' fontSize='sm'>
{account.name}
</Text>
<Text fontSize='xs' color='gray.500'>
{formatBalance(balances[account.address]?.free, network)}
isDisabled={isDisabled}>
<Flex align='center' gap={3} width='100%'>
<AccountAvatar account={account} size={24} />
<Flex direction='column' flex={1}>
<Flex justify='space-between' align='center' width='100%'>
<Text fontWeight='medium' fontSize='sm'>
{account.name}
</Text>
<Text fontSize='xs' color='gray.500'>
{formatBalance(balances[account.address]?.free, network)}
</Text>
</Flex>
<Text fontSize='xs' color='gray.600'>
{shortenAddress(account.address)}
</Text>
</Flex>
<Text fontSize='xs' color='gray.600'>
{shortenAddress(account.address)}
</Text>
</Flex>
</MenuItem>
))}
Expand All @@ -183,4 +194,4 @@ export default function RecipientSelector({
)}
</VStack>
);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,11 @@ export default function RemarkTransactionExample() {
const {
fee: estimatedFee,
isLoading: feeLoading,
error: feeError
error: feeError,
} = useTxFee({
tx: remarkTx,
args: [debouncedMessage],
enabled: debouncedMessage.trim().length > 0
enabled: debouncedMessage.trim().length > 0,
});

const handleSendRemark = async () => {
Expand Down
Loading
Loading