From a18351133ce34ba254e1dd804c23d5b27e78dd12 Mon Sep 17 00:00:00 2001 From: J M Rossy Date: Thu, 31 Oct 2019 11:57:51 +0100 Subject: [PATCH 01/16] Add navigation fix for hot reloading Use updated RN Geth to support hot reloading --- packages/mobile/package.json | 2 +- .../mobile/src/navigator/NavigatorWrapper.tsx | 34 ++++++++++++++++++- yarn.lock | 15 ++------ 3 files changed, 37 insertions(+), 14 deletions(-) diff --git a/packages/mobile/package.json b/packages/mobile/package.json index 93b5b310e07..b14409e2ad2 100644 --- a/packages/mobile/package.json +++ b/packages/mobile/package.json @@ -97,7 +97,7 @@ "react-native-flag-secure-android": "git://github.com/kristiansorens/react-native-flag-secure-android#e234251", "react-native-fs": "^2.14.1", "react-native-gesture-handler": "^1.4.1", - "react-native-geth": "https://github.com/celo-org/react-native-geth#8ba5091", + "react-native-geth": "https://github.com/celo-org/react-native-geth#699ed8a", "react-native-install-referrer": "git://github.com/celo-org/react-native-install-referrer#343bf3d", "react-native-keep-awake": "^4.0.0", "react-native-keyboard-aware-scroll-view": "^0.9.1", diff --git a/packages/mobile/src/navigator/NavigatorWrapper.tsx b/packages/mobile/src/navigator/NavigatorWrapper.tsx index 67649badc6a..7a1b911f8e2 100644 --- a/packages/mobile/src/navigator/NavigatorWrapper.tsx +++ b/packages/mobile/src/navigator/NavigatorWrapper.tsx @@ -1,3 +1,4 @@ +import AsyncStorage from '@react-native-community/async-storage' import * as React from 'react' import { StyleSheet, View } from 'react-native' import { createAppContainer, NavigationState } from 'react-navigation' @@ -6,6 +7,33 @@ import AlertBanner from 'src/alert/AlertBanner' import { recordStateChange, setTopLevelNavigator } from 'src/navigator/NavigationService' import Navigator from 'src/navigator/Navigator' import BackupPrompt from 'src/shared/BackupPrompt' +import Logger from 'src/utils/Logger' + +// This uses RN Navigation's experimental nav state persistence +// to improve the hot reloading experience when in DEV mode +// https://reactnavigation.org/docs/en/state-persistence.html +function getPersistenceFunctions() { + if (!__DEV__) { + return undefined + } + + const persistenceKey = 'NAV_STATE_PERSIST_KEY' + const persistNavigationState = async (navState: any) => { + try { + await AsyncStorage.setItem(persistenceKey, JSON.stringify(navState)) + } catch (e) { + Logger.error('NavigatorWrapper', 'Error persisting nav state', e) + } + } + const loadNavigationState = async () => { + const state = await AsyncStorage.getItem(persistenceKey) + return state && JSON.parse(state) + } + return { + persistNavigationState, + loadNavigationState, + } +} const navigationStateChange = (prev: NavigationState, current: NavigationState) => recordStateChange(prev, current) @@ -26,7 +54,11 @@ export class NavigatorWrapper extends React.Component { render() { return ( - + diff --git a/yarn.lock b/yarn.lock index 20ee84cf0d9..93b4511fb55 100644 --- a/yarn.lock +++ b/yarn.lock @@ -21162,7 +21162,7 @@ lru-cache@^3.2.0: dependencies: pseudomap "^1.0.1" -lru-cache@^4.0.0, lru-cache@^4.1.5, lru-cache@~4.1.1: +lru-cache@^4.0.0, lru-cache@^4.1.3, lru-cache@^4.1.5, lru-cache@~4.1.1: version "4.1.5" resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-4.1.5.tgz#8bbe50ea85bed59bc9e33dcab8235ee9bcf443cd" integrity sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g== @@ -21186,14 +21186,6 @@ lru-cache@^4.1.1, lru-cache@^4.1.2: pseudomap "^1.0.2" yallist "^2.1.2" -lru-cache@^4.1.3, lru-cache@^4.1.5, lru-cache@~4.1.1: - version "4.1.5" - resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-4.1.5.tgz#8bbe50ea85bed59bc9e33dcab8235ee9bcf443cd" - integrity sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g== - dependencies: - pseudomap "^1.0.2" - yallist "^2.1.2" - lru-cache@^5.0.0, lru-cache@^5.1.1: version "5.1.1" resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-5.1.1.tgz#1da27e6710271947695daf6848e847f01d84b920" @@ -26510,9 +26502,9 @@ react-native-gesture-handler@^1.4.1: invariant "^2.2.4" prop-types "^15.7.2" -"react-native-geth@https://github.com/celo-org/react-native-geth#8ba5091": +"react-native-geth@https://github.com/celo-org/react-native-geth#699ed8a": version "0.1.0-development" - resolved "https://github.com/celo-org/react-native-geth#8ba5091d232f58913026b2df1f2b834e2e0108d0" + resolved "https://github.com/celo-org/react-native-geth#699ed8ad8d6623d00b24933b85cd71aeda8920b6" "react-native-install-referrer@git://github.com/celo-org/react-native-install-referrer#343bf3d": version "2.0.0" @@ -33939,7 +33931,6 @@ websocket@^1.0.28: dependencies: debug "^2.2.0" es5-ext "^0.10.50" - gulp "^4.0.2" nan "^2.14.0" typedarray-to-buffer "^3.1.5" yaeti "^0.0.6" From d542a4513924d6d7e77126f0845a12ad0abf4bfb Mon Sep 17 00:00:00 2001 From: J M Rossy Date: Thu, 31 Oct 2019 17:21:41 +0100 Subject: [PATCH 02/16] [Wallet] Add new carousel component --- packages/mobile/package.json | 2 + packages/mobile/src/components/Carousel.tsx | 110 ++++++++++ packages/mobile/src/navigator/Navigator.tsx | 4 +- packages/mobile/src/navigator/Screens.tsx | 2 +- packages/mobile/src/verify/Input.tsx | 188 ------------------ .../src/verify/VerificationLoadingScreen.tsx | 58 ++++++ packages/mobile/src/verify/Verify.test.tsx | 17 -- packages/react-components/styles/colors.tsx | 1 + yarn.lock | 24 +++ 9 files changed, 198 insertions(+), 208 deletions(-) create mode 100644 packages/mobile/src/components/Carousel.tsx delete mode 100644 packages/mobile/src/verify/Input.tsx create mode 100644 packages/mobile/src/verify/VerificationLoadingScreen.tsx diff --git a/packages/mobile/package.json b/packages/mobile/package.json index b14409e2ad2..d324d255cdf 100644 --- a/packages/mobile/package.json +++ b/packages/mobile/package.json @@ -118,6 +118,7 @@ "react-native-send-intent": "git+https://github.com/celo-org/react-native-send-intent#a0f4b00", "react-native-shadow": "^1.2.2", "react-native-share": "^2.0.0", + "react-native-snap-carousel": "^3.8.4", "react-native-splash-screen": "^3.2.0", "react-native-svg": "^9.11.1", "react-native-swiper": "^1.5.14", @@ -151,6 +152,7 @@ "@types/lodash": "^4.14.136", "@types/react": "^16.8.19", "@types/react-native": "^0.60.19", + "@types/react-native-snap-carousel": "^3.7.4", "@types/react-native-fs": "^2.8.1", "@types/react-native-keep-awake": "^2.0.1", "@types/react-redux": "^7.1.2", diff --git a/packages/mobile/src/components/Carousel.tsx b/packages/mobile/src/components/Carousel.tsx new file mode 100644 index 00000000000..9c6e2ba3adc --- /dev/null +++ b/packages/mobile/src/components/Carousel.tsx @@ -0,0 +1,110 @@ +/** + * A custom style carousel based on react-native-snap-carousel + */ + +import colors from '@celo/react-components/styles/colors' +import fontStyles from '@celo/react-components/styles/fonts' +import variables from '@celo/react-components/styles/variables' +import * as React from 'react' +import { StyleSheet, Text, View, ViewStyle } from 'react-native' +import { BoxShadow } from 'react-native-shadow' +import RNCarousel, { Pagination } from 'react-native-snap-carousel' + +const ITEM_WIDTH = variables.width - 70 +const ITEM_HEIGHT = 250 + +interface OwnProps { + containerStyle: ViewStyle + items: CarouselItem[] +} + +export interface CarouselItem { + text: string + icon?: React.ComponentType +} + +function renderItem({ item, index }: { item: CarouselItem; index: number }) { + return ( + + + + {item.icon} + {item.text} + + + + ) +} + +export function Carousel(props: OwnProps) { + const ref = React.useRef(null) + const [activeItem, setActiveItem] = React.useState(0) + + return ( + + {/* For some reason the carousel is adding a bunch of item height, wrapping to cut it off*/} + + + + + + ) +} + +const shadowOpt = { + width: ITEM_WIDTH, + height: ITEM_HEIGHT, + color: '#6b7b8b', + opacity: 0.03, + border: 1, + radius: 12, + x: 0, + y: 0, + style: { + padding: 3, + }, +} + +const styles = StyleSheet.create({ + itemContainer: { + backgroundColor: '#FFFFFF', + borderWidth: 2, + borderColor: 'rgba(255, 255, 255, 0.5)', + borderRadius: 12, + width: ITEM_WIDTH - 6, + height: ITEM_HEIGHT - 6, + alignItems: 'center', + justifyContent: 'center', + }, + itemText: { + ...fontStyles.bodyLarge, + }, + paginationContainer: { + marginVertical: 10, + }, + paginationDot: {}, +}) + +export default React.memo(Carousel) diff --git a/packages/mobile/src/navigator/Navigator.tsx b/packages/mobile/src/navigator/Navigator.tsx index 3dbbfb801cb..4c0022663c6 100644 --- a/packages/mobile/src/navigator/Navigator.tsx +++ b/packages/mobile/src/navigator/Navigator.tsx @@ -62,7 +62,7 @@ import SendAmount from 'src/send/SendAmount' import SendConfirmation from 'src/send/SendConfirmation' import SetClock from 'src/set-clock/SetClock' import TransactionReviewScreen from 'src/transactions/TransactionReviewScreen' -import VerifyInput from 'src/verify/Input' +import VerificationLoadingScreen from 'src/verify/VerificationLoadingScreen' import VerifyVerified from 'src/verify/Verified' import VerifyVerifying from 'src/verify/Verifying' import VerifyEducation from 'src/verify/VerifyPhoneEducation' @@ -113,7 +113,7 @@ const NuxStack = createStackNavigator( [Screens.ImportWalletEmpty]: { screen: ImportWalletEmpty }, [Screens.ImportContacts]: { screen: ImportContacts }, [Screens.VerifyEducation]: { screen: VerifyEducation }, - [Screens.VerifyInput]: { screen: VerifyInput }, + [Screens.VerificationLoadingScreen]: { screen: VerificationLoadingScreen }, [Screens.VerifyVerifying]: { screen: VerifyVerifying }, [Screens.VerifyVerified]: { screen: VerifyVerified }, ...commonScreens, diff --git a/packages/mobile/src/navigator/Screens.tsx b/packages/mobile/src/navigator/Screens.tsx index a6a7718a9b3..e680e1f602f 100644 --- a/packages/mobile/src/navigator/Screens.tsx +++ b/packages/mobile/src/navigator/Screens.tsx @@ -65,8 +65,8 @@ export enum Screens { TransactionReview = 'TransactionReview', UpgradeScreen = 'UpgradeScreen', VerifyEducation = 'VerifyEducation', - VerifyInput = 'VerifyInput', VerifyVerified = 'VerifyVerified', VerifyVerifying = 'VerifyVerifying', + VerificationLoadingScreen = 'VerificationLoadingScreen', WalletHome = 'WalletHome', } diff --git a/packages/mobile/src/verify/Input.tsx b/packages/mobile/src/verify/Input.tsx deleted file mode 100644 index 7150dcc7898..00000000000 --- a/packages/mobile/src/verify/Input.tsx +++ /dev/null @@ -1,188 +0,0 @@ -import Button, { BtnTypes } from '@celo/react-components/components/Button' -import PhoneNumberInput from '@celo/react-components/components/PhoneNumberInput' -import colors from '@celo/react-components/styles/colors' -import { fontStyles } from '@celo/react-components/styles/fonts' -import { isE164Number } from '@celo/utils/src/phoneNumbers' -import * as React from 'react' -import { WithNamespaces, withNamespaces } from 'react-i18next' -import { Platform, ScrollView, StyleSheet, Text, View } from 'react-native' -import { connect } from 'react-redux' -import { setPhoneNumber } from 'src/account/actions' -import { hideAlert, showError } from 'src/alert/actions' -import componentWithAnalytics from 'src/analytics/wrapper' -import { ErrorMessages } from 'src/app/ErrorMessages' -import DevSkipButton from 'src/components/DevSkipButton' -import { DEFAULT_COUNTRY } from 'src/config' -import NuxLogo from 'src/icons/NuxLogo' -import { startVerification } from 'src/identity/actions' -import { navigate } from 'src/navigator/NavigationService' -import { Screens } from 'src/navigator/Screens' -import { RootState } from 'src/redux/reducers' -import DisconnectBanner from 'src/shared/DisconnectBanner' - -interface StateProps { - devModeActive: boolean -} - -interface DispatchProps { - setPhoneNumber: typeof setPhoneNumber - showError: typeof showError - hideAlert: typeof hideAlert - startVerification: typeof startVerification -} - -type Props = StateProps & DispatchProps & WithNamespaces - -interface State { - e164Number: string - countryCode: string - isValidNumber: boolean -} - -const mapDispatchToProps = { - setPhoneNumber, - showError, - hideAlert, - startVerification, -} - -const mapStateToProps = (state: RootState): StateProps => { - return { - devModeActive: state.account.devModeActive || false, - } -} - -export class Input extends React.Component { - state = { - e164Number: '', - countryCode: '', - isValidNumber: false, - } - - scrollView = React.createRef() - - onInputChange = () => { - this.props.hideAlert() - } - - setE164Number = (e164Number: string) => { - this.setState({ - e164Number, - }) - } - - setCountryCode = (countryCode: string) => { - this.setState({ - countryCode, - }) - } - - setIsValidNumber = (isValidNumber: boolean) => { - this.setState({ - isValidNumber, - }) - } - - onSubmit = async () => { - const { e164Number, countryCode, isValidNumber } = this.state - if (!e164Number || !isValidNumber || !isE164Number(e164Number)) { - this.props.showError(ErrorMessages.INVALID_PHONE_NUMBER) - return - } - - this.props.setPhoneNumber(e164Number, countryCode) - this.props.startVerification() - - navigate(Screens.VerifyVerifying) - } - - scrollToEnd = () => { - // (Rossy): This does not work without a setTimeout. - // I believe the animation of the keyboard showing is the problem - setTimeout(() => { - if (this.scrollView && this.scrollView.current) { - this.scrollView.current.scrollToEnd() - } - }, 1000) - } - - render() { - const { t, lng } = this.props - // This locks the country code - const defaultCountry = this.props.devModeActive ? null : DEFAULT_COUNTRY - - return ( - - - - - - {t('enterPhoneToVerify')} - - - - -