Skip to content

[Issue-4316] Extension - Improve UI after Bitcoin integration #4384

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

Merged
merged 10 commits into from
May 30, 2025
Merged
Original file line number Diff line number Diff line change
Expand Up @@ -339,6 +339,11 @@ export function _getChainNativeTokenBasicInfo (chainInfo: _ChainInfo): BasicToke
symbol: chainInfo.cardanoInfo.symbol,
decimals: chainInfo.cardanoInfo.decimals
};
} else if (chainInfo.bitcoinInfo) {
return {
symbol: chainInfo.bitcoinInfo.symbol,
decimals: chainInfo.bitcoinInfo.decimals
};
}

return defaultTokenInfo;
Expand Down
5 changes: 0 additions & 5 deletions packages/extension-base/src/types/balance/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,11 +37,6 @@ export interface BalanceItem {
// substrate fields
metadata?: _BalanceMetadata;
}

export interface BalanceItemWithAddressType extends BalanceItem {
addressTypeLabel?: string
}

/** Balance info of all tokens on an address */
export type BalanceInfo = Record<string, BalanceItem>; // Key is tokenSlug
/** Balance info of all addresses */
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

import { NotificationType } from '@subwallet/extension-base/background/KoniTypes';
import { AccountActions, AccountProxy, AccountProxyType } from '@subwallet/extension-base/types';
import { AccountProxyTypeTag, CloseIcon, Layout, PageWrapper } from '@subwallet/extension-koni-ui/components';
import { AccountChainTypeLogos, AccountProxyTypeTag, CloseIcon, Layout, PageWrapper } from '@subwallet/extension-koni-ui/components';
import { FilterTabItemType, FilterTabs } from '@subwallet/extension-koni-ui/components/FilterTabs';
import { WalletModalContext } from '@subwallet/extension-koni-ui/contexts/WalletModalContextProvider';
import { useDefaultNavigate, useGetAccountProxyById, useNotification } from '@subwallet/extension-koni-ui/hooks';
Expand All @@ -14,7 +14,7 @@ import { FormCallbacks, FormFieldData } from '@subwallet/extension-koni-ui/types
import { convertFieldToObject } from '@subwallet/extension-koni-ui/utils/form/form';
import { Button, Form, Icon, Input } from '@subwallet/react-ui';
import CN from 'classnames';
import { CircleNotch, Export, FloppyDiskBack, GitMerge, Trash } from 'phosphor-react';
import { Export, GitMerge, Trash } from 'phosphor-react';
import { RuleObject } from 'rc-field-form/lib/interface';
import React, { useCallback, useContext, useEffect, useMemo, useRef, useState } from 'react';
import { useTranslation } from 'react-i18next';
Expand Down Expand Up @@ -95,7 +95,6 @@ const Component: React.FC<ComponentProps> = ({ accountProxy, onBack, requestView
const [deleting, setDeleting] = useState(false);
// @ts-ignore
const [deriving, setDeriving] = useState(false);
const [saving, setSaving] = useState(false);

const filterTabItems = useMemo<FilterTabItemType[]>(() => {
const result = [
Expand Down Expand Up @@ -211,16 +210,13 @@ const Component: React.FC<ComponentProps> = ({ accountProxy, onBack, requestView

if (changeMap[FormFieldName.NAME]) {
clearTimeout(saveTimeOutRef.current);
setSaving(true);

const isValidForm = form.getFieldsError().every((field) => !field.errors.length);

if (isValidForm) {
saveTimeOutRef.current = setTimeout(() => {
form.submit();
}, 1000);
} else {
setSaving(false);
}
}
}, [form]);
Expand All @@ -230,25 +226,18 @@ const Component: React.FC<ComponentProps> = ({ accountProxy, onBack, requestView
const name = values[FormFieldName.NAME];

if (name === accountProxy.name) {
setSaving(false);

return;
}

const accountProxyId = accountProxy.id;

if (!accountProxyId) {
setSaving(false);

return;
}

editAccount(accountProxyId, name.trim())
.catch((error: Error) => {
form.setFields([{ name: FormFieldName.NAME, errors: [error.message] }]);
})
.finally(() => {
setSaving(false);
});
}, [accountProxy.id, accountProxy.name, form]);

Expand Down Expand Up @@ -427,10 +416,9 @@ const Component: React.FC<ComponentProps> = ({ accountProxy, onBack, requestView
onBlur={form.submit}
placeholder={t('Account name')}
suffix={(
<Icon
className={CN({ loading: saving })}
phosphorIcon={saving ? CircleNotch : FloppyDiskBack}
size='sm'
<AccountChainTypeLogos
chainTypes={accountProxy.chainTypes}
className={'__account-item-chain-type-logos'}
/>
)}
/>
Expand Down Expand Up @@ -526,6 +514,12 @@ const AccountDetail = styled(Wrapper)<Props>(({ theme: { token } }: Props) => {
gap: token.sizeSM
},

'.__account-item-chain-type-logos': {
minHeight: 20,
marginRight: 12,
marginLeft: 12
},

'.account-detail-form, .derivation-info-form': {
paddingTop: token.padding,
paddingLeft: token.padding,
Expand Down
37 changes: 28 additions & 9 deletions packages/extension-koni-ui/src/Popup/Home/Tokens/DetailModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,13 @@

import { APIItemState } from '@subwallet/extension-base/background/KoniTypes';
import { _isChainBitcoinCompatible } from '@subwallet/extension-base/services/chain-service/utils';
import { BalanceItemWithAddressType } from '@subwallet/extension-base/types';
import { AccountTokenBalanceItem, EmptyList, RadioGroup } from '@subwallet/extension-koni-ui/components';
import { useSelector } from '@subwallet/extension-koni-ui/hooks';
import useTranslation from '@subwallet/extension-koni-ui/hooks/common/useTranslation';
import { RootState } from '@subwallet/extension-koni-ui/stores';
import { ThemeProps } from '@subwallet/extension-koni-ui/types';
import { BalanceItemWithAddressType, ThemeProps } from '@subwallet/extension-koni-ui/types';
import { TokenBalanceItemType } from '@subwallet/extension-koni-ui/types/balance';
import { getBitcoinLabelByKeypair, isAccountAll } from '@subwallet/extension-koni-ui/utils';
import { getBitcoinAccountDetails, getBitcoinKeypairAttributes, isAccountAll } from '@subwallet/extension-koni-ui/utils';
import { getKeypairTypeByAddress, isBitcoinAddress } from '@subwallet/keyring';
import { Form, Icon, ModalContext, Number, SwModal } from '@subwallet/react-ui';
import BigN from 'bignumber.js';
Expand Down Expand Up @@ -161,19 +160,39 @@ function Component ({ className = '', currentTokenInfo, id, onCancel, tokenBalan
if (isBitcoinAddress(item.address)) {
const keyPairType = getKeypairTypeByAddress(item.address);

resultItem.addressTypeLabel = getBitcoinLabelByKeypair(keyPairType);
const attributes = getBitcoinKeypairAttributes(keyPairType);

resultItem.addressTypeLabel = attributes.label;
resultItem.schema = attributes.schema;
}

result.push(resultItem);
}

// Sort by total balance in descending order
return result.sort((a, b) => {
const aTotal = new BigN(a.free).plus(BigN(a.locked));
const bTotal = new BigN(b.free).plus(BigN(b.locked));
return result
.sort((a, b) => {
const _isABitcoin = isBitcoinAddress(a.address);
const _isBBitcoin = isBitcoinAddress(b.address);

return bTotal.minus(aTotal).toNumber();
});
if (_isABitcoin && _isBBitcoin) {
const aKeyPairType = getKeypairTypeByAddress(a.address);
const bKeyPairType = getKeypairTypeByAddress(b.address);

const aDetails = getBitcoinAccountDetails(aKeyPairType);
const bDetails = getBitcoinAccountDetails(bKeyPairType);

return aDetails.order - bDetails.order;
}

return 0;
})
.sort((a, b) => {
const aTotal = new BigN(a.free).plus(BigN(a.locked));
const bTotal = new BigN(b.free).plus(BigN(b.locked));

return bTotal.minus(aTotal).toNumber();
});
}, [accounts, balanceMap, currentAccountProxy, currentTokenInfo?.slug, isAllAccount, isBitcoinChain]);

const symbol = currentTokenInfo?.symbol || '';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,12 @@ type Props = ThemeProps & {
onClickQrButton?: VoidFunction;
onClickInfoButton?: VoidFunction;
isShowInfoButton?: boolean;
infoButtonTooltip?: string;
}

function Component (props: Props): React.ReactElement<Props> {
const { className, isShowInfoButton,
const { className, infoButtonTooltip,
isShowInfoButton,
item,
onClick, onClickCopyButton, onClickInfoButton, onClickQrButton } = props;
const _onClickCopyButton: React.MouseEventHandler<HTMLAnchorElement | HTMLButtonElement> = React.useCallback((event) => {
Expand Down Expand Up @@ -84,7 +86,7 @@ function Component (props: Props): React.ReactElement<Props> {
}
onClick={_onClickInfoButton}
size='xs'
tooltip={'This network has two address formats'}
tooltip={infoButtonTooltip}
tooltipPlacement={'topLeft'}
type='ghost'
/>
Expand Down Expand Up @@ -129,7 +131,7 @@ const AccountChainAddressItem = styled(Component)<Props>(({ theme: { token } }:
'white-space': 'nowrap',
gap: token.sizeXXS,
flex: 1,
alignItems: 'flex-end'
alignItems: 'baseline'
},

'.__item-chain-name': {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ const AccountChainTypeLogos = styled(Component)<Props>(({ theme: { token } }: Pr
},

'.__chain-type-logo + .__chain-type-logo': {
marginLeft: -token.marginXXS
marginLeft: -6
}
};
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,10 @@ function Component (props: Props): React.ReactElement<Props> {
<div className={'__derive-account-path'}>
{accountProxy.suri || ''}
</div>
<AccountChainTypeLogos
chainTypes={accountProxy.chainTypes}
className={'__item-chain-type-logos'}
/>
</div>
)
: (
Expand Down Expand Up @@ -343,13 +347,17 @@ const AccountProxySelectorItem = styled(Component)<Props>(({ theme }) => {

'.__item-derived-path': {
display: 'flex',
gap: token.sizeXS - 2,
gap: 4,
alignItems: 'center',

'.__derive-account-path': {
fontSize: token.fontSizeSM,
color: token.colorTextLight4,
lineHeight: token.lineHeightSM
lineHeight: token.lineHeightSM,
maxWidth: 103,
overflow: 'hidden',
'white-space': 'nowrap',
textOverflow: 'ellipsis'
}
},

Expand Down
Loading
Loading