diff --git a/packages/mobile/ios/Podfile.lock b/packages/mobile/ios/Podfile.lock index 36c9c5d9c5d..3b7ebbe71cb 100644 --- a/packages/mobile/ios/Podfile.lock +++ b/packages/mobile/ios/Podfile.lock @@ -119,6 +119,10 @@ PODS: - GoogleUtilities/Logger - GTMSessionFetcher/Core (1.2.2) - leveldb-library (1.20) + - lottie-ios (3.1.3) + - lottie-react-native (3.2.1): + - lottie-ios (~> 3.1.3) + - React - nanopb (0.3.901): - nanopb/decode (= 0.3.901) - nanopb/encode (= 0.3.901) @@ -429,6 +433,8 @@ DEPENDENCIES: - Folly (from `../../../node_modules/react-native/third-party-podspecs/Folly.podspec`) - glog (from `../../../node_modules/react-native/third-party-podspecs/glog.podspec`) - GoogleUtilities (~> 5.3.7) + - lottie-ios (from `../../../node_modules/lottie-ios`) + - lottie-react-native (from `../../../node_modules/lottie-react-native`) - RCTRequired (from `../../../node_modules/react-native/Libraries/RCTRequired`) - RCTTypeSafety (from `../../../node_modules/react-native/Libraries/TypeSafety`) - React (from `../../../node_modules/react-native/`) @@ -485,19 +491,22 @@ DEPENDENCIES: - Yoga (from `../../../node_modules/react-native/ReactCommon/yoga`) SPEC REPOS: - https://github.com/cocoapods/specs.git: + https://cdn.cocoapods.org/: - Analytics - boost-for-react-native - - Crashlytics - Fabric - Firebase + - FirebaseCore + - FirebaseInstanceID + - GoogleUtilities + https://github.com/cocoapods/specs.git: + - Crashlytics + - Firebase - FirebaseAnalytics - FirebaseAnalyticsInterop - FirebaseAuth - FirebaseAuthInterop - - FirebaseCore - FirebaseDatabase - - FirebaseInstanceID - FirebaseMessaging - FirebaseStorage - GoogleAppMeasurement @@ -522,6 +531,10 @@ EXTERNAL SOURCES: :podspec: "../../../node_modules/react-native/third-party-podspecs/Folly.podspec" glog: :podspec: "../../../node_modules/react-native/third-party-podspecs/glog.podspec" + lottie-ios: + :path: "../../../node_modules/lottie-ios" + lottie-react-native: + :path: "../../../node_modules/lottie-react-native" RCTRequired: :path: "../../../node_modules/react-native/Libraries/RCTRequired" RCTTypeSafety: @@ -650,6 +663,8 @@ SPEC CHECKSUMS: GoogleUtilities: 111a012f4c3a29c9e7c954c082fafd6ee3c999c0 GTMSessionFetcher: 61bb0f61a4cb560030f1222021178008a5727a23 leveldb-library: 08cba283675b7ed2d99629a4bc5fd052cd2bb6a5 + lottie-ios: 496ac5cea1bbf1a7bd1f1f472f3232eb1b8d744b + lottie-react-native: b123a79529cc732201091f585c62c89bb4747252 nanopb: 2901f78ea1b7b4015c860c2fdd1ea2fee1a18d48 Protobuf: 1097ca58584c8d9be81bfbf2c5ff5975648dd87a RCTRequired: c639d59ed389cfb1f1203f65c2ea946d8ec586e2 diff --git a/packages/mobile/ios/celo.xcodeproj/project.pbxproj b/packages/mobile/ios/celo.xcodeproj/project.pbxproj index b1eb6be22a6..2633bf461c1 100644 --- a/packages/mobile/ios/celo.xcodeproj/project.pbxproj +++ b/packages/mobile/ios/celo.xcodeproj/project.pbxproj @@ -498,6 +498,8 @@ "${BUILT_PRODUCTS_DIR}/Yoga/yoga.framework", "${BUILT_PRODUCTS_DIR}/glog/glog.framework", "${BUILT_PRODUCTS_DIR}/leveldb-library/leveldb.framework", + "${BUILT_PRODUCTS_DIR}/lottie-ios/Lottie.framework", + "${BUILT_PRODUCTS_DIR}/lottie-react-native/lottie_react_native.framework", "${BUILT_PRODUCTS_DIR}/nanopb/nanopb.framework", "${BUILT_PRODUCTS_DIR}/react-native-camera/react_native_camera.framework", "${BUILT_PRODUCTS_DIR}/react-native-config/react_native_config.framework", @@ -559,6 +561,8 @@ "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/yoga.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/glog.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/leveldb.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Lottie.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/lottie_react_native.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/nanopb.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/react_native_camera.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/react_native_config.framework", diff --git a/packages/mobile/locales/en-US/nuxVerification2.json b/packages/mobile/locales/en-US/nuxVerification2.json index 0d30fc4237a..7e71db11cb0 100644 --- a/packages/mobile/locales/en-US/nuxVerification2.json +++ b/packages/mobile/locales/en-US/nuxVerification2.json @@ -64,5 +64,18 @@ "learnMore": "Learn more about phone verification", "start": "Start Verification", "skip": "Skip For Now" + }, + "loading": { + "verifyingNumber": "Verifying {{number}}", + "keepOpen": "Please keep the app open", + "card1": "Verifying your phone number helps your friends find you on the Celo Network.", + "card2": "On Celo, you can send money to your anyone using just their phone number.", + "card3": "Verification requires three text messages sent to your phone number." + }, + "skipModal": { + "header": "Skip Verification?", + "body1": "Verifying allows others to send value to your phone number.", + "body2": + "Without verification, you can still receive payments but only using Celo addresses or QR codes." } } diff --git a/packages/mobile/locales/es-419/nuxVerification2.json b/packages/mobile/locales/es-419/nuxVerification2.json index 71c3536d285..a75eda0e9a0 100755 --- a/packages/mobile/locales/es-419/nuxVerification2.json +++ b/packages/mobile/locales/es-419/nuxVerification2.json @@ -65,5 +65,18 @@ "learnMore": "~~Learn more about phone verification", "start": "~~Start Verification", "skip": "~~Skip For Now" + }, + "loading": { + "verifyingNumber": "~~Verifying {{number}}", + "keepOpen": "~~Please keep the app open", + "card1": "~~Verifying your phone number helps your friends find you on the Celo Network.", + "card2": "~~On Celo, you can send money to your anyone using just their phone number.", + "card3": "~~Verification requires three text messages sent to your phone number." + }, + "skipModal": { + "header": "~~Skip Verification?", + "body1": "~~Verifying allows others to send value to your phone number.", + "body2": + "~~Without verification, you can still receive payments but only using Celo addresses or QR codes." } } diff --git a/packages/mobile/package.json b/packages/mobile/package.json index 19e912a23de..9d6707df455 100644 --- a/packages/mobile/package.json +++ b/packages/mobile/package.json @@ -71,6 +71,8 @@ "i18next": "^11.9.1", "js-sha3": "^0.7.0", "lodash": "^4.17.14", + "lottie-ios": "3.1.3", + "lottie-react-native": "^3.2.1", "moment": "^2.22.1", "moment-timezone": "^0.5.23", "node-libs-react-native": "^1.0.3", @@ -99,8 +101,7 @@ "react-native-keyboard-aware-scroll-view": "^0.9.1", "react-native-localize": "^1.3.0", "react-native-mail": "^4.0.0", - "react-native-modal": "^11.4.0", - "react-native-modal-dropdown": "^0.7.0", + "react-native-modal": "^11.5.1", "react-native-permissions": "^2.0.2", "react-native-progress": "^3.6.0", "react-native-qrcode-svg": "^5.2.0", @@ -114,8 +115,8 @@ "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-sms": "^1.9.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", @@ -149,9 +150,9 @@ "@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-native-snap-carousel": "^3.7.4", "@types/react-redux": "^7.1.2", "@types/react-test-renderer": "^16.9.0", "@types/redux-mock-store": "^1.0.0", diff --git a/packages/mobile/src/components/Carousel.tsx b/packages/mobile/src/components/Carousel.tsx index 9c6e2ba3adc..9e94ab05be2 100644 --- a/packages/mobile/src/components/Carousel.tsx +++ b/packages/mobile/src/components/Carousel.tsx @@ -11,7 +11,7 @@ import { BoxShadow } from 'react-native-shadow' import RNCarousel, { Pagination } from 'react-native-snap-carousel' const ITEM_WIDTH = variables.width - 70 -const ITEM_HEIGHT = 250 +const ITEM_HEIGHT = 275 interface OwnProps { containerStyle: ViewStyle @@ -20,7 +20,7 @@ interface OwnProps { export interface CarouselItem { text: string - icon?: React.ComponentType + icon?: React.ReactElement } function renderItem({ item, index }: { item: CarouselItem; index: number }) { @@ -36,7 +36,7 @@ function renderItem({ item, index }: { item: CarouselItem; index: number }) { ) } -export function Carousel(props: OwnProps) { +function Carousel(props: OwnProps) { const ref = React.useRef(null) const [activeItem, setActiveItem] = React.useState(0) @@ -63,7 +63,6 @@ export function Carousel(props: OwnProps) { containerStyle={styles.paginationContainer} dotColor={colors.dark} inactiveDotColor={colors.lightGray} - dotStyle={styles.paginationDot} inactiveDotOpacity={1} inactiveDotScale={0.8} carouselRef={ref as any} @@ -77,7 +76,7 @@ const shadowOpt = { width: ITEM_WIDTH, height: ITEM_HEIGHT, color: '#6b7b8b', - opacity: 0.03, + opacity: 0.02, border: 1, radius: 12, x: 0, @@ -89,6 +88,7 @@ const shadowOpt = { const styles = StyleSheet.create({ itemContainer: { + padding: 20, backgroundColor: '#FFFFFF', borderWidth: 2, borderColor: 'rgba(255, 255, 255, 0.5)', @@ -97,14 +97,16 @@ const styles = StyleSheet.create({ height: ITEM_HEIGHT - 6, alignItems: 'center', justifyContent: 'center', + // TODO style shadow right on iOS + elevation: 1, }, itemText: { ...fontStyles.bodyLarge, + ...fontStyles.center, }, paginationContainer: { - marginVertical: 10, + marginTop: 5, }, - paginationDot: {}, }) export default React.memo(Carousel) diff --git a/packages/mobile/src/icons/LoadingSpinner.tsx b/packages/mobile/src/icons/LoadingSpinner.tsx new file mode 100644 index 00000000000..f4ef8eafac5 --- /dev/null +++ b/packages/mobile/src/icons/LoadingSpinner.tsx @@ -0,0 +1,23 @@ +import LottieView from 'lottie-react-native' +import React from 'react' + +interface Props { + width?: number +} + +export default class LoadingSpinner extends React.PureComponent { + static defaultProps = { + width: 40, + } + + render() { + return ( + + ) + } +} diff --git a/packages/mobile/src/icons/loadingSpinnerGreen.json b/packages/mobile/src/icons/loadingSpinnerGreen.json new file mode 100644 index 00000000000..81fbb58b006 --- /dev/null +++ b/packages/mobile/src/icons/loadingSpinnerGreen.json @@ -0,0 +1,281 @@ +{ + "v": "5.4.4", + "fr": 60, + "ip": 0, + "op": 173, + "w": 600, + "h": 600, + "nm": "WINNER green R", + "ddd": 0, + "assets": [ + { + "id": "comp_0", + "layers": [ + { + "ddd": 0, + "ind": 1, + "ty": 3, + "nm": "Null 5", + "sr": 1, + "ks": { + "o": { "a": 0, "k": 0, "ix": 11 }, + "r": { "a": 0, "k": 0, "ix": 10 }, + "p": { "a": 0, "k": [300, 300, 0], "ix": 2 }, + "a": { "a": 0, "k": [50, 50, 0], "ix": 1 }, + "s": { "a": 0, "k": [388, 388, 100], "ix": 6 } + }, + "ao": 0, + "ip": 0, + "op": 173, + "st": 0, + "bm": 0 + }, + { + "ddd": 0, + "ind": 2, + "ty": 4, + "nm": "green", + "parent": 1, + "sr": 1, + "ks": { + "o": { "a": 0, "k": 100, "ix": 11 }, + "r": { + "a": 1, + "k": [ + { + "i": { "x": [0.461], "y": [1] }, + "o": { "x": [0.074], "y": [0.125] }, + "t": 0, + "s": [268.63], + "e": [90] + }, + { + "i": { "x": [0.64], "y": [1] }, + "o": { "x": [0.36], "y": [0] }, + "t": 56, + "s": [90], + "e": [90] + }, + { + "i": { "x": [1], "y": [1] }, + "o": { "x": [0.6], "y": [0.265] }, + "t": 101, + "s": [90], + "e": [-88.135] + }, + { "t": 172.000048877002 } + ], + "ix": 10 + }, + "p": { "a": 0, "k": [50, 50, 0], "ix": 2 }, + "a": { "a": 0, "k": [103, -115, 0], "ix": 1 }, + "s": { "a": 0, "k": [18.343, 18.343, 100], "ix": 6 } + }, + "ao": 0, + "shapes": [ + { + "ty": "gr", + "it": [ + { + "ind": 0, + "ty": "sh", + "ix": 1, + "ks": { + "a": 1, + "k": [ + { + "i": { "x": 0.833, "y": 0.833 }, + "o": { "x": 0.81, "y": 0 }, + "t": 32.746, + "s": [ + { + "i": [[-206.701, 0], [0, -206.701], [206.701, 0], [0, 206.701]], + "o": [[206.701, 0], [0, 206.701], [-206.701, 0], [0, -206.701]], + "v": [[0, -374.266], [374.266, 0], [0, 374.266], [-374.266, 0]], + "c": true + } + ], + "e": [ + { + "i": [[-206.701, 0], [1.349, -4.634], [206.702, 0], [0.205, 6.376]], + "o": [[206.701, 0], [-2.223, 7.633], [-206.701, 0], [-0.25, -7.779]], + "v": [[-2.095, 0.013], [374.266, 0], [-2.237, -0.013], [-374.266, 0]], + "c": true + } + ] + }, + { + "i": { "x": 0.02, "y": 1 }, + "o": { "x": 0.167, "y": 0.167 }, + "t": 56.221, + "s": [ + { + "i": [[-206.701, 0], [1.349, -4.634], [206.702, 0], [0.205, 6.376]], + "o": [[206.701, 0], [-2.223, 7.633], [-206.701, 0], [-0.25, -7.779]], + "v": [[-2.095, 0.013], [374.266, 0], [-2.237, -0.013], [-374.266, 0]], + "c": true + } + ], + "e": [ + { + "i": [[-206.701, 0], [0, -206.701], [206.701, 0], [0, 206.701]], + "o": [[206.701, 0], [0, 206.701], [-206.701, 0], [0, -206.701]], + "v": [[0, -374.266], [374.266, 0], [0, 374.266], [-374.266, 0]], + "c": true + } + ] + }, + { + "i": { "x": 1, "y": 1 }, + "o": { "x": 0.167, "y": 0 }, + "t": 68.518, + "s": [ + { + "i": [[-206.701, 0], [0, -206.701], [206.701, 0], [0, 206.701]], + "o": [[206.701, 0], [0, 206.701], [-206.701, 0], [0, -206.701]], + "v": [[0, -374.266], [374.266, 0], [0, 374.266], [-374.266, 0]], + "c": true + } + ], + "e": [ + { + "i": [[-206.701, 0], [1.349, -4.634], [206.702, 0], [0.205, 6.376]], + "o": [[206.701, 0], [-2.223, 7.633], [-206.701, 0], [-0.25, -7.779]], + "v": [[-2.095, 0.013], [374.266, 0], [-2.237, -0.013], [-374.266, 0]], + "c": true + } + ] + }, + { + "i": { "x": 0.07, "y": 1 }, + "o": { "x": 0, "y": 0 }, + "t": 80.815, + "s": [ + { + "i": [[-206.701, 0], [1.349, -4.634], [206.702, 0], [0.205, 6.376]], + "o": [[206.701, 0], [-2.223, 7.633], [-206.701, 0], [-0.25, -7.779]], + "v": [[-2.095, 0.013], [374.266, 0], [-2.237, -0.013], [-374.266, 0]], + "c": true + } + ], + "e": [ + { + "i": [[-206.701, 0], [0, -206.701], [206.701, 0], [0, 206.701]], + "o": [[206.701, 0], [0, 206.701], [-206.701, 0], [0, -206.701]], + "v": [[0, -374.266], [374.266, 0], [0, 374.266], [-374.266, 0]], + "c": true + } + ] + }, + { "t": 130.000048877002 } + ], + "ix": 2 + }, + "nm": "Path 1", + "mn": "ADBE Vector Shape - Group", + "hd": false + }, + { + "ty": "st", + "c": { "a": 0, "k": [0.207843137255, 0.81568627451, 0.498039215686, 1], "ix": 3 }, + "o": { "a": 0, "k": 100, "ix": 4 }, + "w": { "a": 0, "k": 118, "ix": 5 }, + "lc": 2, + "lj": 2, + "bm": 0, + "nm": "Stroke 1", + "mn": "ADBE Vector Graphic - Stroke", + "hd": false + }, + { + "ty": "tr", + "p": { "a": 0, "k": [103, -115], "ix": 2 }, + "a": { "a": 0, "k": [0, 0], "ix": 1 }, + "s": { "a": 0, "k": [86.667, 86.667], "ix": 3 }, + "r": { "a": 0, "k": 0, "ix": 6 }, + "o": { "a": 0, "k": 100, "ix": 7 }, + "sk": { "a": 0, "k": 0, "ix": 4 }, + "sa": { "a": 0, "k": 0, "ix": 5 }, + "nm": "Transform" + } + ], + "nm": "Ellipse 1", + "np": 3, + "cix": 2, + "bm": 0, + "ix": 1, + "mn": "ADBE Vector Group", + "hd": false + }, + { + "ty": "tm", + "s": { + "a": 1, + "k": [ + { + "i": { "x": [0.833], "y": [0.833] }, + "o": { "x": [0], "y": [0] }, + "t": 0, + "s": [99], + "e": [0] + }, + { "t": 38.000048877002 } + ], + "ix": 1 + }, + "e": { + "a": 1, + "k": [ + { + "i": { "x": [1], "y": [1] }, + "o": { "x": [0.58], "y": [0] }, + "t": 96, + "s": [100], + "e": [1] + }, + { "t": 172.000048877002 } + ], + "ix": 2 + }, + "o": { "a": 0, "k": 0, "ix": 3 }, + "m": 1, + "ix": 2, + "nm": "Trim Paths 1", + "mn": "ADBE Vector Filter - Trim", + "hd": false + } + ], + "ip": 0, + "op": 173, + "st": -144.997997997998, + "bm": 0 + } + ] + } + ], + "layers": [ + { + "ddd": 0, + "ind": 1, + "ty": 0, + "nm": "WINNER green", + "refId": "comp_0", + "sr": 1, + "ks": { + "o": { "a": 0, "k": 100, "ix": 11 }, + "r": { "a": 0, "k": 0, "ix": 10 }, + "p": { "a": 0, "k": [300, 300, 0], "ix": 2 }, + "a": { "a": 0, "k": [300, 300, 0], "ix": 1 }, + "s": { "a": 0, "k": [-100, 100, 100], "ix": 6 } + }, + "ao": 0, + "w": 600, + "h": 600, + "ip": 0, + "op": 173, + "st": 0, + "bm": 0 + } + ], + "markers": [] +} diff --git a/packages/mobile/src/navigator/Headers.tsx b/packages/mobile/src/navigator/Headers.tsx index 9863990127d..eb02a609331 100644 --- a/packages/mobile/src/navigator/Headers.tsx +++ b/packages/mobile/src/navigator/Headers.tsx @@ -6,6 +6,10 @@ import BackButton from 'src/components/BackButton' import CancelButton from 'src/components/CancelButton' import DisconnectBanner from 'src/shared/DisconnectBanner' +export const noHeader = { + headerLeft: , +} + export const nuxNavigationOptions = { headerLeftContainerStyle: { paddingHorizontal: 10 }, headerLeft: , diff --git a/packages/mobile/src/navigator/Navigator.tsx b/packages/mobile/src/navigator/Navigator.tsx index b09dc8c8aed..ababa45c576 100644 --- a/packages/mobile/src/navigator/Navigator.tsx +++ b/packages/mobile/src/navigator/Navigator.tsx @@ -129,6 +129,9 @@ const NuxStack = createStackNavigator( ...commonScreens, }, { + navigationOptions: { + header: null, + }, ...headerArea, initialRouteName: Screens.Language, } diff --git a/packages/mobile/src/verify/VerificationEducationScreen.test.tsx b/packages/mobile/src/verify/VerificationEducationScreen.test.tsx index d5e3bf83392..b89853d09c2 100644 --- a/packages/mobile/src/verify/VerificationEducationScreen.test.tsx +++ b/packages/mobile/src/verify/VerificationEducationScreen.test.tsx @@ -1,7 +1,7 @@ import * as React from 'react' import 'react-native' +import { fireEvent, render } from 'react-native-testing-library' import { Provider } from 'react-redux' -import * as renderer from 'react-test-renderer' import VerificationEducationScreen from 'src/verify/VerificationEducationScreen' import { createMockStore } from 'test/utils' @@ -9,11 +9,16 @@ describe('VerificationEducationScreen', () => { const store = createMockStore({}) it('renders correctly', () => { - const tree = renderer.create( + const { getByTestId, toJSON } = render( ) - expect(tree).toMatchSnapshot() + expect(toJSON()).toMatchSnapshot() + + // And snapshot again after showing the modal + const skipButton = getByTestId('VerificationEducationSkip') + fireEvent.press(skipButton) + expect(toJSON()).toMatchSnapshot() }) }) diff --git a/packages/mobile/src/verify/VerificationEducationScreen.tsx b/packages/mobile/src/verify/VerificationEducationScreen.tsx index c6e63d18df5..d4640050cde 100644 --- a/packages/mobile/src/verify/VerificationEducationScreen.tsx +++ b/packages/mobile/src/verify/VerificationEducationScreen.tsx @@ -1,10 +1,13 @@ import Button, { BtnTypes } from '@celo/react-components/components/Button' import Link from '@celo/react-components/components/Link' +import TextButton from '@celo/react-components/components/TextButton' import colors from '@celo/react-components/styles/colors' import fontStyles from '@celo/react-components/styles/fonts' +import { componentStyles } from '@celo/react-components/styles/styles' import * as React from 'react' import { withNamespaces, WithNamespaces } from 'react-i18next' -import { ScrollView, StyleSheet, Text } from 'react-native' +import { ScrollView, StyleSheet, Text, View } from 'react-native' +import Modal from 'react-native-modal' import SafeAreaView from 'react-native-safe-area-view' import componentWithAnalytics from 'src/analytics/wrapper' import { Namespaces } from 'src/i18n' @@ -13,19 +16,35 @@ import { nuxNavigationOptions } from 'src/navigator/Headers' import { navigate } from 'src/navigator/NavigationService' import { Screens } from 'src/navigator/Screens' -class VerificationEducationScreen extends React.Component { +interface State { + isModalVisible: boolean +} + +class VerificationEducationScreen extends React.Component { static navigationOptions = nuxNavigationOptions + state: State = { + isModalVisible: false, + } + onPressLearnMore = () => { navigate(Screens.VerificationLearnMoreScreen) } onPressStart = () => { // TODO(Rossy) Use new verification screen when it's ready - navigate(Screens.VerifyVerifying) + navigate(Screens.VerificationLoadingScreen) } onPressSkip = () => { + this.setState({ isModalVisible: true }) + } + + onPressSkipCancel = () => { + this.setState({ isModalVisible: false }) + } + + onPressSkipConfirm = () => { // TODO(Rossy) mark verificaiton as skipped so app doesn't come back to this screen // navigateReset? navigate(Screens.WalletHome) @@ -61,6 +80,23 @@ class VerificationEducationScreen extends React.Component { testID="VerificationEducationSkip" /> + + + {t('skipModal.header')} + {t('skipModal.body1')} + + {t('skipModal.body2')} + + + + {t('global:cancel')} + + + {t('global:skip')} + + + + ) } @@ -88,6 +124,28 @@ const styles = StyleSheet.create({ textAlign: 'center', marginBottom: 20, }, + modalContainer: { + backgroundColor: colors.background, + padding: 20, + borderRadius: 4, + }, + modalButtonsContainer: { + marginTop: 20, + flexDirection: 'row', + alignItems: 'center', + justifyContent: 'space-evenly', + }, + modalCancelText: { + ...fontStyles.body, + ...fontStyles.semiBold, + paddingRight: 20, + }, + modalSkipText: { + ...fontStyles.body, + ...fontStyles.semiBold, + color: colors.celoGreen, + paddingLeft: 20, + }, }) export default componentWithAnalytics( diff --git a/packages/mobile/src/verify/VerificationLoadingScreen.tsx b/packages/mobile/src/verify/VerificationLoadingScreen.tsx index e49f469da88..51c9c7812e9 100644 --- a/packages/mobile/src/verify/VerificationLoadingScreen.tsx +++ b/packages/mobile/src/verify/VerificationLoadingScreen.tsx @@ -1,34 +1,110 @@ import colors from '@celo/react-components/styles/colors' +import { fontStyles } from '@celo/react-components/styles/fonts' import * as React from 'react' import { withNamespaces, WithNamespaces } from 'react-i18next' -import { ScrollView, StyleSheet } from 'react-native' +import { ScrollView, StyleSheet, Text, View } from 'react-native' import SafeAreaView from 'react-native-safe-area-view' +import { connect } from 'react-redux' +import { hideAlert, showError } from 'src/alert/actions' +import { errorSelector } from 'src/alert/reducer' import componentWithAnalytics from 'src/analytics/wrapper' +import { ErrorMessages } from 'src/app/ErrorMessages' +import CancelButton from 'src/components/CancelButton' import Carousel, { CarouselItem } from 'src/components/Carousel' import DevSkipButton from 'src/components/DevSkipButton' import { Namespaces } from 'src/i18n' +import LoadingSpinner from 'src/icons/LoadingSpinner' +import NuxLogo from 'src/icons/NuxLogo' +import { + cancelVerification, + receiveAttestationMessage, + startVerification, +} from 'src/identity/actions' +import { AttestationCode } from 'src/identity/verification' import { Screens } from 'src/navigator/Screens' +import { RootState } from 'src/redux/reducers' +import { currentAccountSelector } from 'src/web3/selectors' -class VerificationLoadingScreen extends React.Component { - static navigationOptions = null +interface StateProps { + numberVerified: boolean + e164Number: string + account: string | null + attestationCodes: AttestationCode[] + numCompleteAttestations: number + verificationFailed: boolean + underlyingError: ErrorMessages | null | undefined +} + +interface DispatchProps { + startVerification: typeof startVerification + cancelVerification: typeof cancelVerification + receiveVerificationMessage: typeof receiveAttestationMessage + showError: typeof showError + hideAlert: typeof hideAlert +} + +type Props = StateProps & DispatchProps & WithNamespaces + +const mapDispatchToProps = { + startVerification, + cancelVerification, + receiveVerificationMessage: receiveAttestationMessage, + showError, + hideAlert, +} + +const mapStateToProps = (state: RootState): StateProps => { + return { + numberVerified: state.app.numberVerified, + e164Number: state.account.e164PhoneNumber, + attestationCodes: state.identity.attestationCodes, + numCompleteAttestations: state.identity.numCompleteAttestations, + verificationFailed: state.identity.verificationFailed, + account: currentAccountSelector(state), + underlyingError: errorSelector(state), + } +} + +class VerificationLoadingScreen extends React.Component { + static navigationOptions = { header: null } + + onCancel = () => { + // TODO + } render() { - // const { t } = this.props + const { verificationFailed, e164Number, t } = this.props + const items: CarouselItem[] = [ { - text: 'Test 1', + icon: , + text: t('loading.card1'), }, { - text: 'Test 2', + icon: , + text: t('loading.card2'), }, { - text: 'Test 3', + icon: , + text: t('loading.card3'), }, ] return ( + {!verificationFailed && ( + + + + )} + + + + {t('loading.verifyingNumber', { number: e164Number })} + + {t('loading.keepOpen')} + @@ -44,16 +120,38 @@ const styles = StyleSheet.create({ }, scrollContainer: { flex: 1, - padding: 30, - paddingTop: 0, + paddingTop: 60, + alignItems: 'center', + justifyContent: 'space-between', + }, + buttonCancelContainer: { + position: 'absolute', + top: 5, + left: 0, + zIndex: 10, + }, + statusContainer: { alignItems: 'center', justifyContent: 'center', }, + textPhoneNumber: { + ...fontStyles.body, + ...fontStyles.semiBold, + marginTop: 20, + }, + textOpenTip: { + ...fontStyles.body, + marginTop: 5, + }, + carouselContainer: { - marginVertical: 30, + marginVertical: 20, }, }) export default componentWithAnalytics( - withNamespaces(Namespaces.nuxVerification2)(VerificationLoadingScreen) + connect( + mapStateToProps, + mapDispatchToProps + )(withNamespaces(Namespaces.nuxVerification2)(VerificationLoadingScreen)) ) diff --git a/packages/mobile/src/verify/__snapshots__/VerificationEducationScreen.test.tsx.snap b/packages/mobile/src/verify/__snapshots__/VerificationEducationScreen.test.tsx.snap index 7c007410779..c9a5c3da8b7 100644 --- a/packages/mobile/src/verify/__snapshots__/VerificationEducationScreen.test.tsx.snap +++ b/packages/mobile/src/verify/__snapshots__/VerificationEducationScreen.test.tsx.snap @@ -301,5 +301,751 @@ exports[`VerificationEducationScreen renders correctly 1`] = ` + + + + + + skipModal.header + + + skipModal.body1 + + + skipModal.body2 + + + + + global:cancel + + + + + global:skip + + + + + + + +`; + +exports[`VerificationEducationScreen renders correctly 2`] = ` + + + + + + + + education.header + + + education.body1 + + + education.body2 + + + + education.learnMore + + + + + + + + + education.start + + + + + + + + + education.skip + + + + + + + + + + skipModal.header + + + skipModal.body1 + + + skipModal.body2 + + + + + global:cancel + + + + + global:skip + + + + + + `; diff --git a/packages/mobile/src/verify/__snapshots__/VerificationLoadingScreen.test.tsx.snap b/packages/mobile/src/verify/__snapshots__/VerificationLoadingScreen.test.tsx.snap index e9acab24dd7..9563347b7b3 100644 --- a/packages/mobile/src/verify/__snapshots__/VerificationLoadingScreen.test.tsx.snap +++ b/packages/mobile/src/verify/__snapshots__/VerificationLoadingScreen.test.tsx.snap @@ -10,14 +10,62 @@ exports[`VerificationLoadingScreen renders correctly 1`] = ` } } > + + + + cancel + + + @@ -53,14 +101,80 @@ exports[`VerificationLoadingScreen renders correctly 1`] = ` + + + + + loading.verifyingNumber + + + loading.keepOpen + + + diff --git a/packages/verifier/package.json b/packages/verifier/package.json index 0e8618a8294..cbcc8d75062 100644 --- a/packages/verifier/package.json +++ b/packages/verifier/package.json @@ -49,7 +49,6 @@ "react-native-fs": "^2.14.1", "react-native-gesture-handler": "^1.4.1", "react-native-languages": "^3.0.2", - "react-native-modal-dropdown": "^0.6.2", "react-native-reanimated": "^1.3.0", "react-native-restart-android": "^0.0.7", "react-native-safe-area-context": "^0.5.0", diff --git a/patches/react-native-modal-dropdown+0.6.2.patch b/patches/react-native-modal-dropdown+0.6.2.patch deleted file mode 100644 index 9bf5d7530ac..00000000000 --- a/patches/react-native-modal-dropdown+0.6.2.patch +++ /dev/null @@ -1,9 +0,0 @@ -patch-package ---- a/node_modules/react-native-modal-dropdown/index.js -+++ b/node_modules/react-native-modal-dropdown/index.js -@@ -4,4 +4,3 @@ - - import ModalDropdown from './components/ModalDropdown'; - export default ModalDropdown; -\ No newline at end of file --module.exports = ModalDropdown; diff --git a/yarn.lock b/yarn.lock index bf4ea74db6f..86c309f3013 100644 --- a/yarn.lock +++ b/yarn.lock @@ -11211,7 +11211,7 @@ create-hmac@^1.1.0, create-hmac@^1.1.2, create-hmac@^1.1.4, create-hmac@^1.1.7: safe-buffer "^5.0.1" sha.js "^2.4.8" -create-react-class@*, create-react-class@^15.6.2, create-react-class@^15.6.3: +create-react-class@^15.6.2, create-react-class@^15.6.3: version "15.6.3" resolved "https://registry.yarnpkg.com/create-react-class/-/create-react-class-15.6.3.tgz#2d73237fb3f970ae6ebe011a9e66f46dbca80036" integrity sha512-M+/3Q6E6DLO6Yx3OwrWjwHBnvfXXYA7W+dFjt/ZDBemHO1DDZhsalX/NUtnTYclN6GfnBDRh4qRHjcDHmlJBJg== @@ -11754,6 +11754,11 @@ decompress@^4.0.0: pify "^2.3.0" strip-dirs "^2.0.0" +dedent@^0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/dedent/-/dedent-0.6.0.tgz#0e6da8f0ce52838ef5cec5c8f9396b0c1b64a3cb" + integrity sha1-Dm2o8M5Sg471zsXI+TlrDBtko8s= + dedent@^0.7.0: version "0.7.0" resolved "https://registry.yarnpkg.com/dedent/-/dedent-0.7.0.tgz#2495ddbaf6eb874abb0e1be9df22d2e5a544326c" @@ -11987,16 +11992,6 @@ deprecated-decorator@^0.1.6: resolved "https://registry.yarnpkg.com/deprecated-decorator/-/deprecated-decorator-0.1.6.tgz#00966317b7a12fe92f3cc831f7583af329b86c37" integrity sha1-AJZjF7ehL+kvPMgx91g68ym4bDc= -deprecated-react-native-listview@0.0.5: - version "0.0.5" - resolved "https://registry.yarnpkg.com/deprecated-react-native-listview/-/deprecated-react-native-listview-0.0.5.tgz#fc8a6dc45b0a8ba611e6014e13b38d6d763e763f" - integrity sha512-Cy7nDdd+KU+nR3tY1BSMuoZpsYC6OVSZyAiUSTDBop2lIgzCseDx7XI57x6h+NXer/8aor2yiQDQfeFcmBMwgQ== - dependencies: - create-react-class "*" - fbjs "*" - invariant "*" - react-clone-referenced-element "*" - deprecation@^2.0.0: version "2.3.1" resolved "https://registry.yarnpkg.com/deprecation/-/deprecation-2.3.1.tgz#6368cbdb40abf3373b525ac87e4a260c3a700919" @@ -14532,20 +14527,6 @@ fbjs-scripts@^1.1.0: semver "^5.1.0" through2 "^2.0.0" -fbjs@*, fbjs@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/fbjs/-/fbjs-1.0.0.tgz#52c215e0883a3c86af2a7a776ed51525ae8e0a5a" - integrity sha512-MUgcMEJaFhCaF1QtWGnmq9ZDRAzECTCRAF7O6UZIlAlkTs1SasiX9aP0Iw7wfD2mJ7wDTNfg2w7u5fSCwJk1OA== - dependencies: - core-js "^2.4.1" - fbjs-css-vars "^1.0.0" - isomorphic-fetch "^2.1.1" - loose-envify "^1.0.0" - object-assign "^4.1.0" - promise "^7.1.1" - setimmediate "^1.0.5" - ua-parser-js "^0.7.18" - fbjs@^0.8.0, fbjs@^0.8.9: version "0.8.16" resolved "https://registry.yarnpkg.com/fbjs/-/fbjs-0.8.16.tgz#5e67432f550dc41b572bf55847b8aca64e5337db" @@ -14572,6 +14553,20 @@ fbjs@^0.8.16, fbjs@^0.8.4: setimmediate "^1.0.5" ua-parser-js "^0.7.18" +fbjs@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/fbjs/-/fbjs-1.0.0.tgz#52c215e0883a3c86af2a7a776ed51525ae8e0a5a" + integrity sha512-MUgcMEJaFhCaF1QtWGnmq9ZDRAzECTCRAF7O6UZIlAlkTs1SasiX9aP0Iw7wfD2mJ7wDTNfg2w7u5fSCwJk1OA== + dependencies: + core-js "^2.4.1" + fbjs-css-vars "^1.0.0" + isomorphic-fetch "^2.1.1" + loose-envify "^1.0.0" + object-assign "^4.1.0" + promise "^7.1.1" + setimmediate "^1.0.5" + ua-parser-js "^0.7.18" + fclone@^1.0.11: version "1.0.11" resolved "https://registry.yarnpkg.com/fclone/-/fclone-1.0.11.tgz#10e85da38bfea7fc599341c296ee1d77266ee640" @@ -18218,7 +18213,7 @@ into-stream@^3.1.0: from2 "^2.1.1" p-is-promise "^1.1.0" -invariant@*, invariant@2.2.4, invariant@^2.2.2, invariant@^2.2.4: +invariant@2.2.4, invariant@^2.2.2, invariant@^2.2.4: version "2.2.4" resolved "https://registry.yarnpkg.com/invariant/-/invariant-2.2.4.tgz#610f3c92c9359ce1db616e538008d23ff35158e6" integrity sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA== @@ -21185,6 +21180,21 @@ loose-envify@^1.0.0, loose-envify@^1.1.0, loose-envify@^1.3.1, loose-envify@^1.4 dependencies: js-tokens "^3.0.0 || ^4.0.0" +lottie-ios@3.1.3, lottie-ios@^3.1.3: + version "3.1.3" + resolved "https://registry.yarnpkg.com/lottie-ios/-/lottie-ios-3.1.3.tgz#dfa18a3a7e66e5d4a6665bf0a2392d143d15661a" + integrity sha512-FKSx9l5Ekwm1Wt/ncoCwvsq8NAb1nylzMFlxrHixLYNBtO2eCQet+vwQag+74Nc/E9Lp3DKkBUCyBfz+zjtmAw== + +lottie-react-native@^3.2.1: + version "3.2.1" + resolved "https://registry.yarnpkg.com/lottie-react-native/-/lottie-react-native-3.2.1.tgz#0b2b13b03a2cda9ece8474b9cf633d0fbe78d82b" + integrity sha512-dmOySV+qgFrQszCY+7uOZR0XkgbP2FXgCWJZ2h39GqlxJZRHD2uCmXoT+WxaBGw83Gk/2KfIQ1MQMFL8UqC2bw== + dependencies: + invariant "^2.2.2" + lottie-ios "^3.1.3" + prop-types "^15.5.10" + react-native-safe-modules "^1.0.0" + lottie-web@^5.5.7: version "5.5.7" resolved "https://registry.yarnpkg.com/lottie-web/-/lottie-web-5.5.7.tgz#a8341487be1c31915fb3a784c3ec25fd6c9468df" @@ -26338,11 +26348,6 @@ react-autowhatever@^10.1.2: react-themeable "^1.1.0" section-iterator "^2.0.0" -react-clone-referenced-element@*: - version "1.1.0" - resolved "https://registry.yarnpkg.com/react-clone-referenced-element/-/react-clone-referenced-element-1.1.0.tgz#9cdda7f2aeb54fea791f3ab8c6ab96c7a77d0158" - integrity sha512-FKOsfKbBkPxYE8576EM6uAfHC4rnMpLyH6/TJUL4WcHUEB3EUn8AxPjnnV/IiwSSzsClvHYK+sDELKN/EJ0WYg== - react-debounce-input@^3.2.0: version "3.2.0" resolved "https://registry.yarnpkg.com/react-debounce-input/-/react-debounce-input-3.2.0.tgz#6978c6061d898f549f40417fb0d2ebbecf50aaaa" @@ -26500,12 +26505,12 @@ react-native-android-open-settings@^1.3.0: resolved "https://registry.yarnpkg.com/react-native-android-open-settings/-/react-native-android-open-settings-1.3.0.tgz#c6257f3c88ada62945fffe887933a2a828adf45d" integrity sha512-h4FTWRtTLRVNS7RK4Bg2gAXe8G5bFZL8Jtmfk2rG2a/N/wJR+v1rAY2BxRkC48lQTqwQCIAQRbWgLVkJ8XmaLQ== -react-native-animatable@^1.3.1: - version "1.3.2" - resolved "https://registry.yarnpkg.com/react-native-animatable/-/react-native-animatable-1.3.2.tgz#4783ee1a73dc98815aef234ce6b819f80bfe7d80" - integrity sha512-rmah3KQ63ft8DxkzFUwJSuZeq+oSYwldoGF4DTOR5WM2WR5wiWLgBAtrAHlI3Di3by323uOR21s+MlqPcHz2Kw== +react-native-animatable@1.3.3: + version "1.3.3" + resolved "https://registry.yarnpkg.com/react-native-animatable/-/react-native-animatable-1.3.3.tgz#a13a4af8258e3bb14d0a9d839917e9bb9274ec8a" + integrity sha512-2ckIxZQAsvWn25Ho+DK3d1mXIgj7tITkrS4pYDvx96WyOttSvzzFeQnM2od0+FUMzILbdHDsDEqZvnz1DYNQ1w== dependencies: - prop-types "^15.5.10" + prop-types "^15.7.2" react-native-autocomplete-input@^4.1.0: version "4.1.0" @@ -26663,28 +26668,13 @@ react-native-mail@^4.0.0: resolved "https://registry.yarnpkg.com/react-native-mail/-/react-native-mail-4.0.0.tgz#f98fe5f99001e3d47d8bd45bbe1c70d81705fd22" integrity sha512-G6lLrWFj0JwKeMR/rXCFQu7dXLm1tbG0nnezMt9xTRXikvKFbz0BMp6JICOb/wO4VJf+FkCEoRHuouPEA1dPww== -react-native-modal-dropdown@^0.6.2: - version "0.6.2" - resolved "https://registry.yarnpkg.com/react-native-modal-dropdown/-/react-native-modal-dropdown-0.6.2.tgz#da6027b3546d2031fbce46242bffc95561e13599" - integrity sha512-1jzByA+ME+q0mZJ2rZrLsf6jVwJhNxNHdg7I50xYZ9oYJsmmKwF9r/ks95tZxxks4Zvd7/FnaveNtyd+uevXAw== - dependencies: - prop-types "^15.6.0" - -react-native-modal-dropdown@^0.7.0: - version "0.7.0" - resolved "https://registry.yarnpkg.com/react-native-modal-dropdown/-/react-native-modal-dropdown-0.7.0.tgz#d030c4159ff026bedd5c20aa59b146495b860814" - integrity sha512-h2UrozBByQhL56XDboj/wjc/5Ny787eLQ++4ql7TecBdbLqbf+tlE62VeXKz30XVMN3iUVYUR/XmM/RIwLIXEg== - dependencies: - deprecated-react-native-listview "0.0.5" - prop-types "^15.6.0" - -react-native-modal@^11.4.0: - version "11.4.0" - resolved "https://registry.yarnpkg.com/react-native-modal/-/react-native-modal-11.4.0.tgz#29f2fd241aed864f3c7ce25a941498ebe2ab217a" - integrity sha512-5AsvppV843bGBOgRotORhb0OJLAf3EOueJ1Z0a9vH/ZO3E2KdnGpxgJFRWx2SZhgUmQ65YP392rsMZl/NYwdIw== +react-native-modal@^11.5.1: + version "11.5.1" + resolved "https://registry.yarnpkg.com/react-native-modal/-/react-native-modal-11.5.1.tgz#ed94e4317d9d6c4233349b928cd15334f0446345" + integrity sha512-4qIoXfFvvRzKW4ynR3H3uyJ+VleLa6qqXC6T2fZmZxl19Q2YRhv5woVvqVA2Xz6IulxVrBUGWFzGlKogp1TK6Q== dependencies: prop-types "^15.6.2" - react-native-animatable "^1.3.1" + react-native-animatable "1.3.3" react-native-ntp-client@0.5.5, react-native-ntp-client@^1.0.0: version "1.0.0" @@ -26755,6 +26745,13 @@ react-native-safe-area-view@^1.0.0: dependencies: hoist-non-react-statics "^2.3.1" +react-native-safe-modules@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/react-native-safe-modules/-/react-native-safe-modules-1.0.0.tgz#10a918adf97da920adb1e33e0c852b1e80123b65" + integrity sha512-ShT8duWBT30W4OFcltZl+UvpPDikZFURvLDQqAsrvbyy6HzWPGJDCpdqM+6GqzPPs4DPEW31YfMNmdJcZ6zI2w== + dependencies: + dedent "^0.6.0" + "react-native-screens@^1.0.0 || ^1.0.0-alpha", react-native-screens@^1.0.0-alpha.23: version "1.0.0-alpha.23" resolved "https://registry.yarnpkg.com/react-native-screens/-/react-native-screens-1.0.0-alpha.23.tgz#25d7ea4d11bda4fcde2d1da7ae50271c6aa636e0" @@ -26795,6 +26792,11 @@ react-native-share@^2.0.0: resolved "https://registry.yarnpkg.com/react-native-share/-/react-native-share-2.0.0.tgz#4543a746013e0d8fa9f765fb8937007cfbcffcf7" integrity sha512-J8Xl3mq0L9KDFtSYtKsQDAnZWw/niZIpAD1PRiNfZFHo44Rc+oS2bEIhskNnoQXKEgBNdPzCl/DenMXYAHXRYg== +react-native-sms@^1.9.0: + version "1.9.0" + resolved "https://registry.yarnpkg.com/react-native-sms/-/react-native-sms-1.9.0.tgz#362d94f75664d45144433d820155d9da66adf053" + integrity sha512-qq5v5rClCSMwTZL5TAK3P89SOQFrBwa6LyqCQV1Md0p78cS+DQWTX/wnb1H8gH32gDzr7barKTEXZPN7lZ5fCA== + react-native-snap-carousel@^3.8.4: version "3.8.4" resolved "https://registry.yarnpkg.com/react-native-snap-carousel/-/react-native-snap-carousel-3.8.4.tgz#4c749c6a714d46ed82de527233850cb5da41bc62" @@ -26802,11 +26804,6 @@ react-native-snap-carousel@^3.8.4: dependencies: prop-types "^15.6.1" react-addons-shallow-compare "15.6.2" - -react-native-sms@^1.9.0: - version "1.9.0" - resolved "https://registry.yarnpkg.com/react-native-sms/-/react-native-sms-1.9.0.tgz#362d94f75664d45144433d820155d9da66adf053" - integrity sha512-qq5v5rClCSMwTZL5TAK3P89SOQFrBwa6LyqCQV1Md0p78cS+DQWTX/wnb1H8gH32gDzr7barKTEXZPN7lZ5fCA== react-native-splash-screen@^3.2.0: version "3.2.0"