Skip to content

Commit

Permalink
[Wallet] New camera permission flow (#1398)
Browse files Browse the repository at this point in the history
  • Loading branch information
jeanregisser authored and celo-ci-bot-user committed Oct 18, 2019
1 parent 43c9ebf commit 5d7db19
Show file tree
Hide file tree
Showing 8 changed files with 247 additions and 87 deletions.
7 changes: 5 additions & 2 deletions packages/mobile/locales/en-US/sendFlow7.json
Original file line number Diff line number Diff line change
Expand Up @@ -55,8 +55,11 @@
"scanCode": "Scan Code",
"writeStorageNeededForQrDownload":
"Storage write permission is needed for downloading the QR code",
"ScanCodeByPlacingItInTheBox": "Scan code by placing it in the box",
"needCameraPermissionToScan": "App needs camera permission to scan QR codes",
"cameraScanInfo": "Scan code by placing it in the box",
"cameraNotAuthorizedTitle": "Enable Camera",
"cameraNotAuthorizedDescription":
"Please enable the Camera in your phone’s Settings. You’ll need it to scan QR codes.",
"cameraSettings": "Settings",
"showYourQRCode": "Show your QR code",
"toSentOrRequestPayment": "to send or request payment",
"requestSent": "Request Sent",
Expand Down
8 changes: 5 additions & 3 deletions packages/mobile/locales/es-419/sendFlow7.json
Original file line number Diff line number Diff line change
Expand Up @@ -55,9 +55,11 @@
"scanCode": "Escanear código",
"writeStorageNeededForQrDownload":
"Se necesita permiso de escritura de almacenamiento para descargar el código QR",
"ScanCodeByPlacingItInTheBox": "Escanee el código colocándolo en la caja",
"needCameraPermissionToScan":
"La aplicación necesita permiso de la cámara para escanear códigos QR",
"cameraScanInfo": "Escanee el código colocándolo en la caja",
"cameraNotAuthorizedTitle": "Habilitar Cámara",
"cameraNotAuthorizedDescription":
"Habilite la cámara en la configuración de su teléfono. Lo necesitará para escanear códigos QR.",
"cameraSettings": "Configuraciones",
"showYourQRCode": "Muestra tu código QR",
"toSentOrRequestPayment": "enviar o solicitar pago",
"requestSent": "Solicitud Enviada",
Expand Down
11 changes: 11 additions & 0 deletions packages/mobile/src/qrcode/NotAuthorizedView.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import * as React from 'react'
import { render } from 'react-native-testing-library'
import NotAuthorizedView from 'src/qrcode/NotAuthorizedView'

describe('NotAuthorizedView', () => {
it('renders correctly', () => {
const { toJSON } = render(<NotAuthorizedView />)

expect(toJSON()).toMatchSnapshot()
})
})
53 changes: 53 additions & 0 deletions packages/mobile/src/qrcode/NotAuthorizedView.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import Button, { BtnTypes } from '@celo/react-components/components/Button'
import fontStyles from '@celo/react-components/styles/fonts'
import React, { useCallback } from 'react'
import { withNamespaces, WithNamespaces } from 'react-i18next'
import { Platform, StyleSheet, Text, View } from 'react-native'
import * as AndroidOpenSettings from 'react-native-android-open-settings'
import { Namespaces } from 'src/i18n'
import { navigateToURI } from 'src/utils/linking'

type Props = WithNamespaces

function NotAuthorizedView({ t }: Props) {
const onPressSettings = useCallback(() => {
if (Platform.OS === 'ios') {
navigateToURI('app-settings:')
} else if (Platform.OS === 'android') {
AndroidOpenSettings.appDetailsSettings()
}
}, [])

return (
<View style={styles.container}>
<Text style={styles.title}>{t('cameraNotAuthorizedTitle')}</Text>
<Text style={styles.description}>{t('cameraNotAuthorizedDescription')}</Text>
<Button
onPress={onPressSettings}
text={t('cameraSettings')}
standard={false}
type={BtnTypes.SECONDARY}
/>
</View>
)
}

const styles = StyleSheet.create({
container: {
flex: 1,
alignItems: 'center',
justifyContent: 'center',
paddingHorizontal: 30,
},
title: {
...fontStyles.h2,
...fontStyles.bold,
},
description: {
marginTop: 10,
...fontStyles.body,
textAlign: 'center',
},
})

export default withNamespaces(Namespaces.sendFlow7)(NotAuthorizedView)
113 changes: 49 additions & 64 deletions packages/mobile/src/qrcode/QRScanner.tsx
Original file line number Diff line number Diff line change
@@ -1,25 +1,22 @@
import Button, { BtnTypes } from '@celo/react-components/components/Button'
import QRCode from '@celo/react-components/icons/QRCode'
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 { WithNamespaces, withNamespaces } from 'react-i18next'
import { StyleSheet, Text, TouchableOpacity, View } from 'react-native'
import { Platform, StyleSheet, Text, View } from 'react-native'
import { RNCamera } from 'react-native-camera'
import SafeAreaView from 'react-native-safe-area-view'
import { NavigationFocusInjectedProps, withNavigationFocus } from 'react-navigation'
import { connect } from 'react-redux'
import { componentWithAnalytics } from 'src/analytics/wrapper'
import i18n, { Namespaces } from 'src/i18n'
import { headerWithBackButton } from 'src/navigator/Headers'
import { navigate } from 'src/navigator/NavigationService'
import { Screens } from 'src/navigator/Screens'
import NotAuthorizedView from 'src/qrcode/NotAuthorizedView'
import { handleBarcodeDetected } from 'src/send/actions'
import Logger from 'src/utils/Logger'
import { requestCameraPermission } from 'src/utils/permissions'

enum BarcodeTypes {
QR_CODE = 'QR_CODE',
}

interface DispatchProps {
handleBarcodeDetected: typeof handleBarcodeDetected
Expand All @@ -40,24 +37,11 @@ class QRScanner extends React.Component<Props> {
camera: RNCamera | null = null

state = {
camera: false,
qrSubmitted: false,
}

async componentDidMount() {
const { t } = this.props
const cameraPermission = await requestCameraPermission()

if (!cameraPermission) {
Logger.showMessage(t('needCameraPermissionToScan'))
navigate(Screens.QRCode)
return
}
this.setState({ camera: true, qrSubmitted: false })
}

onBardCodeDetected = (rawData: any) => {
if (rawData.type === BarcodeTypes.QR_CODE && !this.state.qrSubmitted) {
if (!this.state.qrSubmitted) {
this.setState({ qrSubmitted: true }, () => {
this.props.handleBarcodeDetected(rawData)
})
Expand All @@ -67,9 +51,9 @@ class QRScanner extends React.Component<Props> {
render() {
const { t } = this.props
return (
<View style={styles.container}>
{this.state.camera &&
this.props.isFocused && (
<SafeAreaView style={styles.container}>
<View style={styles.innerContainer}>
{(Platform.OS !== 'android' || this.props.isFocused) && (
<RNCamera
ref={(ref) => {
this.camera = ref
Expand All @@ -82,32 +66,41 @@ class QRScanner extends React.Component<Props> {
flashMode={RNCamera.Constants.FlashMode.auto}
captureAudio={false}
autoFocus={RNCamera.Constants.AutoFocus.on}
// Passing null here since we want the default system message
// @ts-ignore
androidCameraPermissionOptions={null}
notAuthorizedView={<NotAuthorizedView />}
>
<View style={styles.view}>
<View style={styles.viewFillVertical} />
<View style={styles.viewCameraRow}>
<View style={styles.viewFillHorizontal} />
<View style={styles.viewCameraContainer}>
<View style={styles.fillVertical} />
<View style={styles.cameraRow}>
<View style={styles.fillHorizontal} />
<View style={styles.cameraContainer}>
<View style={styles.camera} />
<Text style={[fontStyles.bodySmall, styles.viewInfoBox]}>
{t('ScanCodeByPlacingItInTheBox')}
</Text>
<View style={styles.infoBox}>
<Text style={styles.infoText}>{t('cameraScanInfo')}</Text>
</View>
</View>
<View style={styles.viewFillHorizontal} />
</View>
<View style={styles.viewFillVertical} />
</View>
<View style={styles.footerContainer}>
<View style={styles.footerIcon}>
<QRCode />
<View style={styles.fillHorizontal} />
</View>
<TouchableOpacity onPress={goToQrCodeScreen}>
<Text style={styles.footerText}> {t('showYourQRCode')} </Text>
</TouchableOpacity>
<View style={styles.fillVertical} />
</View>
</RNCamera>
)}
</View>
</View>
<View style={styles.footerContainer}>
<Button
onPress={goToQrCodeScreen}
text={t('showYourQRCode')}
standard={false}
type={BtnTypes.SECONDARY}
>
<View style={styles.footerIcon}>
<QRCode />
</View>
</Button>
</View>
</SafeAreaView>
)
}
}
Expand All @@ -116,6 +109,9 @@ const styles = StyleSheet.create({
container: {
flex: 1,
},
innerContainer: {
flex: 1,
},
preview: {
flex: 1,
justifyContent: 'flex-end',
Expand All @@ -125,61 +121,50 @@ const styles = StyleSheet.create({
height: 200,
width: 200,
borderRadius: 4,
zIndex: 99,
},
view: {
flex: 1,
position: 'absolute',
top: 0,
left: 0,
right: 0,
bottom: 0,
alignItems: 'center',
justifyContent: 'center',
},
viewFillVertical: {
fillVertical: {
backgroundColor: 'rgba(46, 51, 56, 0.3)',
width: variables.width,
flex: 1,
},
viewFillHorizontal: {
fillHorizontal: {
backgroundColor: 'rgba(46, 51, 56, 0.3)',
flex: 1,
},
viewCameraRow: {
cameraRow: {
display: 'flex',
flexDirection: 'row',
},
viewCameraContainer: {
cameraContainer: {
height: 200,
},
viewInfoBox: {
infoBox: {
paddingVertical: 9,
paddingHorizontal: 5,
backgroundColor: colors.dark,
opacity: 1,
marginTop: 15,
borderRadius: 3,
},
infoText: {
...fontStyles.bodySmall,
lineHeight: undefined,
color: colors.white,
zIndex: 99,
},
footerContainer: {
height: 50,
width: variables.width,
backgroundColor: 'white',
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'center',
textAlign: 'center',
backgroundColor: colors.background,
},
footerIcon: {
borderWidth: 1,
borderRadius: 15,
borderColor: colors.celoGreen,
padding: 4,
},
footerText: {
color: colors.celoGreen,
},
})

export default componentWithAnalytics(
Expand Down
Loading

0 comments on commit 5d7db19

Please sign in to comment.