Unverified Commit 8c2c09ac authored by Hanwen Cheng's avatar Hanwen Cheng Committed by GitHub
Browse files

feat: dynamically bind gensisHash to accountId (#480)

* update network constant

* change test naming

* update with cc3 genesisHash

* decouple genesisHash with identities schema

* fix account display error and migration code

* update test

* fix null errors

* move existed cc2 to unknown network

* fix bugs

* fix navigation actions

* fix bugs

* move migration code before agree to privacy policies

* small fix

* refactor accountDetails

* backward compatble with legacy substrate accounts

* fix network list when create identity

* fix: expected address

* correct migration

* remove logs
parent bd3d9270
Pipeline #70798 failed with stages
in 9 minutes and 35 seconds
......@@ -61,7 +61,7 @@ const testSetUpDefaultPath = async () => {
testIDs.AccountNetworkChooser.chooserScreen
);
await testUnlockPin(pinCode);
await testExist(PathsList.pathCard + '//kusama_CC2//default');
await testExist(PathsList.pathCard + '//kusama//default');
};
describe('Load test', async () => {
......@@ -107,18 +107,18 @@ describe('Load test', async () => {
await testInput(PathDerivation.pathInput, fundingPath);
await testTap(PathDerivation.deriveButton);
await testUnlockPin(pinCode);
await testExist(PathsList.pathCard + `//kusama_CC2${fundingPath}`);
await testExist(PathsList.pathCard + `//kusama${fundingPath}`);
});
it('delete a path', async () => {
await tapBack();
await testTap(AccountNetworkChooser.networkButton + '0');
await testTap(PathsList.pathCard + `//kusama_CC2${fundingPath}`);
await testTap(PathsList.pathCard + `//kusama${fundingPath}`);
await testTap(PathDetail.popupMenuButton);
await testTap(PathDetail.deleteButton);
await element(by.text('Delete')).tap();
await testUnlockPin(pinCode);
await testNotExist(PathsList.pathCard + `//kusama_CC2${fundingPath}`);
await testNotExist(PathsList.pathCard + `//kusama${fundingPath}`);
});
it('should sign the transaction', async () => {
......
......@@ -21,7 +21,7 @@ import { NETWORK_LIST, SubstrateNetworkKeys } from '../src/constants';
export const signingTestIdentityPath = `//${NETWORK_LIST[SubstrateNetworkKeys.KUSAMA].pathID}//default`;
const setRemarkExtrinsicKusama =
'47900000100005301023c36776005aec2f32a34c109dc791a82edef980eec3be80da938ac9bcc68217220170000010c11111165030000fa030000e3777fa922cafbff200cadeaea1a76bd7898ad5b89f7848999058b50e715f636dbb5aefb451e26bd64faf476301f980437d87c0d88dec1a8c7a3eb3cc82e9bbb0ec';
'47900000100005301021e169bcc4cdb062f1c85f971be770b6aea1bd32ac1bf7877aa54ccd73309014a20180000010c11111145030000fe030000b0a8d493285c2df73290dfb7e61f870f17b41801197a149ca93654499ea3dafeaf2df518f7017e0442a1d01b0f175e0fa5d427470014c1c3ab2131e6250072a70ec';
export const createMockSignRequest = () => ({
bounds: {
......
......@@ -18,7 +18,7 @@
import {
deserializeIdentities,
getAvailableNetworkKeys,
getExistedNetworkKeys,
getPathName,
groupPaths,
serializeIdentities
......@@ -29,13 +29,13 @@ import {
UnknownNetworkKeys
} from '../../src/constants';
const accountIdFunding1 = 'address1',
accountIdFunding2 = 'address2',
accountIdSoft = 'address3',
accountIdPolkadot = 'address5',
accountIdStaking = 'address4',
accountIdEthereum = 'address6',
accountIdDefault = 'addressDefault',
const addressFunding1 = 'address1',
addressFunding2 = 'address2',
addressSoft = 'address3',
addressPolkadot = 'address5',
addressStaking = 'address4',
addressEthereum = 'address6',
addressDefault = 'addressDefault',
paths = [
'//kusama//default',
'//kusama//funding/1',
......@@ -46,55 +46,55 @@ const accountIdFunding1 = 'address1',
'1'
],
metaDefault = {
accountId: accountIdDefault,
address: addressDefault,
createdAt: 1571068850409,
name: '',
updatedAt: 1571078850509
},
metaFunding1 = {
accountId: accountIdFunding1,
address: addressFunding1,
createdAt: 1571068850409,
name: 'funding account1',
updatedAt: 1571078850509
},
metaFunding2 = {
accountId: accountIdFunding2,
address: addressFunding2,
createdAt: 1571068850409,
name: '',
updatedAt: 1571078850509
},
metaStaking = {
accountId: accountIdStaking,
address: addressStaking,
createdAt: 1571068850409,
name: '',
updatedAt: 1571078850509
},
metaPolkadot = {
accountId: accountIdPolkadot,
address: addressPolkadot,
createdAt: 1573142786972,
name: 'PolkadotFirst',
updatedAt: 1573142786972
},
metaEthereum = {
accountId: accountIdEthereum,
address: addressEthereum,
createdAt: 1573142786972,
name: 'Eth account',
updatedAt: 1573142786972
},
metaSoftKey = {
accountId: accountIdSoft,
address: addressSoft,
createdAt: 1573142786972,
name: '',
updatedAt: 1573142786972
};
const accountIdsMap = new Map([
[accountIdDefault, paths[0]],
[accountIdFunding1, paths[1]],
[accountIdSoft, paths[2]],
[accountIdFunding2, paths[3]],
[accountIdStaking, paths[4]],
[accountIdPolkadot, paths[5]],
[accountIdEthereum, paths[6]]
const addressesMap = new Map([
[addressDefault, paths[0]],
[addressFunding1, paths[1]],
[addressSoft, paths[2]],
[addressFunding2, paths[3]],
[addressStaking, paths[4]],
[addressPolkadot, paths[5]],
[addressEthereum, paths[6]]
]);
const metaMap = new Map([
[paths[0], metaDefault],
......@@ -107,14 +107,14 @@ const metaMap = new Map([
]);
const testIdentities = [
{
accountIds: accountIdsMap,
addresses: addressesMap,
derivationPassword: '',
encryptedSeed: 'yyyy',
meta: metaMap,
name: 'identity1'
},
{
accountIds: accountIdsMap,
addresses: addressesMap,
derivationPassword: '',
encryptedSeed: 'xxxx',
meta: metaMap,
......@@ -169,7 +169,7 @@ describe('IdentitiesUtils', () => {
});
it('get the correspond networkKeys', () => {
const networkKeys = getAvailableNetworkKeys(testIdentities[0]);
const networkKeys = getExistedNetworkKeys(testIdentities[0]);
expect(networkKeys).toEqual([
EthereumNetworkKeys.FRONTIER,
SubstrateNetworkKeys.KUSAMA,
......
......@@ -30,106 +30,145 @@ import { NETWORK_LIST, NetworkProtocols } from '../constants';
import fontStyles from '../fontStyles';
import TouchableItem from './TouchableItem';
import colors from '../colors';
import { getAddressFromAccountId } from '../util/identitiesUtils';
import { AccountPrefixedTitle } from './AccountPrefixedTitle';
export default class AccountCard extends React.PureComponent {
static propTypes = {
accountId: PropTypes.string,
isAdd: PropTypes.bool,
networkKey: PropTypes.string,
onPress: PropTypes.func,
seedType: PropTypes.string,
style: ViewPropTypes.style,
testID: PropTypes.string,
title: PropTypes.string,
titlePrefix: PropTypes.string
};
static defaultProps = {
onPress: () => {},
title: 'No name'
};
render() {
const {
accountId,
isAdd,
isNetworkCard,
networkKey,
networkColor,
onPress,
seedType,
style,
testID,
titlePrefix
} = this.props;
let { title } = this.props;
title = title.length ? title : AccountCard.defaultProps.title;
const seedTypeDisplay = seedType || '';
const network =
NETWORK_LIST[networkKey] || NETWORK_LIST[NetworkProtocols.UNKNOWN];
const extractAddress = getAddressFromAccountId(accountId);
return (
<TouchableItem
accessibilityComponentType="button"
testID={testID}
disabled={false}
onPress={onPress}
>
<Separator
shadow={true}
style={{
backgroundColor: 'transparent',
height: 0,
marginVertical: 0
}}
import AccountPrefixedTitle from './AccountPrefixedTitle';
const CardSeparator = () => (
<Separator
shadow={true}
style={{
backgroundColor: 'transparent',
height: 0,
marginVertical: 0
}}
/>
);
const NetworkFooter = ({ networkColor, network }) => (
<View
style={[
styles.footer,
{
backgroundColor: networkColor || network.color
}
]}
/>
);
NetworkCard.propTypes = {
isAdd: PropTypes.bool,
networkColor: PropTypes.string,
networkKey: PropTypes.string,
onPress: PropTypes.func.isRequired,
testID: PropTypes.string,
title: PropTypes.string.isRequired
};
export function NetworkCard({
isAdd,
networkColor,
networkKey,
onPress,
testID,
title
}) {
const network =
NETWORK_LIST[networkKey] || NETWORK_LIST[NetworkProtocols.UNKNOWN];
return (
<TouchableItem
accessibilityComponentType="button"
testID={testID}
disabled={false}
onPress={onPress}
>
<CardSeparator />
<View style={styles.content}>
{isAdd ? (
<View style={{ height: 40, width: 40 }}>
<Icon name="add" color={colors.bg_text} size={32} />
</View>
) : (
<AccountIcon
address={''}
protocol={network.protocol}
network={network}
style={styles.icon}
/>
)}
<View style={styles.desc}>
<AccountPrefixedTitle title={title} />
</View>
<NetworkFooter network={network} networkColor={networkColor} />
</View>
</TouchableItem>
);
}
AccountCard.propTypes = {
address: PropTypes.string,
networkKey: PropTypes.string,
onPress: PropTypes.func,
seedType: PropTypes.string,
style: ViewPropTypes.style,
testID: PropTypes.string,
title: PropTypes.string,
titlePrefix: PropTypes.string
};
AccountCard.defaultProps = {
onPress: () => {},
title: 'No name'
};
export default function AccountCard({
address,
networkKey,
networkColor,
onPress,
seedType,
style,
testID,
title,
titlePrefix
}) {
const displayTitle = title.length ? title : AccountCard.defaultProps.title;
const seedTypeDisplay = seedType || '';
const network =
NETWORK_LIST[networkKey] || NETWORK_LIST[NetworkProtocols.UNKNOWN];
return (
<TouchableItem
accessibilityComponentType="button"
testID={testID}
disabled={false}
onPress={onPress}
>
<CardSeparator />
<View style={[styles.content, style]}>
<AccountIcon
address={address}
protocol={network.protocol}
network={network}
style={styles.icon}
/>
<View style={[styles.content, style]}>
{isAdd ? (
<View style={{ height: 40, width: 40 }}>
<Icon name="add" color={colors.bg_text} size={32} />
</View>
) : (
<AccountIcon
address={extractAddress}
protocol={network.protocol}
network={network}
style={styles.icon}
/>
)}
<View style={styles.desc}>
{!isNetworkCard && (
<View>
<Text
style={[fontStyles.t_regular, { color: colors.bg_text_sec }]}
>
{`${network.title}${seedTypeDisplay} `}
</Text>
</View>
)}
<AccountPrefixedTitle title={title} titlePrefix={titlePrefix} />
{accountId !== '' && (
<Address
address={isAdd ? 'new' : extractAddress}
protocol={network.protocol}
/>
)}
<View style={styles.desc}>
<View>
<Text style={[fontStyles.t_regular, { color: colors.bg_text_sec }]}>
{`${network.title}${seedTypeDisplay} `}
</Text>
</View>
<View
style={[
styles.footer,
{
backgroundColor: networkColor || network.color
}
]}
<AccountPrefixedTitle
title={displayTitle}
titlePrefix={titlePrefix}
/>
{address !== '' && (
<Address address={address} protocol={network.protocol} />
)}
</View>
</TouchableItem>
);
}
<NetworkFooter network={network} networkColor={networkColor} />
</View>
</TouchableItem>
);
}
const styles = StyleSheet.create({
......
......@@ -19,8 +19,9 @@
import Identicon from '@polkadot/reactnative-identicon';
import PropTypes from 'prop-types';
import React, { useEffect, useState } from 'react';
import { Image, View } from 'react-native';
import Icon from 'react-native-vector-icons/MaterialIcons';
import { Image, StyleSheet, View } from 'react-native';
import MaterialIcon from 'react-native-vector-icons/MaterialIcons';
import FontAwesome from 'react-native-vector-icons/FontAwesome';
import colors from '../colors';
import { NetworkProtocols } from '../constants';
......@@ -53,15 +54,13 @@ export default function AccountIcon(props) {
if (address === '') {
return (
<View>
<Image
source={network.logo}
style={{
backgroundColor: colors.bg_text_sec,
borderRadius: 40,
height: 40,
width: 40
}}
/>
{network.logo ? (
<Image source={network.logo} style={styles.logo} />
) : (
<View style={styles.logo}>
<FontAwesome name="question" color={colors.bg} size={28} />
</View>
)}
</View>
);
}
......@@ -76,6 +75,17 @@ export default function AccountIcon(props) {
);
} else {
// if there's no protocol or it's unknown we return a warning
return <Icon color={colors.bg_text} name={'error'} size={44} />;
return <MaterialIcon color={colors.bg_text} name={'error'} size={44} />;
}
}
const styles = StyleSheet.create({
logo: {
alignItems: 'center',
backgroundColor: colors.bg_text_sec,
borderRadius: 40,
height: 40,
justifyContent: 'center',
width: 40
}
});
......@@ -28,7 +28,7 @@ AccountPrefixedTitle.propTypes = {
titlePrefix: PropTypes.string
};
export function AccountPrefixedTitle({ titlePrefix, title }) {
export default function AccountPrefixedTitle({ titlePrefix, title }) {
return (
<View style={{ flexDirection: 'row' }}>
{titlePrefix && (
......
......@@ -31,7 +31,7 @@ const CompatibleCard = ({ account, accountsStore, titlePrefix }) =>
) : (
<AccountCard
title={account.name}
accountId={account.address}
address={account.address}
networkKey={account.networkKey || ''}
/>
);
......
......@@ -27,6 +27,10 @@ import Separator from './Separator';
import { withAccountStore } from '../util/HOC';
import { getIdentityName } from '../util/identitiesUtils';
import testIDs from '../../e2e/testIDs';
import {
navigateToLegacyAccountList,
resetNavigationTo
} from '../util/navigationHelpers';
function IdentitiesSwitch({ navigation, accounts }) {
const defaultVisible = navigation.getParam('isSwitchOpen', false);
......@@ -44,12 +48,14 @@ function IdentitiesSwitch({ navigation, accounts }) {
params
) => {
await accounts.selectIdentity(identity);
closeModalAndNavigate(screenName, params);
setVisible(false);
resetNavigationTo(navigation, screenName, params);
};
const onLegacyListClicked = async () => {
await accounts.resetCurrentIdentity();
closeModalAndNavigate('LegacyAccountList');
setVisible(false);
navigateToLegacyAccountList(navigation);
};
const renderIdentityOptions = identity => {
......
......@@ -21,19 +21,18 @@ import PropTypes from 'prop-types';
import { StyleSheet, Text, View } from 'react-native';
import AntIcon from 'react-native-vector-icons/AntDesign';
import {
getAccountIdWithPath,
getAddressFromAccountId,
getAddressWithPath,
getNetworkKeyByPath,
getPathName
} from '../util/identitiesUtils';
import { NETWORK_LIST } from '../constants';
import { NETWORK_LIST, NetworkProtocols } from '../constants';
import Separator from '../components/Separator';
import AccountIcon from './AccountIcon';
import Address from './Address';
import colors from '../colors';
import fontStyles from '../fontStyles';
import TouchableItem from './TouchableItem';
import { AccountPrefixedTitle } from './AccountPrefixedTitle';
import AccountPrefixedTitle from './AccountPrefixedTitle';
PathCard.propTypes = {
identity: PropTypes.object.isRequired,
......@@ -54,11 +53,10 @@ export default function PathCard({
}) {
const isNotEmptyName = name && name !== '';
const pathName = isNotEmptyName ? name : getPathName(path, identity);
const accountId = getAccountIdWithPath(path, identity);
const address = getAddressWithPath(path, identity);
const networkKey = getNetworkKeyByPath(path);
const network = NETWORK_LIST[networkKey];
const extractAddress = getAddressFromAccountId(accountId, network.protocol);
const nonSubstrateCard = (
<View testID={testID}>
......@@ -72,7 +70,7 @@ export default function PathCard({
/>
<View style={styles.content}>
<AccountIcon
address={extractAddress}
address={address}
protocol={network.protocol}
network={network}
style={styles.icon}
......@@ -84,7 +82,7 @@ export default function PathCard({
</Text>
</View>
<AccountPrefixedTitle title={pathName} titlePrefix={titlePrefix} />
<Address address={extractAddress} protocol={network.protocol} />
<Address address={address} protocol={network.protocol} />
</View>
<View
style={[
......@@ -107,7 +105,7 @@ export default function PathCard({
>
<View style={[styles.content, styles.contentDer]}>
<AccountIcon
address={extractAddress}
address={address}
protocol={network.protocol}
network={network}
style={styles.icon}
......@@ -118,13 +116,13 @@ export default function PathCard({
<AntIcon name="user" size={10} color={colors.bg_text_sec} />
<Text style={fontStyles.t_codeS}>{path}</Text>
</View>
{extractAddress !== '' && (
{address !== '' && (
<Text
style={fontStyles.t_codeS}
ellipsizeMode="middle"
numberOfLines={1}
>
{extractAddress}
{address}
</Text>
)}
</View>
......@@ -133,7 +131,8 @@ export default function PathCard({
</View>
);
return network.protocol === 'substrate'
return network.protocol === NetworkProtocols.SUBSTRATE ||
network.protocol === NetworkProtocols.UNKNOWN
? substrateDerivationCard
: nonSubstrateCard;
}
......