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

feat(suite-native) Device switcher with fiat balance #12523

Open
wants to merge 1 commit into
base: develop
Choose a base branch
from
Open
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
2 changes: 2 additions & 0 deletions suite-native/device-manager/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,9 @@
"@suite-common/wallet-core": "workspace:*",
"@suite-native/analytics": "workspace:*",
"@suite-native/atoms": "workspace:*",
"@suite-native/device": "workspace:*",
"@suite-native/feature-flags": "workspace:*",
"@suite-native/formatters": "workspace:*",
"@suite-native/intl": "workspace:*",
"@suite-native/link": "workspace:*",
"@suite-native/navigation": "workspace:*",
Expand Down
63 changes: 43 additions & 20 deletions suite-native/device-manager/src/components/WalletItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,36 +7,52 @@ import { Icon } from '@suite-common/icons';
import { TrezorDevice } from '@suite-common/suite-types';
import { prepareNativeStyle, useNativeStyles } from '@trezor/styles';
import { selectDevice, selectDeviceByState } from '@suite-common/wallet-core';
import { FiatAmountFormatter } from '@suite-native/formatters';
import { selectDeviceTotalFiatBalanceNative } from '@suite-native/device';

type WalletItemProps = {
deviceState: NonNullable<TrezorDevice['state']>;
onPress: () => void;
isSelectable?: boolean;
};

const walletItemStyle = prepareNativeStyle<{ isSelected: boolean }>((utils, { isSelected }) => ({
paddingHorizontal: utils.spacings.medium,
justifyContent: 'space-between',
alignItems: 'center',
height: 60,
gap: 12,
backgroundColor: utils.colors.backgroundSurfaceElevation1,
borderWidth: utils.borders.widths.small,
borderRadius: 12,
borderColor: utils.colors.borderElevation1,
extend: {
condition: isSelected,
style: {
borderWidth: utils.borders.widths.large,
borderColor: utils.colors.borderSecondary,
},
},
}));
type WalletItemStyleProps = { isSelected: boolean; isSelectable: boolean };

const walletItemStyle = prepareNativeStyle<WalletItemStyleProps>(
(utils, { isSelected, isSelectable }) => ({
justifyContent: 'space-between',
alignItems: 'center',
height: 60,
gap: 12,
borderRadius: 12,
borderColor: utils.colors.borderElevation1,
extend: [
{
condition: isSelected,
style: {
borderWidth: utils.borders.widths.large,
borderColor: utils.colors.borderSecondary,
},
},
{
condition: isSelectable,
style: {
paddingHorizontal: utils.spacings.medium,
backgroundColor: utils.colors.backgroundSurfaceElevation1,
borderWidth: utils.borders.widths.small,
},
},
],
}),
);

