From 28b43059e1e8c36fdf1bc54ffb799adcca1b348a Mon Sep 17 00:00:00 2001 From: Mariano Cortesi Date: Thu, 12 Sep 2019 14:51:55 -0300 Subject: [PATCH 01/61] [faucet] Improve logging (#922) (#945) --- packages/faucet/scripts/test-celo-adapter.ts | 70 ++++++++++++++++++++ packages/faucet/src/celo-adapter.ts | 5 +- packages/faucet/src/database-helper.ts | 1 + packages/faucet/src/debug-provider.ts | 24 +++++++ packages/faucet/src/index.ts | 3 - packages/faucet/src/tx.ts | 44 +++--------- 6 files changed, 107 insertions(+), 40 deletions(-) create mode 100644 packages/faucet/scripts/test-celo-adapter.ts create mode 100644 packages/faucet/src/debug-provider.ts diff --git a/packages/faucet/scripts/test-celo-adapter.ts b/packages/faucet/scripts/test-celo-adapter.ts new file mode 100644 index 00000000000..4aab343a17f --- /dev/null +++ b/packages/faucet/scripts/test-celo-adapter.ts @@ -0,0 +1,70 @@ +import Web3 from 'web3' +import { CeloAdapter } from '../src/celo-adapter' +import { getAddress } from '../src/tx' + +// Obtained from `yarn cli config:get --net alfajores` +const Config = { + stable_token_address: '0xd4b4fcaCAc9e23225680e89308E0a4C41Dd9C6B4', + node_url: 'http://35.185.236.10:8545', + escrow_address: '0xEa1B07eb5E3D3f3b93bf0d0ca7e6E2ba6F566Af4', + gold_token_address: '0x11CD75C45638Ec9f41C0e8Df78fc756201E48ff2', +} + +const FAUCET_ADDR = '0x22579CA45eE22E2E16dDF72D955D6cf4c767B0eF' + +async function main() { + const web3 = new Web3(Config.node_url) + + if (process.argv.length !== 4) { + console.log(`Call with: `) + process.exit(1) + } + + const pk = process.argv[2] + const to = process.argv[3] + + if (getAddress(web3, pk).toLowerCase() !== FAUCET_ADDR.toLowerCase()) { + console.log( + `PrivateKey for invalid address. Expected: ${FAUCET_ADDR}, GOT: ${getAddress(web3, pk)}` + ) + process.exit(1) + } + + // Escrow address is an empty string, because we don't need that contract in this function + const celo = new CeloAdapter( + web3, + pk, + Config.stable_token_address, + Config.escrow_address, + Config.gold_token_address + ) + + const printBalance = async (name: string, addr: string) => { + console.log(`Account ${name}: ${addr}`) + console.log(`USD: ${await celo.getDollarsBalance(addr)}`) + console.log(`Gold: ${await celo.getGoldBalance(addr)}`) + console.log('-------------------------------------------') + } + + await printBalance('Funder', celo.defaultAddress) + await printBalance('Recipient', to) + + const goldAmount = Web3.utils.toWei('1', 'ether') + const dollarAmount = Web3.utils.toWei('1', 'ether') + + const goldTx = await celo.transferGold(to, goldAmount) + console.log('txhash', await goldTx.getHash()) + console.log('receipt', await goldTx.waitReceipt()) + + const dollarTx = await celo.transferDollars(to, dollarAmount) + console.log('txhash', await dollarTx.getHash()) + console.log('receipt', await dollarTx.waitReceipt()) + + await printBalance('Funder', celo.defaultAddress) + await printBalance('Recipient', to) +} + +main().catch((err) => { + console.log(err) + process.exit(1) +}) diff --git a/packages/faucet/src/celo-adapter.ts b/packages/faucet/src/celo-adapter.ts index 99c2e1b2dac..7415890ebe9 100644 --- a/packages/faucet/src/celo-adapter.ts +++ b/packages/faucet/src/celo-adapter.ts @@ -5,6 +5,7 @@ import getStableTokenInstance from './contracts/StableToken' import { Escrow } from './contracts/types/Escrow' import { GoldToken } from './contracts/types/GoldToken' import { StableToken } from './contracts/types/StableToken' +import { injectDebugProvider } from './debug-provider' import { getAddress, sendTx } from './tx' export class CeloAdapter { @@ -21,6 +22,8 @@ export class CeloAdapter { private readonly escrowAddress: string, private readonly goldTokenAddress: string ) { + injectDebugProvider(web3) + this.privateKey = this.web3.utils.isHexStrict(pk) ? pk : '0x' + pk this.defaultAddress = getAddress(this.web3, this.privateKey) this.goldToken = getGoldTokenInstance(this.web3, goldTokenAddress) @@ -76,7 +79,7 @@ export class CeloAdapter { ) } - async getDollarsBalance(accountAddress: string = this.defaultAddress) { + getDollarsBalance(accountAddress: string = this.defaultAddress) { return this.stableToken.methods.balanceOf(accountAddress).call() } getGoldBalance(accountAddress: string = this.defaultAddress) { diff --git a/packages/faucet/src/database-helper.ts b/packages/faucet/src/database-helper.ts index 9e4d45daffc..c477a1e96fc 100644 --- a/packages/faucet/src/database-helper.ts +++ b/packages/faucet/src/database-helper.ts @@ -54,6 +54,7 @@ export async function processRequest(snap: DataSnapshot, pool: AccountPool, conf const success = await pool.doWithAccount(requestHandler) await snap.ref.update({ status: success ? RequestStatus.Done : RequestStatus.Failed }) } catch (err) { + console.error(`req(${snap.key}): ERROR proccessRequest`, err) await snap.ref.update({ status: RequestStatus.Failed }) throw err } diff --git a/packages/faucet/src/debug-provider.ts b/packages/faucet/src/debug-provider.ts new file mode 100644 index 00000000000..f758b141dfe --- /dev/null +++ b/packages/faucet/src/debug-provider.ts @@ -0,0 +1,24 @@ +import Web3 from 'web3' +import { Callback, JsonRPCRequest, JsonRPCResponse, Provider } from 'web3/providers' + +class DebugProvider implements Provider { + constructor(private provider: Provider) {} + + send(payload: JsonRPCRequest, callback: Callback): any { + console.log('rpc: -> %O', payload) + + const callbackDecorator = (error: Error, result: JsonRPCResponse) => { + console.log('rpc: <- %O', payload) + callback(error as any, result) + } + return this.provider.send(payload, callbackDecorator as any) + } +} + +export function wrap(provider: Provider) { + return new DebugProvider(provider) +} + +export function injectDebugProvider(web3: Web3) { + web3.setProvider(wrap(web3.currentProvider)) +} diff --git a/packages/faucet/src/index.ts b/packages/faucet/src/index.ts index 8242d727b5c..c7b3784cd7c 100644 --- a/packages/faucet/src/index.ts +++ b/packages/faucet/src/index.ts @@ -1,11 +1,8 @@ -import debugFactory from 'debug' import * as admin from 'firebase-admin' import * as functions from 'firebase-functions' import { getNetworkConfig } from './config' import { AccountPool, processRequest } from './database-helper' -debugFactory.enabled('*') - const PROCESSOR_RUNTIME_OPTS: functions.RuntimeOptions = { // When changing this, check that actionTimeoutMS is less than this number timeoutSeconds: 120, diff --git a/packages/faucet/src/tx.ts b/packages/faucet/src/tx.ts index b8b0d41bf4e..e203ff9c37a 100644 --- a/packages/faucet/src/tx.ts +++ b/packages/faucet/src/tx.ts @@ -1,15 +1,12 @@ // tslint:disable: max-classes-per-file // TODO: investigate tslint issues -import debugFactory from 'debug' + import Web3 from 'web3' import { TransactionObject, Tx } from 'web3/eth/types' import PromiEvent from 'web3/promiEvent' import { TransactionReceipt } from 'web3/types' import { signTransaction } from './protocol/signing-utils' -const debug = debugFactory('cli:tx') -const debugTxObjects = debugFactory('cli:tx:obj') - export function getAddress(web3: Web3, pk: string) { pk = Web3.utils.isHexStrict(pk) ? pk : '0x' + pk return web3.eth.accounts.privateKeyToAccount(pk).address @@ -21,7 +18,7 @@ export async function sendTx( privateKey: string, txParams?: Tx ) { - debug('sendTx: %o, %o', tx.arguments, txParams) + console.log('tx: tx.data= %o, tx= %o', tx.arguments, txParams) const gasPrice = await web3.eth.getGasPrice() const address = getAddress(web3, privateKey) @@ -29,7 +26,7 @@ export async function sendTx( // Estimate Gas mutates txParams, but we need our original obj later const clonedTxParams = { ...txParams, from: address, gasPrice } const estGas = (Web3.utils.toBN(await tx.estimateGas(clonedTxParams)) as any).muln(10) - debug('estimatedGas: %s', estGas) + console.log('tx: estimatedGas= %s', estGas) const signedTx: any = await signTransaction( web3, @@ -47,32 +44,6 @@ export async function sendTx( return new TransactionResult(web3.eth.sendSignedTransaction(rawTransaction)) } -export async function sendSimpleTx(web3: Web3, privateKey: string, txParams?: Tx) { - debug('sendTx: %o', txParams) - - const address = getAddress(web3, privateKey) - const gasPrice = await web3.eth.getGasPrice() - - // Estimate Gas mutates txParams, but we need our original obj later - const clonedTxParams = { ...txParams, from: address } - const estGas = Web3.utils.toBN(await web3.eth.estimateGas(clonedTxParams)) - debug('estimatedGas: %s', estGas) - - const signedTx: any = await signTransaction( - web3, - { - ...txParams, - from: address, - gasPrice, - gas: estGas, - }, - privateKey - ) - - const rawTransaction = signedTx.rawTransaction.toString('hex') - return new TransactionResult(web3.eth.sendSignedTransaction(rawTransaction)) -} - export class Future { public resolve!: (val: T) => void public reject!: (err: any) => void @@ -96,19 +67,20 @@ export class TransactionResult { constructor(pe: PromiEvent) { pe.on('transactionHash', (hash: string) => { - debug('hash: %s', hash) + console.info('@TransactionResult hash: ', hash) this.hashFuture.resolve(hash) }) .on('receipt', (receipt: TransactionReceipt) => { - debugTxObjects('receipt: %O', receipt) + console.info('@TransactionResult receipt: ', receipt) this.receiptFuture.resolve(receipt) }) .on('error', ((error: any, receipt: TransactionReceipt | false) => { + console.info('@TransactionResult error: %O %O', error, receipt) if (!receipt) { - debug('send-error: %o', error) + console.info('@TransactionResult no receipt') this.hashFuture.reject(error) } else { - debug('mining-error: %o, %O', error, receipt) + console.info('@TransactionResult mining error') } this.receiptFuture.reject(error) }) as any) From 3102acb19312b39c75d92bcda109aae09431bc50 Mon Sep 17 00:00:00 2001 From: Aaron DeRuvo Date: Thu, 12 Sep 2019 14:35:16 -0700 Subject: [PATCH 02/61] Flags should be ON (#947) --- packages/web/flags.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/web/flags.js b/packages/web/flags.js index 996f0136747..ddd345e6429 100644 --- a/packages/web/flags.js +++ b/packages/web/flags.js @@ -5,14 +5,14 @@ const FLAGS = { SDK: true, }, production: { - ECOFUND: false, + ECOFUND: true, ENV: 'production', - SDK: false, + SDK: true, }, staging: { - ECOFUND: false, + ECOFUND: true, ENV: 'staging', - SDK: false, + SDK: true, }, } module.exports = FLAGS[process.env.DEPLOY_ENV] From 26fe65ccd9a3c7161b798d3bd0805b27359c8169 Mon Sep 17 00:00:00 2001 From: Trevor Porter Date: Thu, 12 Sep 2019 16:49:40 -0700 Subject: [PATCH 03/61] Redeploy integration with older version without Fixidity (#951) --- .env.integration | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.env.integration b/.env.integration index 100493a0bba..7c70d7acdc3 100644 --- a/.env.integration +++ b/.env.integration @@ -20,15 +20,15 @@ BLOCKSCOUT_SUBNETWORK_NAME="Integration" GETH_NODE_DOCKER_IMAGE_REPOSITORY="us.gcr.io/celo-testnet/geth" # When upgrading change this to latest commit hash from the master of the geth repo # `geth $ git show | head -n 1` -GETH_NODE_DOCKER_IMAGE_TAG="288e163082235106e5473a075a544af4b3f55fb6" +GETH_NODE_DOCKER_IMAGE_TAG="7a12221e9c7ca5a9f6a7904b0325d4ee275041bc" GETH_BOOTNODE_DOCKER_IMAGE_REPOSITORY="gcr.io/celo-testnet/geth-all" # When upgrading change this to latest commit hash from the master of the geth repo # `geth $ git show | head -n 1` -GETH_BOOTNODE_DOCKER_IMAGE_TAG="288e163082235106e5473a075a544af4b3f55fb6" +GETH_BOOTNODE_DOCKER_IMAGE_TAG="7a12221e9c7ca5a9f6a7904b0325d4ee275041bc" CELOTOOL_DOCKER_IMAGE_REPOSITORY="gcr.io/celo-testnet/celo-monorepo" -CELOTOOL_DOCKER_IMAGE_TAG="celotool-74feb4952ea540279405e61e1243b63852b878c0" +CELOTOOL_DOCKER_IMAGE_TAG="celotool-dfdc3e8b26e98aa294b27e2b5621c184488a10db" TRANSACTION_METRICS_EXPORTER_DOCKER_IMAGE_REPOSITORY="gcr.io/celo-testnet/celo-monorepo" TRANSACTION_METRICS_EXPORTER_DOCKER_IMAGE_TAG="transaction-metrics-exporter-d3d165a7db548d175cd703c86c20c1657c04368d" From e46cbfa1b7dcee106ba6a77950b38b15aee8b1af Mon Sep 17 00:00:00 2001 From: Connor McEwen Date: Fri, 13 Sep 2019 05:12:52 -0500 Subject: [PATCH 04/61] [wallet] Fix crashes due to referrer lib (#917) --- .../android/app/src/main/AndroidManifest.xml | 11 ++--------- .../java/org/celo/mobile/MainApplication.java | 15 +++------------ packages/mobile/package.json | 2 +- yarn.lock | 7 +++---- 4 files changed, 9 insertions(+), 26 deletions(-) diff --git a/packages/mobile/android/app/src/main/AndroidManifest.xml b/packages/mobile/android/app/src/main/AndroidManifest.xml index 8345e97c403..54e3dcbf8bb 100644 --- a/packages/mobile/android/app/src/main/AndroidManifest.xml +++ b/packages/mobile/android/app/src/main/AndroidManifest.xml @@ -1,4 +1,4 @@ - @@ -22,8 +22,7 @@ - + @@ -43,12 +42,6 @@ See README(https://goo.gl/l4GJaQ) for more. --> - - - - - - diff --git a/packages/mobile/android/app/src/main/java/org/celo/mobile/MainApplication.java b/packages/mobile/android/app/src/main/java/org/celo/mobile/MainApplication.java index ab57acb4af5..472c27add20 100644 --- a/packages/mobile/android/app/src/main/java/org/celo/mobile/MainApplication.java +++ b/packages/mobile/android/app/src/main/java/org/celo/mobile/MainApplication.java @@ -1,8 +1,7 @@ package org.celo.mobile; import android.content.Context; -import android.support.multidex.MultiDex; -import android.support.multidex.MultiDexApplication; +import android.app.Application; import org.reactnative.camera.RNCameraPackage; import com.chirag.RNMail.RNMail; @@ -50,7 +49,7 @@ import me.furtado.smsretriever.RNSmsRetrieverPackage; import cl.json.RNSharePackage; import cl.json.ShareApplication; -import com.reactlibrary.RNInstallReferrerPackage; +import com.rninstallreferrer.RNInstallReferrerPackage; import com.reactlibrary.securekeystore.RNSecureKeyStorePackage; import android.util.Log; @@ -68,7 +67,7 @@ // import com.instabug.library.invocation.InstabugInvocationEvent; // import com.instabug.reactlibrary.RNInstabugReactnativePackage; -public class MainApplication extends MultiDexApplication implements ShareApplication, ReactApplication { +public class MainApplication extends Application implements ShareApplication, ReactApplication { private final ReactNativeHost mReactNativeHost = new ReactNativeHost(this) { @Override @@ -164,14 +163,6 @@ public void onCreate() { initNdkCrashHandler(); } - @Override - protected void attachBaseContext(Context base) { - super.attachBaseContext(base); - if (BuildConfig.DEBUG) { - MultiDex.install(this); - } - } - @Override public String getFileProviderAuthority() { return BuildConfig.APPLICATION_ID + ".provider"; diff --git a/packages/mobile/package.json b/packages/mobile/package.json index c88be0a14e8..fd39c536f7e 100644 --- a/packages/mobile/package.json +++ b/packages/mobile/package.json @@ -95,7 +95,7 @@ "react-native-fs": "^2.12.1", "react-native-gesture-handler": "^1.1.0", "react-native-geth": "git+https://github.com/celo-org/react-native-geth#51dc4f1", - "react-native-install-referrer": "^1.0.1", + "react-native-install-referrer": "git://github.com/celo-org/react-native-install-referrer#343bf3d", "react-native-keep-awake": "^3.0.1", "react-native-keyboard-aware-scroll-view": "^0.6.0", "react-native-languages": "^3.0.1", diff --git a/yarn.lock b/yarn.lock index efb372500e4..904ddc23d96 100644 --- a/yarn.lock +++ b/yarn.lock @@ -25238,10 +25238,9 @@ react-native-gesture-handler@^1.1.0: version "0.1.0-development" resolved "git+https://github.com/celo-org/react-native-geth#51dc4f1bb36bcf6280d19ff7ef2546d631a1eb32" -react-native-install-referrer@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/react-native-install-referrer/-/react-native-install-referrer-1.0.1.tgz#a44ce5353aee503ca3d56af3d94bd445445e8611" - integrity sha512-eOG5YOU/CSmqgbYQkmgiCLScLB20VSu2L+1+KY7VOM8/PaE39PCPxme0FynUFjABPmeZ21FTnTqu89DeMR18jQ== +"react-native-install-referrer@git://github.com/celo-org/react-native-install-referrer#343bf3d": + version "2.0.0" + resolved "git://github.com/celo-org/react-native-install-referrer#343bf3d1c3af6f72b8e16622826c2f8092ef45eb" react-native-iphone-x-helper@^1.0.1: version "1.0.2" From 51743eaf97f514807f41eb5dd5025c3c6654f2d4 Mon Sep 17 00:00:00 2001 From: Jean Regisser Date: Fri, 13 Sep 2019 12:34:11 +0200 Subject: [PATCH 05/61] [Wallet] Speed up jest tests (#958) --- packages/mobile/jest.config.js | 9 ++++++++- packages/react-components/jest.config.js | 7 ++++++- packages/verifier/jest.config.js | 7 ++++++- 3 files changed, 20 insertions(+), 3 deletions(-) diff --git a/packages/mobile/jest.config.js b/packages/mobile/jest.config.js index 105e439a2a8..ca0841ac944 100644 --- a/packages/mobile/jest.config.js +++ b/packages/mobile/jest.config.js @@ -4,6 +4,11 @@ module.exports = { ...defaultConfig, globals: { navigator: true, + 'ts-jest': { + // Disables type-check when running tests as it takes valuable time + // and is redundant with the tsc build step + isolatedModules: true, + }, window: true, }, moduleNameMapper: { @@ -15,5 +20,7 @@ module.exports = { preset: 'react-native', setupFilesAfterEnv: ['/jest_setup.ts'], snapshotSerializers: ['enzyme-to-json/serializer'], - transformIgnorePatterns: ['node_modules/(?!react-native|react-navigation|)'], + transformIgnorePatterns: [ + 'node_modules/(?!(@celo/)?react-native|@react-navigation|redux-persist|date-fns)', + ], } diff --git a/packages/react-components/jest.config.js b/packages/react-components/jest.config.js index 16266778be5..a43de66828b 100644 --- a/packages/react-components/jest.config.js +++ b/packages/react-components/jest.config.js @@ -5,10 +5,15 @@ module.exports = { collectCoverageFrom: ['**/*.ts?(x)', '!**/*.d.ts'], globals: { navigator: true, + 'ts-jest': { + // Disables type-check when running tests as it takes valuable time + // and is redundant with the tsc build step + isolatedModules: true, + }, window: true, }, modulePathIgnorePatterns: ['/node_modules/(.*)/node_modules/react-native'], preset: 'react-native', setupFilesAfterEnv: ['/jest_setup'], - transformIgnorePatterns: ['node_modules/(?!react-native|react-navigation|)'], + transformIgnorePatterns: ['node_modules/(?!(@celo/)?react-native|@react-navigation)'], } diff --git a/packages/verifier/jest.config.js b/packages/verifier/jest.config.js index fb66e7b237f..3d404b44128 100644 --- a/packages/verifier/jest.config.js +++ b/packages/verifier/jest.config.js @@ -4,6 +4,11 @@ module.exports = { ...defaultConfig, globals: { navigator: true, + 'ts-jest': { + // Disables type-check when running tests as it takes valuable time + // and is redundant with the tsc build step + isolatedModules: true, + }, window: true, }, moduleNameMapper: { @@ -15,5 +20,5 @@ module.exports = { '\\.(ts|tsx)$': 'ts-jest', '^.+\\.(js)$': 'babel-jest', }, - transformIgnorePatterns: ['node_modules/(?!react-native|react-navigation|)'], + transformIgnorePatterns: ['node_modules/(?!(@celo/)?react-native|@react-navigation)'], } From 42d81d49c0e6e68c64597e734d85509f758acec4 Mon Sep 17 00:00:00 2001 From: J M Rossy Date: Fri, 13 Sep 2019 14:07:32 +0200 Subject: [PATCH 06/61] [Wallet] Fix contact thumbnails in tx feed and related cleanup (#943) * Fix contact thumbnails in tx feed * Remove duplicated logic around avatars * Remove now obsolete AccountInfo component * Add simple component for the user's own avatar * Fix Transaction type enum to match value in blockchain api * Some more related cleanup --- packages/mobile/src/account/Account.tsx | 17 +- .../mobile/src/account/AccountInfo.test.tsx | 56 -- packages/mobile/src/account/AccountInfo.tsx | 128 --- packages/mobile/src/account/Profile.tsx | 4 +- .../__snapshots__/Account.test.tsx.snap | 392 ++------- .../__snapshots__/AccountInfo.test.tsx.snap | 759 ------------------ .../__snapshots__/Invite.test.tsx.snap | 27 +- .../__snapshots__/InviteReview.test.tsx.snap | 19 +- packages/mobile/src/account/reducer.ts | 7 +- packages/mobile/src/apollo/__mockData__.ts | 17 +- packages/mobile/src/apollo/types.ts | 5 +- packages/mobile/src/components/Avatar.tsx | 99 +-- packages/mobile/src/components/AvatarSelf.tsx | 23 + packages/mobile/src/exchange/Activity.tsx | 2 +- packages/mobile/src/exchange/actions.ts | 2 +- .../PaymentRequestConfirmation.test.tsx.snap | 19 +- packages/mobile/src/qrcode/QRCode.tsx | 38 +- .../qrcode/__snapshots__/QRCode.test.tsx.snap | 114 +-- .../mobile/src/recipients/RecipientItem.tsx | 4 +- .../__snapshots__/RecipientItem.test.tsx.snap | 27 +- packages/mobile/src/recipients/recipient.ts | 6 +- .../src/send/TransferConfirmationCard.tsx | 4 +- .../__snapshots__/SendAmount.test.tsx.snap | 19 +- .../SendConfirmation.test.tsx.snap | 114 +-- .../TransferConfirmationCard.test.tsx.snap | 8 +- .../TransferReviewCard.test.tsx.snap | 4 +- .../transactions/ExchangeFeedItem.test.tsx | 4 +- .../src/transactions/TransactionFeed.test.tsx | 6 +- .../src/transactions/TransactionFeed.tsx | 39 +- .../transactions/TransactionReviewScreen.tsx | 2 +- .../src/transactions/TransferFeedItem.tsx | 110 ++- .../TransactionFeed.test.tsx.snap | 612 ++------------ .../TransferFeedItem.test.tsx.snap | 174 ++-- packages/mobile/src/transactions/actions.ts | 2 +- packages/mobile/src/transactions/reducer.ts | 5 +- .../react-components/components/Avatar.tsx | 2 +- .../components/ContactCircle.tsx | 19 +- 37 files changed, 637 insertions(+), 2252 deletions(-) delete mode 100644 packages/mobile/src/account/AccountInfo.test.tsx delete mode 100644 packages/mobile/src/account/AccountInfo.tsx delete mode 100644 packages/mobile/src/account/__snapshots__/AccountInfo.test.tsx.snap create mode 100644 packages/mobile/src/components/AvatarSelf.tsx diff --git a/packages/mobile/src/account/Account.tsx b/packages/mobile/src/account/Account.tsx index c128276c142..19daafe8cff 100644 --- a/packages/mobile/src/account/Account.tsx +++ b/packages/mobile/src/account/Account.tsx @@ -9,11 +9,12 @@ import { Clipboard, ScrollView, StyleSheet, Text, TouchableOpacity, View } from import DeviceInfo from 'react-native-device-info' import { Sentry } from 'react-native-sentry' import { connect } from 'react-redux' -import AccountInfo from 'src/account/AccountInfo' +import { devModeTriggerClicked } from 'src/account/actions' import SettingsItem from 'src/account/SettingsItem' import CeloAnalytics from 'src/analytics/CeloAnalytics' import { CustomEventNames } from 'src/analytics/constants' import { resetAppOpenedState, setAnalyticsEnabled, setNumberVerified } from 'src/app/actions' +import { AvatarSelf } from 'src/components/AvatarSelf' import { FAQ_LINK, TOS_LINK } from 'src/config' import { features } from 'src/flags' import { Namespaces } from 'src/i18n' @@ -32,6 +33,7 @@ interface DispatchProps { setNumberVerified: typeof setNumberVerified resetAppOpenedState: typeof resetAppOpenedState setAnalyticsEnabled: typeof setAnalyticsEnabled + devModeTriggerClicked: typeof devModeTriggerClicked } interface StateProps { @@ -61,6 +63,7 @@ const mapDispatchToProps = { setNumberVerified, resetAppOpenedState, setAnalyticsEnabled, + devModeTriggerClicked, } export class Account extends React.Component { @@ -137,7 +140,7 @@ export class Account extends React.Component { } } - onCopyAddressClick = () => { + onPressAddress = () => { const { account, t } = this.props if (!account) { return @@ -146,6 +149,10 @@ export class Account extends React.Component { Logger.showMessage(t('addressCopied')) } + onPressAvatar = () => { + this.props.devModeTriggerClicked() + } + getDevSettingsComp() { const { devModeActive } = this.props const { verified } = this.state @@ -195,9 +202,11 @@ export class Account extends React.Component { - + + + - + {account} diff --git a/packages/mobile/src/account/AccountInfo.test.tsx b/packages/mobile/src/account/AccountInfo.test.tsx deleted file mode 100644 index 5fb8939b550..00000000000 --- a/packages/mobile/src/account/AccountInfo.test.tsx +++ /dev/null @@ -1,56 +0,0 @@ -import * as React from 'react' -import 'react-native' -import { Provider } from 'react-redux' -import * as renderer from 'react-test-renderer' -import AccountInfo from 'src/account/AccountInfo' -import { createMockStore } from 'test/utils' - -describe('AccountInfo', () => { - it('renders correctly', () => { - const store = createMockStore({ - account: { photosNUXClicked: true }, - }) - const tree = renderer.create( - - - - ) - expect(tree).toMatchSnapshot() - }) - - it('renders correctly', () => { - const store = createMockStore({ - account: { name: '' }, - }) - const tree = renderer.create( - - - - ) - expect(tree).toMatchSnapshot() - }) - - it('renders correctly', () => { - const store = createMockStore({ - account: { e164PhoneNumber: '' }, - }) - const tree = renderer.create( - - - - ) - expect(tree).toMatchSnapshot() - }) - - it('renders correctly', () => { - const store = createMockStore({ - account: { name: '', e164PhoneNumber: '' }, - }) - const tree = renderer.create( - - - - ) - expect(tree).toMatchSnapshot() - }) -}) diff --git a/packages/mobile/src/account/AccountInfo.tsx b/packages/mobile/src/account/AccountInfo.tsx deleted file mode 100644 index f420f773054..00000000000 --- a/packages/mobile/src/account/AccountInfo.tsx +++ /dev/null @@ -1,128 +0,0 @@ -import ContactCircle from '@celo/react-components/components/ContactCircle' -import PhoneNumberWithFlag from '@celo/react-components/components/PhoneNumberWithFlag' -import QRCode from '@celo/react-components/icons/QRCode' -import { fontStyles } from '@celo/react-components/styles/fonts' -import * as React from 'react' -import { StyleSheet, Text, TouchableOpacity, View } from 'react-native' -import { connect } from 'react-redux' -import { devModeTriggerClicked } from 'src/account/actions' -import { getUserContactDetails, UserContactDetails } from 'src/account/reducer' -import CeloAnalytics from 'src/analytics/CeloAnalytics' -import { CustomEventNames } from 'src/analytics/constants' -import { componentWithAnalytics } from 'src/analytics/wrapper' -import { navigate } from 'src/navigator/NavigationService' -import { Screens } from 'src/navigator/Screens' -import { RootState } from 'src/redux/reducers' - -interface DispatchProps { - devModeTriggerClicked: typeof devModeTriggerClicked -} - -interface StateProps { - name: string - e164Number: string - photosNUXClicked: boolean - userContact: UserContactDetails - devModeActive: boolean - defaultCountryCode: string -} - -type Props = StateProps & DispatchProps - -const mapStateToProps = (state: RootState) => { - return { - name: state.account.name, - e164Number: state.account.e164PhoneNumber, - // Disable education flow for now - photosNUXClicked: true, // state.account.photosNUXClicked, - userContact: getUserContactDetails(state), - devModeActive: state.account.devModeActive || false, - defaultCountryCode: state.account.defaultCountryCode, - } -} - -export class AccountInfo extends React.Component { - handleNameClicked = () => { - this.props.devModeTriggerClicked() - } - - handlePhotoClicked = () => { - const { photosNUXClicked } = this.props - if (!photosNUXClicked) { - CeloAnalytics.track(CustomEventNames.photos_education) - navigate(Screens.PhotosEducation) - } else { - CeloAnalytics.track(CustomEventNames.qrcode_main_screen_visit) - navigate(Screens.QRCode) - } - } - - render() { - const { name, e164Number, userContact, defaultCountryCode } = this.props - - return ( - - - - - - - - {!!name && ( - - {name} - - )} - {!!e164Number && ( - - )} - - ) - } -} - -const style = StyleSheet.create({ - accountProfile: { - alignItems: 'center', - }, - photosNUX: { - flexDirection: 'row', - alignItems: 'flex-end', - overflow: 'visible', - padding: 10, - }, - qrcode: { - position: 'absolute', - right: 5, - bottom: 5, - zIndex: 10, - }, - nameText: { - paddingTop: 10, - }, - dot: { - position: 'absolute', - right: -15, - bottom: 25, - zIndex: 20, - }, -}) - -export default componentWithAnalytics( - connect( - mapStateToProps, - { devModeTriggerClicked } - )(AccountInfo) -) diff --git a/packages/mobile/src/account/Profile.tsx b/packages/mobile/src/account/Profile.tsx index 4138ec7f048..fe0e4936533 100644 --- a/packages/mobile/src/account/Profile.tsx +++ b/packages/mobile/src/account/Profile.tsx @@ -3,7 +3,7 @@ import * as React from 'react' import { WithNamespaces, withNamespaces } from 'react-i18next' import { ScrollView, StyleSheet, View } from 'react-native' import { connect } from 'react-redux' -import { getUserContactDetails, UserContactDetails } from 'src/account/reducer' +import { UserContactDetails, userContactDetailsSelector } from 'src/account/reducer' import SettingsItem from 'src/account/SettingsItem' import CeloAnalytics from 'src/analytics/CeloAnalytics' import { CustomEventNames } from 'src/analytics/constants' @@ -26,7 +26,7 @@ type Props = OwnProps & StateProps & WithNamespaces const mapStateToProps = (state: RootState) => { return { name: state.account.name, - userContact: getUserContactDetails(state), + userContact: userContactDetailsSelector(state), } } diff --git a/packages/mobile/src/account/__snapshots__/Account.test.tsx.snap b/packages/mobile/src/account/__snapshots__/Account.test.tsx.snap index 42d029fc3d5..3caa9563788 100644 --- a/packages/mobile/src/account/__snapshots__/Account.test.tsx.snap +++ b/packages/mobile/src/account/__snapshots__/Account.test.tsx.snap @@ -20,28 +20,26 @@ exports[`Account renders correctly 1`] = ` } > @@ -53,7 +51,10 @@ exports[`Account renders correctly 1`] = ` "flexDirection": "row", "justifyContent": "center", }, - undefined, + Object { + "alignSelf": "center", + "margin": "auto", + }, ] } > @@ -66,175 +67,54 @@ exports[`Account renders correctly 1`] = ` }, Object { "backgroundColor": "#7AD6FE", - "borderRadius": 27.5, - "height": 55, - "width": 55, + "borderRadius": 20, + "height": 40, + "width": 40, }, ] } > - - J - - - - - - - - - - - - - - + - - - John Doe - - - +1 - - - (415) 555-6666 + John Doe @@ -956,28 +836,26 @@ exports[`Account when dev mode active renders correctly 1`] = ` } > @@ -989,7 +867,10 @@ exports[`Account when dev mode active renders correctly 1`] = ` "flexDirection": "row", "justifyContent": "center", }, - undefined, + Object { + "alignSelf": "center", + "margin": "auto", + }, ] } > @@ -1002,175 +883,54 @@ exports[`Account when dev mode active renders correctly 1`] = ` }, Object { "backgroundColor": "#7AD6FE", - "borderRadius": 27.5, - "height": 55, - "width": 55, + "borderRadius": 20, + "height": 40, + "width": 40, }, ] } > - - J - - - - - - - - - - - - - - + - - - John Doe - - - +1 - - - (415) 555-6666 + John Doe diff --git a/packages/mobile/src/account/__snapshots__/AccountInfo.test.tsx.snap b/packages/mobile/src/account/__snapshots__/AccountInfo.test.tsx.snap deleted file mode 100644 index 104f9e429c8..00000000000 --- a/packages/mobile/src/account/__snapshots__/AccountInfo.test.tsx.snap +++ /dev/null @@ -1,759 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`AccountInfo renders correctly 1`] = ` - - - - - - J - - - - - - - - - - - - - - - - - - John Doe - - - - +1 - - - (415) 555-6666 - - - -`; - -exports[`AccountInfo renders correctly 2`] = ` - - - - - - # - - - - - - - - - - - - - - - - - - - +1 - - - (415) 555-6666 - - - -`; - -exports[`AccountInfo renders correctly 3`] = ` - - - - - - J - - - - - - - - - - - - - - - - - - John Doe - - -`; - -exports[`AccountInfo renders correctly 4`] = ` - - - - - - # - - - - - - - - - - - - - - - - - -`; diff --git a/packages/mobile/src/account/__snapshots__/Invite.test.tsx.snap b/packages/mobile/src/account/__snapshots__/Invite.test.tsx.snap index 849499fbf47..957f764b21f 100644 --- a/packages/mobile/src/account/__snapshots__/Invite.test.tsx.snap +++ b/packages/mobile/src/account/__snapshots__/Invite.test.tsx.snap @@ -529,22 +529,21 @@ exports[`Invite renders correctly with recipients 1`] = ` ] } > - - J - + /> - - J - + /> = 5, + devModeActive: newClickCount >= 3, } case Actions.PHOTOSNUX_CLICKED: return { @@ -124,7 +124,8 @@ export const reducer = (state: State | undefined = initialState, action: ActionT } export const devModeSelector = (state: RootState) => state.account.devModeActive +export const nameSelector = (state: RootState) => state.account.name export const e164NumberSelector = (state: RootState) => state.account.e164PhoneNumber export const defaultCountryCodeSelector = (state: RootState) => state.account.defaultCountryCode -export const getUserContactDetails = (state: RootState) => state.account.contactDetails +export const userContactDetailsSelector = (state: RootState) => state.account.contactDetails export const pincodeSelector = (state: RootState) => state.account.pincodeSet diff --git a/packages/mobile/src/apollo/__mockData__.ts b/packages/mobile/src/apollo/__mockData__.ts index 34b149059bb..7ed67551a4d 100644 --- a/packages/mobile/src/apollo/__mockData__.ts +++ b/packages/mobile/src/apollo/__mockData__.ts @@ -5,12 +5,13 @@ import { UserTransactionsData, } from 'src/apollo/types' import { SENTINEL_INVITE_COMMENT } from 'src/invite/actions' +import { TransactionTypes } from 'src/transactions/reducer' export const invitedAddress = '0x1b173' const exchangeDollar: HomeExchangeFragment = { __typename: EventTypeNames.Exchange, - type: 'Exchange', + type: 'EXCHANGE' as TransactionTypes, hash: '1', inValue: 19080, timestamp: Date.now(), @@ -21,7 +22,7 @@ const exchangeDollar: HomeExchangeFragment = { const exchangeGold: HomeExchangeFragment = { __typename: EventTypeNames.Exchange, - type: 'Exchange', + type: 'EXCHANGE' as TransactionTypes, hash: '1', inValue: 190, timestamp: Date.now(), @@ -32,7 +33,7 @@ const exchangeGold: HomeExchangeFragment = { const sent: HomeTransferFragment = { __typename: EventTypeNames.Transfer, - type: 'SENT', + type: 'SENT' as TransactionTypes, value: 987161, symbol: 'Celo Gold', timestamp: Date.now(), @@ -43,7 +44,7 @@ const sent: HomeTransferFragment = { const sentInvite: HomeTransferFragment = { __typename: EventTypeNames.Transfer, - type: 'SENT', + type: 'SENT' as TransactionTypes, value: 0.33, symbol: 'Celo Dollar', timestamp: Date.now(), @@ -54,7 +55,7 @@ const sentInvite: HomeTransferFragment = { const recieved: HomeTransferFragment = { __typename: EventTypeNames.Transfer, - type: 'RECEIVED', + type: 'RECEIVED' as TransactionTypes, value: 587161, symbol: 'Celo Gold', timestamp: Date.now(), @@ -64,7 +65,7 @@ const recieved: HomeTransferFragment = { } const faucet: HomeTransferFragment = { __typename: EventTypeNames.Transfer, - type: 'FAUCET', + type: 'FAUCET' as TransactionTypes, value: 387161, symbol: 'Celo Dollar', timestamp: Date.now(), @@ -74,7 +75,7 @@ const faucet: HomeTransferFragment = { } const verificationFee: HomeTransferFragment = { __typename: EventTypeNames.Transfer, - type: 'VERIFICATION_FEE', + type: 'VERIFICATION_FEE' as TransactionTypes, value: 0.3, symbol: 'Celo Gold', timestamp: Date.now(), @@ -84,7 +85,7 @@ const verificationFee: HomeTransferFragment = { } const verificationReward: HomeTransferFragment = { __typename: EventTypeNames.Transfer, - type: 'VERIFICATION_REWARD', + type: 'VERIFICATION_REWARD' as TransactionTypes, value: 9371, symbol: 'Celo Dollar', timestamp: Date.now(), diff --git a/packages/mobile/src/apollo/types.ts b/packages/mobile/src/apollo/types.ts index a8e17b9a156..d3d57b23ad0 100644 --- a/packages/mobile/src/apollo/types.ts +++ b/packages/mobile/src/apollo/types.ts @@ -1,4 +1,5 @@ import { Query } from 'react-apollo' +import { TransactionTypes } from 'src/transactions/reducer' export interface UserTransactionsVariables { address: string @@ -11,7 +12,7 @@ export enum EventTypeNames { export interface HomeExchangeFragment { __typename: EventTypeNames.Exchange - type: string + type: TransactionTypes hash: string inValue: number outValue: number @@ -22,7 +23,7 @@ export interface HomeExchangeFragment { export interface HomeTransferFragment { __typename: EventTypeNames.Transfer - type: string + type: TransactionTypes hash: string value: number symbol: string diff --git a/packages/mobile/src/components/Avatar.tsx b/packages/mobile/src/components/Avatar.tsx index b691a8b2c4e..df4e07974e3 100644 --- a/packages/mobile/src/components/Avatar.tsx +++ b/packages/mobile/src/components/Avatar.tsx @@ -8,81 +8,59 @@ import { Avatar as BaseAvatar } from '@celo/react-components/components/Avatar' import * as React from 'react' import { withNamespaces, WithNamespaces } from 'react-i18next' import { Image, StyleSheet } from 'react-native' -import { connect } from 'react-redux' +import { useSelector } from 'react-redux' +import { defaultCountryCodeSelector } from 'src/account/reducer' import { Namespaces } from 'src/i18n' import { unknownUserIcon } from 'src/images/Images' -import { getRecipientThumbnail, Recipient, RecipientKind } from 'src/recipients/recipient' -import { RootState } from 'src/redux/reducers' +import { getRecipientThumbnail, Recipient } from 'src/recipients/recipient' const DEFAULT_ICON_SIZE = 40 -interface Props { +interface OwnProps { recipient?: Recipient e164Number?: string name?: string address?: string - defaultCountryCode: string iconSize?: number } -interface StateProps { - defaultCountryCode: string -} +type Props = OwnProps & WithNamespaces -const mapStateToProps = (state: RootState): StateProps => { - return { - defaultCountryCode: state.account.defaultCountryCode, +function getDisplayName({ name, recipient, e164Number, address, t }: Props) { + if (name) { + return name + } + if (recipient && recipient.displayName) { + return recipient.displayName + } + if (e164Number) { + return t('mobileNumber') } + if (address) { + return t('walletAddress') + } + throw new Error('Invalid avatar props, cannot determine display name') } -type AvatarProps = Props & StateProps & WithNamespaces - -export class Avatar extends React.PureComponent { - render() { - const { t, recipient, address, e164Number, iconSize = DEFAULT_ICON_SIZE } = this.props +export function Avatar(props: Props) { + const defaultCountryCode = useSelector(defaultCountryCodeSelector) + const { recipient, e164Number, iconSize = DEFAULT_ICON_SIZE } = props - let { name } = this.props - - if (!recipient && !name) { - // TransferFeedItem does not specify what kind of recipient was used, so - // here we assume if the address is missing, then it is a mobile # and - // if the phone number is missing, then it is an address. Since - // blockchain-api responds only addresses and the recipient is fetched - // during navigation, then it (should) be only address & contact recipients - if (!address) { - name = t('mobileNumber') - } - - if (!e164Number) { - name = t('walletAddress') - } - } - - if (recipient && recipient.kind === RecipientKind.Contact) { - return ( - - ) - } - - return ( - - - - ) - } + return ( + + + + ) } const style = StyleSheet.create({ @@ -92,7 +70,4 @@ const style = StyleSheet.create({ }, }) -// TODO(Rossy + Jean) simplify this file with useSelector -export default connect(mapStateToProps)( - withNamespaces(Namespaces.sendFlow7)(Avatar) -) +export default withNamespaces(Namespaces.sendFlow7)(Avatar) diff --git a/packages/mobile/src/components/AvatarSelf.tsx b/packages/mobile/src/components/AvatarSelf.tsx new file mode 100644 index 00000000000..1d0e04f7e64 --- /dev/null +++ b/packages/mobile/src/components/AvatarSelf.tsx @@ -0,0 +1,23 @@ +import * as React from 'react' +import { useSelector } from 'react-redux' +import { e164NumberSelector, nameSelector, userContactDetailsSelector } from 'src/account/reducer' +import Avatar from 'src/components/Avatar' +import { RecipientKind, RecipientWithContact } from 'src/recipients/recipient' + +// An avatar for the wallet user themselves +export function AvatarSelf() { + const displayName = useSelector(nameSelector) + const e164PhoneNumber = useSelector(e164NumberSelector) + const contactDetails = useSelector(userContactDetailsSelector) + + // Recipient refering to the wallet user, used for the avatar + const recipient: RecipientWithContact = { + kind: RecipientKind.Contact, + contactId: contactDetails.contactId || 'none', + thumbnailPath: contactDetails.thumbnailPath || undefined, + displayName, + e164PhoneNumber, + } + + return +} diff --git a/packages/mobile/src/exchange/Activity.tsx b/packages/mobile/src/exchange/Activity.tsx index ef8010489aa..7e430ec9f3f 100644 --- a/packages/mobile/src/exchange/Activity.tsx +++ b/packages/mobile/src/exchange/Activity.tsx @@ -23,7 +23,7 @@ const mapStateToProps = (state: RootState): StateProps => ({ }) function filterToExchangeTxs(tx: StandbyTransaction) { - return tx.type === TransactionTypes.EXCHANGED + return tx.type === TransactionTypes.EXCHANGE } export class Activity extends React.Component { diff --git a/packages/mobile/src/exchange/actions.ts b/packages/mobile/src/exchange/actions.ts index 05a82793311..9126796ed06 100644 --- a/packages/mobile/src/exchange/actions.ts +++ b/packages/mobile/src/exchange/actions.ts @@ -266,7 +266,7 @@ function* createStandbyTx( yield put( addStandbyTransaction({ id: txId, - type: TransactionTypes.EXCHANGED, + type: TransactionTypes.EXCHANGE, status: TransactionStatus.Pending, inSymbol: makerToken, inValue: makerAmount.toString(), diff --git a/packages/mobile/src/paymentRequest/__snapshots__/PaymentRequestConfirmation.test.tsx.snap b/packages/mobile/src/paymentRequest/__snapshots__/PaymentRequestConfirmation.test.tsx.snap index 7eef9c7469a..8aff3cb3bfe 100644 --- a/packages/mobile/src/paymentRequest/__snapshots__/PaymentRequestConfirmation.test.tsx.snap +++ b/packages/mobile/src/paymentRequest/__snapshots__/PaymentRequestConfirmation.test.tsx.snap @@ -138,22 +138,25 @@ exports[`PaymentRequestConfirmation renders correctly for request payment confir ] } > - - J - + /> { return { name: state.account.name, e164Number: state.account.e164PhoneNumber, - userContact: getUserContactDetails(state), + userContact: userContactDetailsSelector(state), account: currentAccountSelector(state), defaultCountryCode: state.account.defaultCountryCode, } @@ -66,13 +65,13 @@ class QRCodeDisplay extends React.Component { qrCodeRefReady = (c: SVG) => (this.svg = c) - downloadImage = () => this.props.shareQRCode(this.svg) + onPressShare = () => this.props.shareQRCode(this.svg) - goToScanCode = () => { + onPressScanCode = () => { navigate(Screens.QRScanner) } - async componentDidMount() { + componentDidMount() { const { name, account, e164Number } = this.props const qrContent = JSON.stringify({ address: account, @@ -83,24 +82,12 @@ class QRCodeDisplay extends React.Component { } render() { - const { name, e164Number, userContact, defaultCountryCode, t } = this.props + const { t } = this.props return ( - - - {name} - - + { />