Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add: mvp age verification #4331

Draft
wants to merge 7 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
94 changes: 94 additions & 0 deletions src/components/faceVerification/components/AgeCheckError.jsx
Original file line number Diff line number Diff line change
@@ -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 (
<Section style={styles.descriptionContainer} justifyContent="space-evenly">
<Section.Title fontWeight="medium" textTransform="none" color="red">
{displayTitle}
{(displayTitle ? `,\n` : '') +
t`Unfortunately,
it seems your are under 18`}
</Section.Title>
<Section.Row justifyContent="space-evenly">
<View style={styles.halfIllustration}>
<FaceVerificationErrorSmiley />
</View>
<View style={styles.halfIllustration}>
<FaceVerificationErrorSmiley />
</View>
</Section.Row>
<Section style={styles.errorSection}>
<Separator width={2} />
<View style={styles.descriptionWrapper}>
<Text color="primary" fontWeight="bold" fontSize={18} lineHeight={25}>
{t`Only persons 18 years or older can use our service`}
</Text>
<Text color="primary" fontSize={18} lineHeight={25}>
{t`If you think this is a mistake
please contact our support`}
</Text>
</View>
<Separator width={2} />
</Section>
</Section>
)
}

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)
58 changes: 42 additions & 16 deletions src/components/faceVerification/components/Instructions.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -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'
Expand All @@ -32,26 +32,41 @@ const Dot = () => (
</Text>
)

