From 7ff12f5186ed2afb54c2c0e6481ea199526c272e Mon Sep 17 00:00:00 2001 From: sirpy Date: Sun, 24 Nov 2024 12:46:03 +0200 Subject: [PATCH 1/7] add: mvp age verification --- .../components/AgeCheckError.jsx | 94 +++++++++++++++++++ .../components/Instructions.jsx | 58 ++++++++---- .../hooks/useFaceTecVerification.js | 2 + .../faceVerification/screens/ErrorScreen.jsx | 4 +- .../faceVerification/screens/IntroScreen.jsx | 33 +++++-- .../screens/VerificationScreen.jsx | 6 +- .../components/CameraPermissionDialog.jsx | 2 +- src/lib/analytics/constants.js | 1 + 8 files changed, 172 insertions(+), 28 deletions(-) create mode 100644 src/components/faceVerification/components/AgeCheckError.jsx diff --git a/src/components/faceVerification/components/AgeCheckError.jsx b/src/components/faceVerification/components/AgeCheckError.jsx new file mode 100644 index 0000000000..bc36cc6e9c --- /dev/null +++ b/src/components/faceVerification/components/AgeCheckError.jsx @@ -0,0 +1,94 @@ +import React, { useEffect } from 'react' +import { View } from 'react-native' +import { t } from '@lingui/macro' + +import Text from '../../common/view/Text' +import Separator from '../../common/layout/Separator' +import { Section } from '../../common' +import FaceVerificationErrorSmiley from '../../common/animations/FaceVerificationErrorSmiley' + +import { isMobileOnly } from '../../../lib/utils/platform' +import { getDesignRelativeHeight, getDesignRelativeWidth } from '../../../lib/utils/sizes' +import { withStyles } from '../../../lib/styles' + +import { fireEvent, FV_AGECHECKERROR } from '../../../lib/analytics/analytics' + +const AgeCheckError = ({ styles, displayTitle, onRetry, nav, exception }) => { + useEffect(() => { + if (!exception) { + return + } + + fireEvent(FV_AGECHECKERROR) + }, []) + + return ( +
+ + {displayTitle} + {(displayTitle ? `,\n` : '') + + t`Unfortunately, + it seems your are under 18`} + + + + + + + + + +
+ + + + {t`Only persons 18 years or older can use our service`} + + + {t`If you think this is a mistake + please contact our support`} + + + +
+
+ ) +} + +const getStylesFromProps = ({ theme }) => { + return { + halfIllustration: { + marginTop: isMobileOnly ? getDesignRelativeHeight(25) : 0, + marginBottom: isMobileOnly ? getDesignRelativeHeight(30) : 0, + width: getDesignRelativeWidth(130, false), + maxHeight: isMobileOnly ? getDesignRelativeHeight(97) : 'auto', + display: 'flex', + justifyContent: 'center', + marginRight: 0, + marginLeft: 0, + }, + descriptionContainer: { + flex: 1, + marginBottom: 0, + paddingBottom: getDesignRelativeHeight(theme.sizes.defaultDouble), + paddingLeft: getDesignRelativeWidth(theme.sizes.default), + paddingRight: getDesignRelativeWidth(theme.sizes.default), + paddingTop: getDesignRelativeHeight(theme.sizes.default), + width: '100%', + }, + actionsSpace: { + marginBottom: getDesignRelativeHeight(16), + }, + errorSection: { + paddingBottom: 0, + paddingTop: 0, + marginBottom: 0, + }, + descriptionWrapper: { + paddingTop: getDesignRelativeHeight(25), + paddingBottom: getDesignRelativeHeight(25), + }, + } +} + +export default withStyles(getStylesFromProps)(AgeCheckError) diff --git a/src/components/faceVerification/components/Instructions.jsx b/src/components/faceVerification/components/Instructions.jsx index b95e3318c2..56852b635f 100644 --- a/src/components/faceVerification/components/Instructions.jsx +++ b/src/components/faceVerification/components/Instructions.jsx @@ -12,7 +12,7 @@ import { CustomButton, Section, Wrapper } from '../../common' import { getDesignRelativeHeight, getDesignRelativeWidth, isLargeDevice } from '../../../lib/utils/sizes' import normalize from '../../../lib/utils/normalizeText' import { withStyles } from '../../../lib/styles' -import { isBrowser } from '../../../lib/utils/platform' +import { isBrowser, isMobile } from '../../../lib/utils/platform' // assets import illustration from '../../../assets/FRInstructions.png' @@ -32,26 +32,41 @@ const Dot = () => ( ) -const Instructions = ({ styles, onDismiss = noop, ready }) => ( +const Instructions = ({ styles, onDismiss = noop, ready, fvStarted = false }) => (
- - - - {t`Hold Your Camera at Eye Level`} - - - - {t`Light Your Face Evenly`} - - - - {t`Avoid Smiling & Back Light`} - - + {!fvStarted ? ( + + {!isMobile && ( + + + {t`Face Directly In Front of the Camera`} + + )} + + + {t`Hold Your Camera at Eye Level`} + + + + {t`Light Your Face Evenly`} + + + + {t`Avoid Smiling & Back Light`} + + + ) : ( + + {t`Notice: do not continue if this is not your account`} + {t`Doing this for someone else may result in lose of funds, and your account being blocked`} + + )} ({ flexDirection: 'column', alignItems: 'flex-start', }, + warnDescriptionWrapper: { + flexDirection: 'column', + alignItems: 'center', + }, descriptionWrapperB: { backgroundColor: theme.colors.darkGray, borderRadius: 8, @@ -136,6 +155,13 @@ const getStylesFromProps = ({ theme }) => ({ fontSize: normalize(isLargeDevice ? 22 : 20), lineHeight: isLargeDevice ? 36 : 34, }, + warnText: { + // textAlign: 'center', + fontSize: normalize(isLargeDevice ? 22 : 20), + lineHeight: isLargeDevice ? 36 : 34, + fontWeight: 'bold', + color: 'red', + }, textB: { textAlign: 'left', fontSize: 16, diff --git a/src/components/faceVerification/hooks/useFaceTecVerification.js b/src/components/faceVerification/hooks/useFaceTecVerification.js index 3befb7182c..276260290a 100644 --- a/src/components/faceVerification/hooks/useFaceTecVerification.js +++ b/src/components/faceVerification/hooks/useFaceTecVerification.js @@ -125,6 +125,8 @@ export default (options = null) => { name = 'DuplicateFoundError' } else if (/face.+n.t\s+match/.test(message)) { name = 'NotMatchError' + } else if (/age check failed/.test(message)) { + name = 'AgeCheckError' } else { // the following code is needed to categorize exceptions // then we could display specific error messages diff --git a/src/components/faceVerification/screens/ErrorScreen.jsx b/src/components/faceVerification/screens/ErrorScreen.jsx index 89af6804d1..e27fc5b2bf 100644 --- a/src/components/faceVerification/screens/ErrorScreen.jsx +++ b/src/components/faceVerification/screens/ErrorScreen.jsx @@ -9,6 +9,7 @@ import NotMatchError from '../components/NotMatchError' import GeneralError from '../components/GeneralError' import UnrecoverableError from '../components/UnrecoverableError' import SwitchToAnotherDevice from '../components/SwitchToAnotherDevice' +import AgeCheckError from '../components/AgeCheckError' import useVerificationAttempts from '../hooks/useVerificationAttempts' @@ -55,7 +56,7 @@ const ErrorScreen = ({ styles, screenProps, navigation }) => { return getFirstWord(fullName) }, [profile]) - const onRetry = useCallback(() => screenProps.navigateTo('FaceVerificationIntro'), [screenProps]) + const onRetry = useCallback(() => screenProps.navigateTo('FaceVerification'), [screenProps]) useEffectOnce(() => { // determining error component to display @@ -97,6 +98,7 @@ const ErrorScreen = ({ styles, screenProps, navigation }) => { } ErrorScreen.kindOfTheIssue = { + AgeCheckError, NotMatchError, UnrecoverableError, DuplicateFoundError, diff --git a/src/components/faceVerification/screens/IntroScreen.jsx b/src/components/faceVerification/screens/IntroScreen.jsx index 5cb9cd8e9d..2a5bf22d39 100644 --- a/src/components/faceVerification/screens/IntroScreen.jsx +++ b/src/components/faceVerification/screens/IntroScreen.jsx @@ -110,7 +110,7 @@ const IntroReVerification = ({ styles, firstName, ready, onVerify, onLearnMore } ) -const Intro = ({ styles, firstName, ready, onVerify, onLearnMore }) => ( +const Intro = ({ styles, firstName, ready, onVerify, onLearnMore, onDeny }) => (
@@ -127,7 +127,7 @@ const Intro = ({ styles, firstName, ready, onVerify, onLearnMore }) => ( lineHeight={25} letterSpacing={0.18} fontWeight="700" - >{t`To claim G$, you need to be a unique human and prove it with your camera.`} + >{t`To continue, you need to be a unique human and prove it with your camera.`} {t`Your image is only used to ensure you’re you and prevent duplicate accounts.`} @@ -144,9 +144,19 @@ const Intro = ({ styles, firstName, ready, onVerify, onLearnMore }) => ( - - {t`OK, VERIFY ME`} - + + + {t`I'M OVER 18, CONTINUE`} + + onDeny(`not 18 or didn't accept`)} + disabled={!ready} + mode="outlined" + > + {t`I Don't agree Or I'M NOT OVER 18`} + +
@@ -160,7 +170,6 @@ const IntroScreen = ({ styles, screenProps, navigation }) => { const goodWallet = useWallet() const { account } = goodWallet ?? {} const [expiryDate, , state] = useIdentityExpiryDate(externalAccount || account) - const isReverify = expiryDate?.lastAuthenticated?.isZero() === false const { goToRoot, navigateTo, push } = screenProps @@ -171,6 +180,13 @@ const IntroScreen = ({ styles, screenProps, navigation }) => { [isFVFlow, firstName, fullName], ) + const onDeny = useCallback( + reason => { + return isFVFlow ? fvRedirect(false, reason) : goToRoot() + }, + [isFVFlow], + ) + const [disposing, checkDisposalState] = useDisposingState( { requestOnMounted: false, @@ -182,14 +198,14 @@ const IntroScreen = ({ styles, screenProps, navigation }) => { } const dialogData = showQueueDialog(WalletDeletedPopupText, true, { - onDismiss: isFVFlow ? () => fvRedirect(false, 'Wait 24 hours') : goToRoot, + onDismiss: () => onDeny('Wait 24 hours'), imageSource: Wait24HourSVG, }) showDialog(dialogData) }, }, - [enrollmentIdentifier], + [enrollmentIdentifier, onDeny], ) const openPrivacy = useOnPress(() => openLink(Config.faceVerificationPrivacyUrl), []) @@ -267,6 +283,7 @@ const IntroScreen = ({ styles, screenProps, navigation }) => { firstName={userName} onLearnMore={openPrivacy} onVerify={handleVerifyClick} + onDeny={onDeny} ready={false === disposing} /> ) diff --git a/src/components/faceVerification/screens/VerificationScreen.jsx b/src/components/faceVerification/screens/VerificationScreen.jsx index cba6882264..11c173d152 100644 --- a/src/components/faceVerification/screens/VerificationScreen.jsx +++ b/src/components/faceVerification/screens/VerificationScreen.jsx @@ -1,4 +1,4 @@ -import React, { useCallback, useContext, useMemo } from 'react' +import React, { useCallback, useContext, useMemo, useState } from 'react' import { identity } from 'lodash' @@ -33,6 +33,7 @@ import AsyncStorage from '../../../lib/utils/asyncStorage' const log = logger.child({ from: 'FaceVerification' }) const FaceVerification = ({ screenProps, navigation }) => { + const [fvStarted, setFVStarted] = useState() const { attemptsCount, trackAttempt, resetAttempts } = useVerificationAttempts() const goodWallet = useWallet() const userStorage = useUserStorage() @@ -192,6 +193,7 @@ const FaceVerification = ({ screenProps, navigation }) => { } fireEvent(FV_START) + setFVStarted(true) startVerification() }, [startVerification, enrollmentIdentifier]) @@ -205,7 +207,7 @@ const FaceVerification = ({ screenProps, navigation }) => { // othwerise page will stuck on 'loading' "GOT IT" button useFVLoginInfoCheck(navigation) - return + return } export default FaceVerification diff --git a/src/components/permissions/components/CameraPermissionDialog.jsx b/src/components/permissions/components/CameraPermissionDialog.jsx index eae612cb00..da4e911e5b 100644 --- a/src/components/permissions/components/CameraPermissionDialog.jsx +++ b/src/components/permissions/components/CameraPermissionDialog.jsx @@ -10,7 +10,7 @@ export default ({ onDismiss }) => { return ( Date: Wed, 27 Nov 2024 08:24:05 +0100 Subject: [PATCH 2/7] fix: button overlapping picture on android --- src/components/faceVerification/screens/IntroScreen.jsx | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/components/faceVerification/screens/IntroScreen.jsx b/src/components/faceVerification/screens/IntroScreen.jsx index 2a5bf22d39..07bef64874 100644 --- a/src/components/faceVerification/screens/IntroScreen.jsx +++ b/src/components/faceVerification/screens/IntroScreen.jsx @@ -25,6 +25,7 @@ import { getFirstWord } from '../../../lib/utils/getFirstWord' import { getDesignRelativeHeight, getDesignRelativeWidth, + isLargeDevice, isMediumDevice, isSmallDevice, } from '../../../lib/utils/sizes' @@ -144,8 +145,8 @@ const Intro = ({ styles, firstName, ready, onVerify, onLearnMore, onDeny }) => ( - - + + {t`I'M OVER 18, CONTINUE`} ({ color: theme.colors.primary, marginTop: getDesignRelativeHeight(isSmallDevice ? theme.sizes.defaultDouble : 20), }, + buttonContainer: { + marginTop: !isLargeDevice ? 50 : 0, + }, }) export default withStyles(getStylesFromProps)(IntroScreen) From e30ae6d08963beba7c784bef65d2db17d61daeec Mon Sep 17 00:00:00 2001 From: sirpy Date: Wed, 27 Nov 2024 16:35:15 +0200 Subject: [PATCH 3/7] fix: remove wrongfuly used deps --- .../faceVerification/screens/IntroScreen.jsx | 35 +++++++++---------- 1 file changed, 16 insertions(+), 19 deletions(-) diff --git a/src/components/faceVerification/screens/IntroScreen.jsx b/src/components/faceVerification/screens/IntroScreen.jsx index 07bef64874..45196a917e 100644 --- a/src/components/faceVerification/screens/IntroScreen.jsx +++ b/src/components/faceVerification/screens/IntroScreen.jsx @@ -188,26 +188,23 @@ const IntroScreen = ({ styles, screenProps, navigation }) => { [isFVFlow], ) - const [disposing, checkDisposalState] = useDisposingState( - { - requestOnMounted: false, - enrollmentIdentifier, - fvSigner, - onComplete: isDisposing => { - if (!isDisposing) { - return - } - - const dialogData = showQueueDialog(WalletDeletedPopupText, true, { - onDismiss: () => onDeny('Wait 24 hours'), - imageSource: Wait24HourSVG, - }) - - showDialog(dialogData) - }, + const [disposing, checkDisposalState] = useDisposingState({ + requestOnMounted: false, + enrollmentIdentifier, + fvSigner, + onComplete: isDisposing => { + if (!isDisposing) { + return + } + + const dialogData = showQueueDialog(WalletDeletedPopupText, true, { + onDismiss: () => onDeny('Wait 24 hours'), + imageSource: Wait24HourSVG, + }) + + showDialog(dialogData) }, - [enrollmentIdentifier, onDeny], - ) + }) const openPrivacy = useOnPress(() => openLink(Config.faceVerificationPrivacyUrl), []) const openFaceVerification = useCallback(() => push('FaceVerification'), [push]) From 792273190610fdb3780244ae2e4e9a30c646d731 Mon Sep 17 00:00:00 2001 From: sirpy Date: Sun, 1 Dec 2024 16:54:54 +0200 Subject: [PATCH 4/7] add: show duplicate id expiration date on web --- .../components/DuplicateFoundError.jsx | 26 +++++++++++++------ .../sdk/EnrollmentProcessor.web.js | 8 +++--- .../sdk/ProcessingSubscriber.web.js | 4 +-- 3 files changed, 25 insertions(+), 13 deletions(-) diff --git a/src/components/faceVerification/components/DuplicateFoundError.jsx b/src/components/faceVerification/components/DuplicateFoundError.jsx index a04dfadac8..8bba18d0c7 100644 --- a/src/components/faceVerification/components/DuplicateFoundError.jsx +++ b/src/components/faceVerification/components/DuplicateFoundError.jsx @@ -1,6 +1,8 @@ import React, { useEffect } from 'react' import { View } from 'react-native' import { t } from '@lingui/macro' +import { get } from 'lodash' +import moment from 'moment' import Text from '../../common/view/Text' import { Section } from '../../common' @@ -13,6 +15,8 @@ import FVErrorTwinSVG from '../../../assets/FaceVerification/FVErrorTwin.svg' import { fireEvent, FV_DUPLICATEERROR } from '../../../lib/analytics/analytics' const DuplicateFoundError = ({ styles, displayTitle, onRetry, nav, exception }) => { + const expiration = get(exception, 'response.enrollmentResult.duplicate.expiration') + useEffect(() => { if (!exception) { return @@ -35,17 +39,23 @@ const DuplicateFoundError = ({ styles, displayTitle, onRetry, nav, exception })
- - - {t`You can open ONLY ONE account + + {t`You can open ONLY ONE account per person. `} - - - {t`If this is your only active - account - please contact our support`} - + + + {t`If this is your only active + account - please contact our support.`} + {expiration && ( + + + {t`The conflicting identity will expire on ${moment(expiration).format('l')}. + You will be able to get verified then.`} + + + )}
diff --git a/src/components/faceVerification/sdk/EnrollmentProcessor.web.js b/src/components/faceVerification/sdk/EnrollmentProcessor.web.js index 5d4d9af7ef..9843ec678d 100644 --- a/src/components/faceVerification/sdk/EnrollmentProcessor.web.js +++ b/src/components/faceVerification/sdk/EnrollmentProcessor.web.js @@ -16,6 +16,8 @@ export class EnrollmentProcessor { lastMessage = null + lastResponse = null + enrollmentIdentifier = null v1Identifier = null @@ -51,7 +53,7 @@ export class EnrollmentProcessor { async onFaceTecSDKCompletelyDone() { const FaceTecSDK = await this.FaceTecSDK const { FaceTecSessionStatus, getFriendlyDescriptionForFaceTecSessionStatus } = FaceTecSDK - const { subscriber, isSuccess, lastMessage, lastResult } = this + const { subscriber, isSuccess, lastMessage, lastResult, lastResponse = {} } = this const { status } = lastResult || {} let latestMessage = lastMessage @@ -65,7 +67,7 @@ export class EnrollmentProcessor { } // calling completion callback - subscriber.onSessionCompleted(isSuccess, lastResult, latestMessage) + subscriber.onSessionCompleted(isSuccess, lastResult, latestMessage, lastResponse) } /** @@ -159,7 +161,7 @@ export class EnrollmentProcessor { // setting lastMessage from exception's message // if response was sent - it will contain message from server this.lastMessage = message - + this.lastResponse = response if (response) { // if error response was sent const { enrollmentResult } = response diff --git a/src/components/faceVerification/sdk/ProcessingSubscriber.web.js b/src/components/faceVerification/sdk/ProcessingSubscriber.web.js index cc6f7a7e09..4ee45f9e34 100644 --- a/src/components/faceVerification/sdk/ProcessingSubscriber.web.js +++ b/src/components/faceVerification/sdk/ProcessingSubscriber.web.js @@ -17,7 +17,7 @@ export class ProcessingSubscriber { return this._promise } - onSessionCompleted(isSuccess, lastResult, lastMessage) { + onSessionCompleted(isSuccess, lastResult, lastMessage, lastResponse = {}) { const { logger, _resolve, _reject } = this const logRecord = { isSuccess, lastMessage } @@ -33,7 +33,7 @@ export class ProcessingSubscriber { } const exception = new Error(lastMessage) - + exception.response = lastResponse if (lastResult) { exception.code = lastResult.status } From cbfebf337d07d80dfba20678430d445a8d475407 Mon Sep 17 00:00:00 2001 From: sirpy Date: Sun, 1 Dec 2024 17:59:36 +0200 Subject: [PATCH 5/7] add: try again to giveup screen --- .../faceVerification/components/ErrorButtons.jsx | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/components/faceVerification/components/ErrorButtons.jsx b/src/components/faceVerification/components/ErrorButtons.jsx index 4918d7f85d..9bf48cee22 100644 --- a/src/components/faceVerification/components/ErrorButtons.jsx +++ b/src/components/faceVerification/components/ErrorButtons.jsx @@ -26,7 +26,12 @@ const ErrorButtons = ({ styles, screenProps, navigation, onRetry, reachedMax })
) : ( - + + + + TRY AGAIN + + )}
) From d5da3999a34dabb3aa24a45e08748448e0831fbd Mon Sep 17 00:00:00 2001 From: sirpy Date: Tue, 3 Dec 2024 10:38:58 +0200 Subject: [PATCH 6/7] add: design changes --- src/components/common/buttons/CheckBox.jsx | 30 ++++- .../common/buttons/CheckBox.native.jsx | 26 ----- .../components/AgeCheckError.jsx | 12 +- .../components/ErrorButtons.jsx | 9 +- .../components/Instructions.jsx | 7 +- .../faceVerification/screens/ErrorScreen.jsx | 7 +- .../faceVerification/screens/IntroScreen.jsx | 104 +++++++++--------- .../standalone/hooks/useGiveUpDialog.jsx | 22 ++-- src/config/config.js | 3 +- src/lib/utils/platform.jsx | 2 +- 10 files changed, 113 insertions(+), 109 deletions(-) delete mode 100644 src/components/common/buttons/CheckBox.native.jsx diff --git a/src/components/common/buttons/CheckBox.jsx b/src/components/common/buttons/CheckBox.jsx index be52c64d85..a5e48fff58 100644 --- a/src/components/common/buttons/CheckBox.jsx +++ b/src/components/common/buttons/CheckBox.jsx @@ -1,10 +1,30 @@ import React from 'react' +import { View } from 'react-native' +import { CheckBox as WebCheckBox } from 'react-native-web' +import RNCheckBox from '@react-native-community/checkbox' +import { isMobileNative } from '../../../lib/utils/platform' +import { withStyles } from '../../../lib/styles' -const CheckBox = ({ onClick, children }) => ( - +
) -export default CheckBox +const mapStylesToProps = () => ({ + container: { + flexDirection: 'row', + alignItems: 'center', + }, + checkbox: { + width: 24, + height: 24, + marginRight: 8, + }, +}) + +export default withStyles(mapStylesToProps)(CheckBox) + +// export default CheckBox diff --git a/src/components/common/buttons/CheckBox.native.jsx b/src/components/common/buttons/CheckBox.native.jsx deleted file mode 100644 index acf5af73c4..0000000000 --- a/src/components/common/buttons/CheckBox.native.jsx +++ /dev/null @@ -1,26 +0,0 @@ -import React from 'react' -import { TouchableOpacity } from 'react-native' -import { default as RNCheckBox } from '@react-native-community/checkbox' - -import { withStyles } from '../../../lib/styles' - -const CheckBox = ({ onClick, value, styles, children }) => ( - - - {children} - -) - -const mapStylesToProps = () => ({ - container: { - flexDirection: 'row', - alignItems: 'center', - marginBottom: 24, - }, - checkbox: { - width: 24, - height: 24, - }, -}) - -export default withStyles(mapStylesToProps)(CheckBox) diff --git a/src/components/faceVerification/components/AgeCheckError.jsx b/src/components/faceVerification/components/AgeCheckError.jsx index bc36cc6e9c..98b22847e8 100644 --- a/src/components/faceVerification/components/AgeCheckError.jsx +++ b/src/components/faceVerification/components/AgeCheckError.jsx @@ -25,15 +25,9 @@ const AgeCheckError = ({ styles, displayTitle, onRetry, nav, exception }) => { return (
- {displayTitle} - {(displayTitle ? `,\n` : '') + - t`Unfortunately, - it seems your are under 18`} + {t`You must be 18 years or older to get verified`} - - - - + @@ -42,7 +36,7 @@ const AgeCheckError = ({ styles, displayTitle, onRetry, nav, exception }) => { - {t`Only persons 18 years or older can use our service`} + {t`It seems your are under 18 years of age`} {t`If you think this is a mistake diff --git a/src/components/faceVerification/components/ErrorButtons.jsx b/src/components/faceVerification/components/ErrorButtons.jsx index 9bf48cee22..eaa2d30264 100644 --- a/src/components/faceVerification/components/ErrorButtons.jsx +++ b/src/components/faceVerification/components/ErrorButtons.jsx @@ -11,17 +11,17 @@ import { withStyles } from '../../../lib/styles' const { fvTypeformUrl } = Config -const ErrorButtons = ({ styles, screenProps, navigation, onRetry, reachedMax }) => { +const ErrorButtons = ({ styles, screenProps, navigation, onRetry, reachedMax, invert = false }) => { const onContactSupport = useCallback(() => openLink(fvTypeformUrl), []) return ( {!reachedMax ? ( - - + + TRY AGAIN - + {t`CONTACT SUPPORT`} @@ -42,6 +42,7 @@ const getStylesFromProps = ({ theme }) => ({ width: '100%', }, actionsSpace: { + marginTop: 10, //native design fix marginBottom: getDesignRelativeHeight(16), }, }) diff --git a/src/components/faceVerification/components/Instructions.jsx b/src/components/faceVerification/components/Instructions.jsx index 56852b635f..25c552222a 100644 --- a/src/components/faceVerification/components/Instructions.jsx +++ b/src/components/faceVerification/components/Instructions.jsx @@ -61,10 +61,11 @@ const Instructions = ({ styles, onDismiss = noop, ready, fvStarted = false }) => ) : ( - {t`Notice: do not continue if this is not your account`} + {t`Notice: + Face verifying for someone else's use is against the terms & service policy.`} {t`Doing this for someone else may result in lose of funds, and your account being blocked`} + >{t`Doing so may result in a loss of funds and/or your account being blocked.`} )} @@ -160,7 +161,7 @@ const getStylesFromProps = ({ theme }) => ({ fontSize: normalize(isLargeDevice ? 22 : 20), lineHeight: isLargeDevice ? 36 : 34, fontWeight: 'bold', - color: 'red', + color: theme.colors.red, }, textB: { textAlign: 'left', diff --git a/src/components/faceVerification/screens/ErrorScreen.jsx b/src/components/faceVerification/screens/ErrorScreen.jsx index e27fc5b2bf..3b2db0e30a 100644 --- a/src/components/faceVerification/screens/ErrorScreen.jsx +++ b/src/components/faceVerification/screens/ErrorScreen.jsx @@ -91,7 +91,12 @@ const ErrorScreen = ({ styles, screenProps, navigation }) => { isFVFlow={isFVFlow} reachedMax={reachedMax} /> - + ) diff --git a/src/components/faceVerification/screens/IntroScreen.jsx b/src/components/faceVerification/screens/IntroScreen.jsx index 45196a917e..6756a8a907 100644 --- a/src/components/faceVerification/screens/IntroScreen.jsx +++ b/src/components/faceVerification/screens/IntroScreen.jsx @@ -1,5 +1,5 @@ // libraries -import React, { useCallback, useContext, useEffect, useMemo } from 'react' +import React, { useCallback, useContext, useEffect, useMemo, useState } from 'react' import { ActivityIndicator, Image, Platform, View } from 'react-native' import { t } from '@lingui/macro' import { useIdentityExpiryDate } from '@gooddollar/web3sdk-v2' @@ -49,6 +49,7 @@ import FashionShootSVG from '../../../assets/FaceVerification/FashionPhotoshoot. import BillyVerifies from '../../../assets/billy-verifies.png' import useProfile from '../../../lib/userStorage/useProfile' import useFVLoginInfoCheck from '../standalone/hooks/useFVLoginInfoCheck' +import CheckBox from '../../common/buttons/CheckBox' const log = logger.child({ from: 'FaceVerificationIntro' }) @@ -111,57 +112,62 @@ const IntroReVerification = ({ styles, firstName, ready, onVerify, onLearnMore } ) -const Intro = ({ styles, firstName, ready, onVerify, onLearnMore, onDeny }) => ( - -
- - - {firstName ? `${firstName},` : ``} - - {firstName ? `\n` : ''} - {t`You are almost there!`} - {`\n`} +const Intro = ({ styles, firstName, ready, onVerify, onLearnMore }) => { + const [ageConfirmed, setAgeConfirmed] = useState(false) + return ( + +
+ + + {firstName ? `${firstName},` : ``} + + {firstName ? `\n` : ''} + {t`You are almost there!`} + {`\n`} + + + {t`To continue, you need to be a unique human and prove it with your camera.`} + + {t`Your image is only used to ensure you’re you and prevent duplicate accounts.`} - - {t`To continue, you need to be a unique human and prove it with your camera.`} - - {t`Your image is only used to ensure you’re you and prevent duplicate accounts.`} - - - {t`Learn More`} - - - - - - - {t`I'M OVER 18, CONTINUE`} - - onDeny(`not 18 or didn't accept`)} - disabled={!ready} - mode="outlined" + - {t`I Don't agree Or I'M NOT OVER 18`} - + {t`Learn More`} + + + + + + + { + setAgeConfirmed(v) + }} + value={ageConfirmed} + > + I confirm I'm over 18 years of age + + + + {t`OK, Verify me`} + + - -
-
-) +
+
+ ) +} const IntroScreen = ({ styles, screenProps, navigation }) => { const { fullName } = useProfile() diff --git a/src/components/faceVerification/standalone/hooks/useGiveUpDialog.jsx b/src/components/faceVerification/standalone/hooks/useGiveUpDialog.jsx index 51ab38abd5..b0c101c324 100644 --- a/src/components/faceVerification/standalone/hooks/useGiveUpDialog.jsx +++ b/src/components/faceVerification/standalone/hooks/useGiveUpDialog.jsx @@ -16,13 +16,13 @@ const useGiveUpDialog = (navigation, type) => { const fvRedirect = useFVRedirect() const { isFVFlow } = useContext(FVFlowContext) const { navigate } = navigation - const { fvTypeformUrl } = Config + const { fvTypeformUrl, showFVSurvey } = Config const onReasonChosen = useCallback( (reason = undefined) => { const data = pickBy({ reason }, negate(isUndefined)) - if (reason !== 'closed') { + if (reason && reason !== 'closed') { AsyncStorage.removeItem('hasStartedFV') fireEvent(FV_GIVEUP, { data, surveyType: type }) } @@ -43,14 +43,16 @@ const useGiveUpDialog = (navigation, type) => { ) const onGiveUp = useCallback(() => { - showDialog({ - content: , - isMinHeight: false, - showButtons: false, - showCloseButtons: false, - - onDismiss: onReasonChosen, - }) + showFVSurvey + ? showDialog({ + content: , + isMinHeight: false, + showButtons: false, + showCloseButtons: false, + + onDismiss: onReasonChosen, + }) + : onReasonChosen() }, [showDialog, onReasonChosen]) return { onGiveUp } diff --git a/src/config/config.js b/src/config/config.js index 5f5b47e678..9ddee42c32 100644 --- a/src/config/config.js +++ b/src/config/config.js @@ -246,7 +246,8 @@ const Config = { fvTypeformUrl: 'https://docs.gooddollar.org/frequently-asked-questions/troubleshooting#passing-face-verification', gasFeeNotionUrl: 'https://www.notion.so/gooddollar/Why-does-it-say-I-m-Out-of-Gas-d92e5e20b6144dfbb12979e266e72959', GoodIdFeatureBranch: env.REACT_APP_GOODID_FEATURE_BRANCH === 'true', - feedContext: env.REACT_APP_FEEDCONTEXT_PROD + feedContext: env.REACT_APP_FEEDCONTEXT_PROD, + showFVSurvey: env.REACT_APP_SHOW_FV_SURVEY ?? env.REACT_APP_BUILD_TARGET !== 'FV' // default false for FV flow } global.config = Config diff --git a/src/lib/utils/platform.jsx b/src/lib/utils/platform.jsx index 05894a1207..0ca645a542 100644 --- a/src/lib/utils/platform.jsx +++ b/src/lib/utils/platform.jsx @@ -33,7 +33,7 @@ export const isAndroidNative = Platform.OS === 'android' export const isMobileNative = isIOSNative || isAndroidNative -export const isMobileOnlyNative = isMobileNative && isTablet === false +export const isMobileOnlyNative = isMobileNative && !isTablet export const isMobile = isMobileNative || isMobileWeb From 9da8cbda1415fb24dead76d47732315518551ddb Mon Sep 17 00:00:00 2001 From: sirpy Date: Wed, 4 Dec 2024 12:45:13 +0200 Subject: [PATCH 7/7] add: change duplicate messaging and add link --- .../components/DuplicateFoundError.jsx | 24 +++++++++++++++---- 1 file changed, 19 insertions(+), 5 deletions(-) diff --git a/src/components/faceVerification/components/DuplicateFoundError.jsx b/src/components/faceVerification/components/DuplicateFoundError.jsx index 8bba18d0c7..147f3529c5 100644 --- a/src/components/faceVerification/components/DuplicateFoundError.jsx +++ b/src/components/faceVerification/components/DuplicateFoundError.jsx @@ -1,9 +1,8 @@ -import React, { useEffect } from 'react' -import { View } from 'react-native' +import React, { useCallback, useEffect } from 'react' +import { Linking, View } from 'react-native' import { t } from '@lingui/macro' import { get } from 'lodash' import moment from 'moment' - import Text from '../../common/view/Text' import { Section } from '../../common' @@ -15,6 +14,10 @@ import FVErrorTwinSVG from '../../../assets/FaceVerification/FVErrorTwin.svg' import { fireEvent, FV_DUPLICATEERROR } from '../../../lib/analytics/analytics' const DuplicateFoundError = ({ styles, displayTitle, onRetry, nav, exception }) => { + const onLearnMore = useCallback(() => { + Linking.openURL('https://docs.gooddollar.org/frequently-asked-questions/troubleshooting#help-it-says-i-have-a-twin') + }, []) + const expiration = get(exception, 'response.enrollmentResult.duplicate.expiration') useEffect(() => { @@ -51,8 +54,19 @@ const DuplicateFoundError = ({ styles, displayTitle, onRetry, nav, exception }) {expiration && ( - {t`The conflicting identity will expire on ${moment(expiration).format('l')}. - You will be able to get verified then.`} + {t`The existing account with your identity will expire on ${moment(expiration).format('l')}. + After this expiry, you may create a new account or verify a different wallet.`} + + + {t`Learn More`} )}