diff --git a/.gitignore b/.gitignore index bf01c765d8e..e0c7fe90988 100644 --- a/.gitignore +++ b/.gitignore @@ -52,4 +52,3 @@ coverage /android/app/src/main/assets/InpageBridge.js /android/app/src/main/assets/InpageBridgeWeb3.js /app/core/InpageBridgeWeb3.js -/shim.js diff --git a/.prettierignore b/.prettierignore index c6393d47f6f..4f153261565 100644 --- a/.prettierignore +++ b/.prettierignore @@ -1,8 +1,6 @@ /android/app/src/main/assets/InpageBridge.js /android/app/src/main/assets/InpageBridgeWeb3.js /app/core/InpageBridgeWeb3.js -/shim.js -/shim.js __snapshots__ android coverage diff --git a/android/app/build.gradle b/android/app/build.gradle index e256c0ba175..65c074192d0 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -103,6 +103,7 @@ android { targetSdkVersion rootProject.ext.targetSdkVersion versionCode 1 versionName "1.0" + multiDexEnabled true ndk { abiFilters "armeabi-v7a", "x86" } @@ -137,6 +138,9 @@ android { } dependencies { + compile 'com.android.support:multidex:1.0.1' + compile project(':react-native-aes-crypto') + compile project(':react-native-keychain') compile project(':react-native-os') compile project(':react-native-linear-gradient') compile project(':react-native-randombytes') diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index e3534775eee..b7c4019e355 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -1,9 +1,11 @@ + package="com.metamask" + xmlns:tools="http://schemas.android.com/tools" +> - + getPackages() { return Arrays.asList( new MainReactPackage(), + new RCTAesPackage(), + new KeychainPackage(), new RNOSModule(), new LinearGradientPackage(), new RandomBytesPackage(), diff --git a/android/settings.gradle b/android/settings.gradle index 94b9dfc162b..9d22a971d14 100644 --- a/android/settings.gradle +++ b/android/settings.gradle @@ -1,4 +1,8 @@ rootProject.name = 'MetaMask' +include ':react-native-aes-crypto' +project(':react-native-aes-crypto').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-aes-crypto/android') +include ':react-native-keychain' +project(':react-native-keychain').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-keychain/android') include ':react-native-os' project(':react-native-os').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-os/android') include ':react-native-linear-gradient' diff --git a/app/components/AccountList/__snapshots__/index.test.js.snap b/app/components/AccountList/__snapshots__/index.test.js.snap index 73c09de5980..75974c620d3 100644 --- a/app/components/AccountList/__snapshots__/index.test.js.snap +++ b/app/components/AccountList/__snapshots__/index.test.js.snap @@ -1,154 +1,16 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`Accounts should render correctly 1`] = ` - - - My Accounts - - - - - - - - - - Account 1 - - - 0.017700 - ETH - - - - - - - - - Account 2 - - - 0.4 - ETH - - - - - + selectedAddress="0xe7E125654064EEa56229f273dA586F10DF96B0a1" +/> `; diff --git a/app/components/AccountList/index.js b/app/components/AccountList/index.js index 8716bd3724a..18fbf682ea7 100644 --- a/app/components/AccountList/index.js +++ b/app/components/AccountList/index.js @@ -1,8 +1,12 @@ import React, { Component } from 'react'; import { TouchableOpacity, StyleSheet, Text, View, SafeAreaView } from 'react-native'; -import Icon from 'react-native-vector-icons/Feather'; +import { connect } from 'react-redux'; +import PropTypes from 'prop-types'; +import Icon from 'react-native-vector-icons/FontAwesome'; import { colors, fontStyles } from '../../styles/common'; import Identicon from '../Identicon'; +import Button from '../Button'; +import Engine from '../../core/Engine'; const styles = StyleSheet.create({ wrapper: { @@ -16,6 +20,9 @@ const styles = StyleSheet.create({ color: colors.fontPrimary, ...fontStyles.bold }, + accountsWrapper: { + flex: 1 + }, account: { flexDirection: 'row', marginLeft: 20, @@ -38,52 +45,76 @@ const styles = StyleSheet.create({ selected: { width: 30, marginRight: 15 + }, + footer: { + height: 80, + justifyContent: 'flex-end', + flexDirection: 'row', + alignItems: 'center' + }, + icon: { + height: 50, + width: 10, + backgroundColor: colors.concrete } }); /** - * View contains the list of all the available accounts + * View that contains the list of all the available accounts */ +class AccountList extends Component { + static propTypes = { + /** + * An object containing each identity in the format addres => account + */ + accounts: PropTypes.object + }; -export default class AccountList extends Component { state = { selectedAccountIndex: 0 }; - onAccountPress = newIndex => { - this.setState({ selectedAccountIndex: newIndex }); + onAccountChange = async newIndex => { + const previousIndex = this.state.selectedAccountIndex; + try { + this.setState({ selectedAccountIndex: newIndex }); + await Engine.api.preferences.update({ selectedAddress: Object.keys(this.props.accounts)[newIndex] }); + } catch (e) { + // Restore to the previous index in case anything goes wrong + this.setState({ selectedAccountIndex: previousIndex }); + console.error('error while trying change the selected account', e); // eslint-disable-line + } }; - renderAccounts() { - const accounts = [ - { - label: 'Account 1', - address: '0xe7E125654064EEa56229f273dA586F10DF96B0a1', - balance: '0.017700' - }, - { - label: 'Account 2', - address: '0xf4F6A83117a9D0a9cA3b9684DEDaBc69d56721D8', - balance: '0.4' - } - ]; + addAccount = async () => { + try { + await Engine.api.keyring.addNewAccount(); + this.setState({ selectedAccountIndex: Object.keys(this.props.accounts).length - 1 }); + } catch (e) { + // Restore to the previous index in case anything goes wrong + console.error('error while trying to add a new account', e); // eslint-disable-line + } + }; + + openAccountSettings = () => false; - return accounts.map((account, i) => { + renderAccounts() { + const { accounts } = this.props; + return Object.keys(accounts).map((key, i) => { + const { name, address, balance = 0 } = accounts[key]; const selected = this.state.selectedAccountIndex === i ? : null; - const { address, label, balance } = account; - return ( this.onAccountPress(i)} // eslint-disable-line + onPress={() => this.onAccountChange(i)} // eslint-disable-line > {selected} - {label} + {name} {balance} ETH @@ -96,7 +127,18 @@ export default class AccountList extends Component { My Accounts {this.renderAccounts()} + + + + ); } } + +const mapStateToProps = state => ({ accounts: state.backgroundState.preferences.identities }); +export default connect(mapStateToProps)(AccountList); diff --git a/app/components/AccountList/index.test.js b/app/components/AccountList/index.test.js index 3af8ed5ac3d..764c069e2ca 100644 --- a/app/components/AccountList/index.test.js +++ b/app/components/AccountList/index.test.js @@ -1,10 +1,22 @@ import React from 'react'; import { shallow } from 'enzyme'; -import Accounts from './'; +import AccountList from './'; +import { Provider } from 'react-redux'; +import configureMockStore from 'redux-mock-store'; + +const mockStore = configureMockStore(); +const store = mockStore({}); describe('Accounts', () => { it('should render correctly', () => { - const wrapper = shallow(); + const address = '0xe7E125654064EEa56229f273dA586F10DF96B0a1'; + const accounts = {}; + accounts[address] = { name: 'account 1', address, balance: 0 }; + const wrapper = shallow( + + + + ); expect(wrapper).toMatchSnapshot(); }); }); diff --git a/app/components/AccountOverview/__snapshots__/index.test.js.snap b/app/components/AccountOverview/__snapshots__/index.test.js.snap index 5cb6b8828df..fe1b611201c 100644 --- a/app/components/AccountOverview/__snapshots__/index.test.js.snap +++ b/app/components/AccountOverview/__snapshots__/index.test.js.snap @@ -36,9 +36,7 @@ exports[`AccountOverview should render correctly 1`] = ` "paddingTop": 7, } } - > - Account 1 - + /> component * which shows information about the selected account */ - export default class AccountOverview extends Component { static propTypes = { /** @@ -63,14 +62,14 @@ export default class AccountOverview extends Component { render() { const { - account: { label, balanceFiat, address } + account: { name, balanceFiat = 0, address } } = this.props; return ( - {label} + {name} ${balanceFiat} diff --git a/app/components/App/__snapshots__/index.test.js.snap b/app/components/App/__snapshots__/index.test.js.snap index fe63b98b49d..7a1c620f2d7 100644 --- a/app/components/App/__snapshots__/index.test.js.snap +++ b/app/components/App/__snapshots__/index.test.js.snap @@ -1,126 +1,9 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`App should render correctly 1`] = ` - `; diff --git a/app/components/App/index.js b/app/components/App/index.js index b6d144ebdbf..7a7f39634cb 100644 --- a/app/components/App/index.js +++ b/app/components/App/index.js @@ -1,13 +1,19 @@ +import React, { Component } from 'react'; +import { AppState, AsyncStorage } from 'react-native'; import { createDrawerNavigator } from 'react-navigation'; +import * as Keychain from 'react-native-keychain'; // eslint-disable-line import/no-namespace +import Login from '../Login'; +import CreatePassword from '../CreatePassword'; +import LockScreen from '../LockScreen'; import Main from '../Main'; import AccountList from '../AccountList'; +import Engine from '../../core/Engine'; /** - * Root application component responsible for instantiating + * Navigator object responsible for instantiating * the two top level views: Main and AccountList */ - -export default createDrawerNavigator( +const Nav = createDrawerNavigator( { Main: { screen: Main @@ -17,3 +23,104 @@ export default createDrawerNavigator( contentComponent: AccountList } ); + +/** + * Root application component responsible for rendering + * the first "guest" screens of the app: CreatePassword, Login + * It will also render