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: decouple account selector from qr code connector #8093

Merged
merged 12 commits into from
Jan 25, 2024
Merged
Original file line number Diff line number Diff line change
@@ -1,48 +1,19 @@
import React from 'react';
import { View, Text, StyleSheet } from 'react-native';
import { View, Text } from 'react-native';
import Icon from 'react-native-vector-icons/EvilIcons';
import Device from '../../../../util/device';
import { renderFromWei } from '../../../../util/number';
import { useTheme } from '../../../../util/theme';
import { fontStyles } from '../../../../styles/common';
import EthereumAddress from '../../../UI/EthereumAddress';
import { createStyle } from './styles';

interface IAccountDetailsProps {
export interface IAccountDetailsProps {
index: number;
address: string;
balance: string;
ticker: string | undefined;
toBlockExplorer: (address: string) => void;
}

const createStyle = (colors: any) =>
StyleSheet.create({
rowContainer: {
flex: 1,
height: 65,
flexDirection: 'row',
justifyContent: 'space-between',
paddingLeft: Device.isIphoneX() ? 20 : 10,
},
accountDetails: {
justifyContent: 'flex-start',
},
linkIcon: {
height: '100%',
fontSize: 36,
textAlignVertical: 'center',
},
index: {
fontSize: 20,
color: colors.text.default,
...fontStyles.normal,
},
information: {
color: colors.text.alternative,
...fontStyles.normal,
},
});

const AccountDetails = (props: IAccountDetailsProps) => {
const { colors } = useTheme();
const styles = createStyle(colors);
Expand Down
32 changes: 32 additions & 0 deletions app/components/UI/HardwareWallet/AccountDetails/styles.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
/* eslint-disable import/prefer-default-export */
import { StyleSheet } from 'react-native';
import { fontStyles } from '../../../../styles/common';
import Device from '../../../../util/device';

export const createStyle = (colors: any) =>
StyleSheet.create({
rowContainer: {
flex: 1,
height: 65,
flexDirection: 'row',
justifyContent: 'space-between',
paddingLeft: Device.isIphoneX() ? 20 : 10,
},
accountDetails: {
justifyContent: 'flex-start',
},
linkIcon: {
height: '100%',
fontSize: 36,
textAlignVertical: 'center',
},
index: {
fontSize: 20,
color: colors.text.default,
...fontStyles.normal,
},
information: {
color: colors.text.alternative,
...fontStyles.normal,
},
});
42 changes: 42 additions & 0 deletions app/components/UI/HardwareWallet/AccountSelector/hooks.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import { useState, useMemo, useEffect } from 'react';
import Engine from '../../../../core/Engine';

export interface IAccount {
address: string;
}

export interface AccountBalance {
balance: string;
}

export interface AccountBalances {
[p: string]: AccountBalance;
}

export const useAccountsBalance = (accounts: IAccount[]) => {
dawnseeker8 marked this conversation as resolved.
Show resolved Hide resolved
const [trackedAccounts, setTrackedAccounts] = useState<AccountBalances>({});
const AccountTrackerController = useMemo(
() => (Engine.context as any).AccountTrackerController,
[],
);

useEffect(() => {
const unTrackedAccounts: string[] = [];
accounts.forEach((account) => {
if (!trackedAccounts[account.address]) {
unTrackedAccounts.push(account.address);
}
});
if (unTrackedAccounts.length > 0) {
AccountTrackerController.syncBalanceWithAddresses(unTrackedAccounts).then(
(_trackedAccounts: AccountBalances) => {
setTrackedAccounts(
Object.assign({}, trackedAccounts, _trackedAccounts),
);
},
);
}
stanleyyconsensys marked this conversation as resolved.
Show resolved Hide resolved
}, [AccountTrackerController, accounts, trackedAccounts]);

return trackedAccounts;
};
144 changes: 144 additions & 0 deletions app/components/UI/HardwareWallet/AccountSelector/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
import React, { useCallback, useState, useMemo } from 'react';
import { View, Text, FlatList, TouchableOpacity } from 'react-native';
import Icon from 'react-native-vector-icons/FontAwesome';
import CheckBox from '@react-native-community/checkbox';
import { useSelector } from 'react-redux';

import { strings } from '../../../../../locales/i18n';
import { IAccount } from './types';
import useBlockExplorer from '../../../hooks/useBlockExplorer';
import { useAccountsBalance } from './hooks';
import { useTheme } from '../../../../util/theme';
import { createStyle } from './styles';
import AccountDetails from '../AccountDetails';
import StyledButton from '../../../UI/StyledButton';
import { selectProviderConfig } from '../../../../selectors/networkController';

interface ISelectQRAccountsProps {
accounts: IAccount[];
selectedAccounts: string[];
nextPage: () => void;
prevPage: () => void;
toggleAccount: (index: number) => void;
onUnlock: (accountIndex: number[]) => void;
onForget: () => void;
title: string;
}

const AccountSelector = (props: ISelectQRAccountsProps) => {
const {
accounts,
prevPage,
nextPage,
toggleAccount,
selectedAccounts,
onForget,
onUnlock,
title,
} = props;

const { colors } = useTheme();
const styles = createStyle(colors);
const providerConfig = useSelector(selectProviderConfig);
const accountsBalance = useAccountsBalance(accounts);
const { toBlockExplorer } = useBlockExplorer();

const [checkedAccounts, setCheckedAccounts] = useState<Set<number>>(
new Set(),
);

const formattedAccounts: IAccount[] = useMemo(() => {
const selectedAccountsSet = new Set<string>(
selectedAccounts.map((address) => address.toLowerCase()),
);
return accounts.map((account) => {
const checked = checkedAccounts.has(account.index);
const selected = selectedAccountsSet.has(account.address.toLowerCase());
return {
...account,
checked: checked || selected,
exist: selected,
balance: accountsBalance[account.address]?.balance || '0x0',
};
});
}, [accounts, checkedAccounts, selectedAccounts, accountsBalance]);

const onCheckBoxClick = useCallback(
(index: number) => {
setCheckedAccounts((prev: Set<number>) => {
prev.has(index) ? prev.delete(index) : prev.add(index);
return new Set(prev);
});
toggleAccount(index);
},
[toggleAccount],
);

return (
<View style={styles.container}>
<Text style={styles.title}>{title}</Text>
<FlatList
data={formattedAccounts}
keyExtractor={(item) => `address-${item.index}`}
renderItem={({ item }) => (
<View style={[styles.account]}>
<CheckBox
style={[styles.checkBox]}
disabled={item.exist}
value={item.checked}
onValueChange={() => onCheckBoxClick(item.index)}
boxType={'square'}
tintColors={{
true: colors.primary.default,
false: colors.border.default,
}}
onCheckColor={colors.background.default}
onFillColor={colors.primary.default}
onTintColor={colors.primary.default}
/>
<AccountDetails
index={item.index}
address={item.address}
balance={item.balance}
ticker={providerConfig.ticker}
toBlockExplorer={toBlockExplorer}
/>
</View>
)}
/>
<View style={styles.pagination}>
<TouchableOpacity style={styles.paginationItem} onPress={prevPage}>
<Icon name={'chevron-left'} color={colors.primary.default} />
<Text style={styles.paginationText}>
{strings('account_selector.prev')}
</Text>
</TouchableOpacity>
<TouchableOpacity style={styles.paginationItem} onPress={nextPage}>
<Text style={styles.paginationText}>
{strings('account_selector.next')}
</Text>
<Icon name={'chevron-right'} color={colors.primary.default} />
</TouchableOpacity>
</View>
<View style={styles.bottom}>
<StyledButton
type={'confirm'}
onPress={() => onUnlock([...checkedAccounts])}
containerStyle={[styles.button]}
disabled={checkedAccounts.size < 1}
>
{strings('account_selector.unlock')}
</StyledButton>
<StyledButton
type={'transparent-blue'}
onPress={onForget}
containerStyle={[styles.button]}
>
{strings('account_selector.forget')}
</StyledButton>
</View>
</View>
);
};

export default AccountSelector;
59 changes: 59 additions & 0 deletions app/components/UI/HardwareWallet/AccountSelector/styles.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
/* eslint-disable import/prefer-default-export */
import { StyleSheet } from 'react-native';
import { fontStyles } from '../../../../styles/common';
import Device from '../../../../util/device';

export const createStyle = (colors: any) =>
StyleSheet.create({
container: {
flex: 1,
width: '100%',
paddingHorizontal: 32,
},
title: {
marginTop: 40,
fontSize: 24,
marginBottom: 24,
...fontStyles.normal,
color: colors.text.default,
},
account: {
flexDirection: 'row',
paddingHorizontal: 10,
paddingVertical: 5,
},
checkBox: {
backgroundColor: colors.background.default,
},
number: {
...fontStyles.normal,
color: colors.text.default,
},
pagination: {
alignSelf: 'flex-end',
flexDirection: 'row',
alignItems: 'center',
},
paginationText: {
...fontStyles.normal,
fontSize: 18,
color: colors.primary.default,
paddingHorizontal: 10,
},
paginationItem: {
flexDirection: 'row',
alignItems: 'center',
marginRight: 8,
},
bottom: {
alignItems: 'center',
justifyContent: 'space-between',
paddingTop: 70,
paddingBottom: Device.isIphoneX() ? 20 : 10,
},
button: {
width: '100%',
justifyContent: 'flex-end',
paddingTop: 15,
},
});
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,6 @@ export interface IAccount {
address: string;
balance: string;
index: number;
checked: boolean;
exist: boolean;
checked?: boolean;
exist?: boolean;
}
Loading
Loading