export const WalletItem = ({ deviceState, onPress, isSelectable = true }: WalletItemProps) => {
const { applyStyle } = useNativeStyles();
const device = useSelector((state: any) => selectDeviceByState(state, deviceState));
const selectedDevice = useSelector(selectDevice);
const fiatBalance = useSelector((state: any) =>
selectDeviceTotalFiatBalanceNative(state, deviceState),
);

if (!device) {
return null;
Expand All @@ -60,7 +76,7 @@ export const WalletItem = ({ deviceState, onPress, isSelectable = true }: Wallet
<Pressable onPress={onPress}>
<HStack
key={device.instance}
style={applyStyle(walletItemStyle, { isSelected: showAsSelected })}
style={applyStyle(walletItemStyle, { isSelected: showAsSelected, isSelectable })}
>
<HStack alignItems="center">
<Icon
Expand All @@ -69,7 +85,14 @@ export const WalletItem = ({ deviceState, onPress, isSelectable = true }: Wallet
/>
<Text variant="callout">{walletNameLabel}</Text>
</HStack>
{isSelectable && <Radio value="" onPress={onPress} isChecked={isSelected} />}
<HStack alignItems="center" spacing={12}>
<FiatAmountFormatter
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Higher numbers does not fit on the screen and Radio is then out of the box. We should handle that. Maybe by using ellipsis for wallet label if it's too long?

value={String(fiatBalance)}
variant="hint"
color="textSubdued"
/>
{isSelectable && <Radio value="" onPress={onPress} isChecked={isSelected} />}
</HStack>
</HStack>
</Pressable>
);
Expand Down
5 changes: 0 additions & 5 deletions suite-native/device-manager/src/components/WalletList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,6 @@ type WalletListProps = {
export const WalletList = ({ onSelectDevice }: WalletListProps) => {
const devices = useSelector(selectDeviceInstances);

// If there are less than 2 device instances(wallets), do not show wallet list
if (devices.length < 2) {
return null;
}

return (
<VStack spacing={12} paddingHorizontal="medium">
{devices.map(device => {
Expand Down
2 changes: 2 additions & 0 deletions suite-native/device-manager/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,9 @@
},
{ "path": "../analytics" },
{ "path": "../atoms" },
{ "path": "../device" },
{ "path": "../feature-flags" },
{ "path": "../formatters" },
{ "path": "../intl" },
{ "path": "../link" },
{ "path": "../navigation" },
Expand Down
6 changes: 6 additions & 0 deletions suite-native/device/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,11 @@
"@reduxjs/toolkit": "1.9.5",
"@sentry/react-native": "5.22.3",
"@suite-common/redux-utils": "workspace:*",
"@suite-common/suite-config": "workspace:*",
"@suite-common/wallet-config": "workspace:*",
"@suite-common/wallet-core": "workspace:*",
"@suite-common/wallet-types": "workspace:*",
"@suite-common/wallet-utils": "workspace:*",
"@suite-native/alerts": "workspace:*",
"@suite-native/analytics": "workspace:*",
"@suite-native/atoms": "workspace:*",
Expand All @@ -31,7 +35,9 @@
"@trezor/connect": "workspace:*",
"@trezor/device-utils": "workspace:*",
"@trezor/styles": "workspace:*",
"bignumber.js": "^9.1.2",
"lottie-react-native": "6.7.2",
"proxy-memoize": "2.0.2",
"react": "18.2.0",
"react-native": "0.74.1",
"react-redux": "8.0.7",
Expand Down
27 changes: 26 additions & 1 deletion suite-native/device/src/selectors.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,23 @@
import { memoizeWithArgs } from 'proxy-memoize';

import {
AccountsRootState,
DeviceRootState,
DiscoveryRootState,
selectDevice,
FiatRatesRootState,
selectAccountsByDeviceState,
selectCurrentFiatRates,
selectDeviceFirmwareVersion,
selectDeviceModel,
selectIsConnectedDeviceUninitialized,
selectIsDeviceConnectedAndAuthorized,
selectIsEmptyDevice,
selectIsUnacquiredDevice,
} from '@suite-common/wallet-core';
import { SettingsSliceRootState, selectFiatCurrencyCode } from '@suite-native/settings';

import { isFirmwareVersionSupported } from './utils';
import { getTotalFiatBalanceNative, isFirmwareVersionSupported } from './utils';

export const selectIsDeviceFirmwareSupported = (state: DeviceRootState) => {
const deviceFwVersion = selectDeviceFirmwareVersion(state);
Expand Down Expand Up @@ -47,3 +53,22 @@ export const selectDeviceError = (

return device?.error;
};

export const selectDeviceTotalFiatBalanceNative = memoizeWithArgs(
(
state: AccountsRootState & FiatRatesRootState & SettingsSliceRootState,
deviceState: string,
) => {
const accounts = deviceState ? selectAccountsByDeviceState(state, deviceState) : [];

const rates = selectCurrentFiatRates(state);
const fiatCurrencyCode = selectFiatCurrencyCode(state);
const fiatBalance = getTotalFiatBalanceNative(accounts, fiatCurrencyCode, rates);

return fiatBalance;
},
{
// reasonably high cache size for a lot of devices and passphrases
size: 20,
},
);
50 changes: 50 additions & 0 deletions suite-native/device/src/utils.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
import { G } from '@mobily/ts-belt';
import * as semver from 'semver';
import BigNumber from 'bignumber.js';

import { FiatCurrencyCode } from '@suite-common/suite-config';
import { NetworkSymbol } from '@suite-common/wallet-config';
import { Account, RatesByKey } from '@suite-common/wallet-types';
import { getFiatRateKey, toFiatCurrency } from '@suite-common/wallet-utils';
import { DeviceModelInternal, VersionArray } from '@trezor/connect';

export const minimalSupportedFirmwareVersion = {
Expand All @@ -25,3 +30,48 @@ export const isFirmwareVersionSupported = (

return semver.satisfies(versionString, `>=${minimalVersionString}`);
};

// TODO: once we start including token balance in the account balance, we should use
// getAccountFiatBalance from wallet-utils and DELETE this function

// only counts native coin balance
export const getAccountFiatBalanceNative = (
account: Account,
localCurrency: string,
rates?: RatesByKey,
) => {
const coinFiatRateKey = getFiatRateKey(
account.symbol as NetworkSymbol,
localCurrency as FiatCurrencyCode,
);
const coinFiatRate = rates?.[coinFiatRateKey];
if (!coinFiatRate?.rate) return null;

let totalBalance = new BigNumber(0);

// account fiat balance
const accountBalance = toFiatCurrency(account.formattedBalance, coinFiatRate.rate, 2);

totalBalance = totalBalance.plus(accountBalance ?? 0);

return totalBalance.toFixed();
};

// TODO: once we start including token balance in the account balance, we should use
// getTotalFiatBalance from wallet-utils and DELETE this function

// only counts native coin balance
// balance of all accounts in the device without tokens
export const getTotalFiatBalanceNative = (
deviceAccounts: Account[],
localCurrency: string,
rates?: RatesByKey,
) => {
let instanceBalance = new BigNumber(0);
deviceAccounts.forEach(a => {
const accountFiatBalance = getAccountFiatBalanceNative(a, localCurrency, rates) ?? '0';
instanceBalance = instanceBalance.plus(accountFiatBalance);
});

return instanceBalance;
};
12 changes: 12 additions & 0 deletions suite-native/device/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,21 @@
{
"path": "../../suite-common/redux-utils"
},
{
"path": "../../suite-common/suite-config"
},
{
"path": "../../suite-common/wallet-config"
},
{
"path": "../../suite-common/wallet-core"
},
{
"path": "../../suite-common/wallet-types"
},
{
"path": "../../suite-common/wallet-utils"
},
{ "path": "../alerts" },
{ "path": "../analytics" },
{ "path": "../atoms" },
Expand Down
8 changes: 8 additions & 0 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -9219,7 +9219,9 @@ __metadata:
"@suite-common/wallet-core": "workspace:*"
"@suite-native/analytics": "workspace:*"
"@suite-native/atoms": "workspace:*"
"@suite-native/device": "workspace:*"
"@suite-native/feature-flags": "workspace:*"
"@suite-native/formatters": "workspace:*"
"@suite-native/intl": "workspace:*"
"@suite-native/link": "workspace:*"
"@suite-native/navigation": "workspace:*"
Expand Down Expand Up @@ -9251,7 +9253,11 @@ __metadata:
"@reduxjs/toolkit": "npm:1.9.5"
"@sentry/react-native": "npm:5.22.3"
"@suite-common/redux-utils": "workspace:*"
"@suite-common/suite-config": "workspace:*"
"@suite-common/wallet-config": "workspace:*"
"@suite-common/wallet-core": "workspace:*"
"@suite-common/wallet-types": "workspace:*"
"@suite-common/wallet-utils": "workspace:*"
"@suite-native/alerts": "workspace:*"
"@suite-native/analytics": "workspace:*"
"@suite-native/atoms": "workspace:*"
Expand All @@ -9266,7 +9272,9 @@ __metadata:
"@trezor/connect": "workspace:*"
"@trezor/device-utils": "workspace:*"
"@trezor/styles": "workspace:*"
bignumber.js: "npm:^9.1.2"
lottie-react-native: "npm:6.7.2"
proxy-memoize: "npm:2.0.2"
react: "npm:18.2.0"
react-native: "npm:0.74.1"
react-redux: "npm:8.0.7"
Expand Down
Loading