diff --git a/native/navigation/route-names.js b/native/navigation/route-names.js --- a/native/navigation/route-names.js +++ b/native/navigation/route-names.js @@ -95,6 +95,8 @@ 'RelationshipListItemTooltipModal'; export const RobotextMessageTooltipModalRouteName = 'RobotextMessageTooltipModal'; +export const SecondaryDeviceQRCodeScannerRouteName = + 'SecondaryDeviceQRCodeScanner'; export const SidebarListModalRouteName = 'SidebarListModal'; export const SubchannelsListModalRouteName = 'SubchannelsListModal'; export const TabNavigatorRouteName = 'TabNavigator'; @@ -217,6 +219,7 @@ +FriendList: void, +BlockList: void, +LinkedDevices: void, + +SecondaryDeviceQRCodeScanner: void, }; export type CommunityDrawerParamList = { +TabNavigator: void }; diff --git a/native/profile/linked-devices-header-right-button.react.js b/native/profile/linked-devices-header-right-button.react.js --- a/native/profile/linked-devices-header-right-button.react.js +++ b/native/profile/linked-devices-header-right-button.react.js @@ -1,15 +1,23 @@ // @flow +import { useNavigation } from '@react-navigation/native'; import * as React from 'react'; import { Text, TouchableOpacity } from 'react-native'; +import { SecondaryDeviceQRCodeScannerRouteName } from '../navigation/route-names.js'; import { useStyles } from '../themes/colors.js'; function LinkedDevicesHeaderRightButton(): React.Node { const styles = useStyles(unboundStyles); + const navigation = useNavigation(); + + const navigateToQRCodeScanner = React.useCallback( + () => navigation.navigate(SecondaryDeviceQRCodeScannerRouteName), + [navigation], + ); return ( - + Add ); diff --git a/native/profile/profile.react.js b/native/profile/profile.react.js --- a/native/profile/profile.react.js +++ b/native/profile/profile.react.js @@ -22,6 +22,7 @@ import ProfileHeader from './profile-header.react.js'; import ProfileScreen from './profile-screen.react.js'; import RelationshipList from './relationship-list.react.js'; +import SecondaryDeviceQRCodeScanner from './secondary-device-qr-code-scanner.react.js'; import KeyboardAvoidingView from '../components/keyboard-avoiding-view.react.js'; import CommunityDrawerButton from '../navigation/community-drawer-button.react.js'; import type { CommunityDrawerNavigationProp } from '../navigation/community-drawer-navigator.react.js'; @@ -39,6 +40,7 @@ DefaultNotificationsPreferencesRouteName, BlockListRouteName, LinkedDevicesRouteName, + SecondaryDeviceQRCodeScannerRouteName, type ScreenParamList, type ProfileParamList, } from '../navigation/route-names.js'; @@ -57,6 +59,10 @@ // eslint-disable-next-line react/display-name headerRight: () => , }; +const secondaryDeviceQRCodeScannerOptions = { + headerTitle: '', + headerBackTitleVisible: false, +}; const buildInfoOptions = { headerTitle: 'Build info' }; const devToolsOptions = { headerTitle: 'Developer tools' }; const appearanceOptions = { headerTitle: 'Appearance' }; @@ -142,6 +148,11 @@ component={LinkedDevices} options={linkedDevicesOptions} /> + { + (async () => { + const { status } = await BarCodeScanner.requestPermissionsAsync(); + setHasPermission(status === 'granted'); + + if (status !== 'granted') { + Alert.alert( + 'No access to camera', + 'Please allow Comm to access your camera in order to scan the QR code.', + [{ text: 'OK' }], + ); + + navigation.goBack(); + } + })(); + }, [navigation]); + + const onConnect = React.useCallback((barCodeEvent: BarCodeEvent) => { + const { type, data } = barCodeEvent; + Alert.alert( + 'Scan successful', + `Bar code with type ${type} and data ${data} has been scanned!`, + [{ text: 'OK' }], + ); + }, []); + + const onCancelScan = React.useCallback(() => setScanned(false), []); + + const handleBarCodeScanned = React.useCallback( + (barCodeEvent: BarCodeEvent) => { + setScanned(true); + Alert.alert( + 'Connect with this device?', + 'Are you sure you want to allow this device to log in to your account?', + [ + { + text: 'Cancel', + style: 'cancel', + onPress: onCancelScan, + }, + { + text: 'Connect', + onPress: () => onConnect(barCodeEvent), + }, + ], + { cancelable: false }, + ); + }, + [onCancelScan, onConnect], + ); + + if (hasPermission === null) { + return ; + } + + // Note: According to the BarCodeScanner Expo docs, we should adhere to two + // guidances when using the BarCodeScanner: + // 1. We should specify the potential barCodeTypes we want to scan for to + // minimize battery usage. + // 2. We should set the onBarCodeScanned callback to undefined if it scanned + // in order to 'pause' the scanner from continuing to scan while we + // process the data from the scan. + // See: https://docs.expo.io/versions/latest/sdk/bar-code-scanner + return ( + + + + ); +} + +const unboundStyles = { + container: { + flex: 1, + flexDirection: 'column', + justifyContent: 'center', + }, + scanner: { + position: 'absolute', + top: 0, + left: 0, + right: 0, + bottom: 0, + }, +}; + +export default SecondaryDeviceQRCodeScanner;