const Instructions = ({ styles, onDismiss = noop, ready }) => (
const Instructions = ({ styles, onDismiss = noop, ready, fvStarted = false }) => (
<Wrapper>
<Section style={styles.topContainer} grow>
<View style={styles.mainContent}>
<Image source={illustration} resizeMode="contain" style={styles.illustration} />
<View style={styles.descriptionContainer}>
<View style={styles.descriptionWrapper}>
<Text style={styles.text}>
<Dot />
{t`Hold Your Camera at Eye Level`}
</Text>
<Text style={styles.text}>
<Dot />
{t`Light Your Face Evenly`}
</Text>
<Text style={styles.text}>
<Dot />
{t`Avoid Smiling & Back Light`}
</Text>
</View>
{!fvStarted ? (
<View style={styles.descriptionWrapper}>
{!isMobile && (
<Text style={styles.text}>
<Dot />
{t`Face Directly In Front of the Camera`}
</Text>
)}
<Text style={styles.text}>
<Dot />
{t`Hold Your Camera at Eye Level`}
</Text>
<Text style={styles.text}>
<Dot />
{t`Light Your Face Evenly`}
</Text>
<Text style={styles.text}>
<Dot />
{t`Avoid Smiling & Back Light`}
</Text>
</View>
) : (
<View style={styles.warnDescriptionWrapper}>
<Text style={styles.warnText}>{t`Notice: do not continue if this is not your account`}</Text>
<Text
style={styles.warnText}
>{t`Doing this for someone else may result in lose of funds, and your account being blocked`}</Text>
</View>
)}
</View>
<CustomButton
loading={!ready}
Expand Down Expand Up @@ -123,6 +138,10 @@ const getStylesFromProps = ({ theme }) => ({
flexDirection: 'column',
alignItems: 'flex-start',
},
warnDescriptionWrapper: {
flexDirection: 'column',
alignItems: 'center',
},
descriptionWrapperB: {
backgroundColor: theme.colors.darkGray,
borderRadius: 8,
Expand All @@ -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,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
4 changes: 3 additions & 1 deletion src/components/faceVerification/screens/ErrorScreen.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -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'

Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -97,6 +98,7 @@ const ErrorScreen = ({ styles, screenProps, navigation }) => {
}

ErrorScreen.kindOfTheIssue = {
AgeCheckError,
NotMatchError,
UnrecoverableError,
DuplicateFoundError,
Expand Down
33 changes: 25 additions & 8 deletions src/components/faceVerification/screens/IntroScreen.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@
</Wrapper>
)

const Intro = ({ styles, firstName, ready, onVerify, onLearnMore }) => (
const Intro = ({ styles, firstName, ready, onVerify, onLearnMore, onDeny }) => (
<Wrapper withMaxHeight={false}>
<Section style={styles.topContainer} grow>
<View style={styles.mainContent}>
Expand All @@ -127,7 +127,7 @@
lineHeight={25}
letterSpacing={0.18}
fontWeight="700"
>{t`To claim G$, you need to be a unique human and prove it with your camera.`}</Section.Text>
>{t`To continue, you need to be a unique human and prove it with your camera.`}</Section.Text>
<Section.Text fontSize={18} lineHeight={25} letterSpacing={0.18}>
{t`Your image is only used to ensure you’re you and prevent duplicate accounts.`}
</Section.Text>
Expand All @@ -144,9 +144,19 @@
<View style={styles.illustrationContainer} marginTop={0}>
<FashionShootSVG />
</View>
<CustomButton style={[styles.button]} onPress={onVerify} disabled={!ready}>
{t`OK, VERIFY ME`}
</CustomButton>
<View>
<CustomButton style={[styles.button]} onPress={onVerify} disabled={!ready}>
{t`I'M OVER 18, CONTINUE`}
</CustomButton>
<CustomButton
style={[styles.button]}
onPress={() => onDeny(`not 18 or didn't accept`)}
disabled={!ready}
mode="outlined"
>
{t`I Don't agree Or I'M NOT OVER 18`}
</CustomButton>
</View>
</View>
</Section>
</Wrapper>
Expand All @@ -160,7 +170,6 @@
const goodWallet = useWallet()
const { account } = goodWallet ?? {}
const [expiryDate, , state] = useIdentityExpiryDate(externalAccount || account)

const isReverify = expiryDate?.lastAuthenticated?.isZero() === false

const { goToRoot, navigateTo, push } = screenProps
Expand All @@ -171,6 +180,13 @@
[isFVFlow, firstName, fullName],
)

const onDeny = useCallback(
reason => {
return isFVFlow ? fvRedirect(false, reason) : goToRoot()
},
[isFVFlow],
)

const [disposing, checkDisposalState] = useDisposingState(
{
requestOnMounted: false,
Expand All @@ -182,14 +198,14 @@
}

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],
github-advanced-security[bot] marked this conversation as resolved.
Fixed
Show resolved Hide resolved
)

const openPrivacy = useOnPress(() => openLink(Config.faceVerificationPrivacyUrl), [])
Expand Down Expand Up @@ -267,6 +283,7 @@
firstName={userName}
onLearnMore={openPrivacy}
onVerify={handleVerifyClick}
onDeny={onDeny}
ready={false === disposing}
/>
)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { useCallback, useContext, useMemo } from 'react'
import React, { useCallback, useContext, useMemo, useState } from 'react'

import { identity } from 'lodash'

Expand Down Expand Up @@ -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()
Expand Down Expand Up @@ -192,6 +193,7 @@ const FaceVerification = ({ screenProps, navigation }) => {
}

fireEvent(FV_START)
setFVStarted(true)
startVerification()
}, [startVerification, enrollmentIdentifier])

Expand All @@ -205,7 +207,7 @@ const FaceVerification = ({ screenProps, navigation }) => {
// othwerise page will stuck on 'loading' "GOT IT" button
useFVLoginInfoCheck(navigation)

return <Instructions onDismiss={verifyFace} ready={initialized} />
return <Instructions onDismiss={verifyFace} ready={initialized} fvStarted={fvStarted} />
}

export default FaceVerification
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ export default ({ onDismiss }) => {
return (
<ExplanationDialog
title={t`Enable camera access
to claim G$'s`}
to get verified`}
image={illustration}
imageHeight={128}
buttons={[
Expand Down
1 change: 1 addition & 0 deletions src/lib/analytics/constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ export const FV_GENERALERROR = 'FV_GENERALERROR'
export const FV_WRONGORIENTATION = 'FV_WRONGORIENTATION'
export const FV_DUPLICATEERROR = 'FV_DUPLICATEERROR'
export const FV_NOTMATCHERROR = 'FV_NOTMATCHERROR'
export const FV_AGECHECKERROR = 'FV_AGECHECKERROR'
export const FV_TRYAGAINLATER = 'FV_TRYAGAINLATER'
export const FV_CANTACCESSCAMERA = 'FV_CANTACCESSCAMERA'
export const FV_GIVEUP = 'FV_GIVEUP'
Expand Down
Loading