Skip to content

Commit 91afdc6

Browse files
committed
feat: add qr code to register nano contract
1 parent 1eaef90 commit 91afdc6

File tree

4 files changed

+151
-3
lines changed

4 files changed

+151
-3
lines changed

src/App.js

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,7 @@ import ShowPushNotificationTxDetails from './components/ShowPushNotificationTxDe
9090
import { NanoContractDetailsScreen } from './screens/NanoContract/NanoContractDetailsScreen';
9191
import { NanoContractTransactionScreen } from './screens/NanoContract/NanoContractTransactionScreen';
9292
import { NanoContractRegisterScreen } from './screens/NanoContract/NanoContractRegisterScreen';
93+
import { NanoContractRegisterQrCodeScreen } from './screens/NanoContractRegisterQrCodeScreen';
9394

9495
/**
9596
* This Stack Navigator is exhibited when there is no wallet initialized on the local storage.
@@ -286,6 +287,72 @@ const RegisterTokenStack = ({ navigation }) => {
286287
);
287288
};
288289

290+
/**
291+
* Stack of screens dedicated to the nano contract registration process
292+
*/
293+
const RegisterNanoContractStack = ({ navigation }) => {
294+
const Stack = createStackNavigator();
295+
const dispatch = useDispatch();
296+
const isCameraAvailable = useSelector((state) => state.isCameraAvailable);
297+
298+
/**
299+
* Defines which screen will be the initial one, according to app camera permissions
300+
* @param {null|boolean} cameraStatus
301+
* @returns {string} Route name
302+
*/
303+
const decideRouteByCameraAvailablity = (cameraStatus) => {
304+
switch (isCameraAvailable) {
305+
case true:
306+
return 'NanoContractRegisterQrCodeScreen';
307+
case false:
308+
return 'NanoContractRegisterScreen';
309+
default:
310+
return 'RegisterCameraPermissionScreen';
311+
}
312+
};
313+
314+
// Initial screen set on component initial rendering
315+
const [initialRoute, setInitialRoute] = useState(
316+
decideRouteByCameraAvailablity(isCameraAvailable)
317+
);
318+
319+
/*
320+
* Request camera permission on initialization only if permission is not already set
321+
*/
322+
useEffect(() => {
323+
if (isCameraAvailable === null) {
324+
dispatch(requestCameraPermission());
325+
}
326+
}, []);
327+
328+
// Listen to camera permission changes from user input and navigate to the relevant screen
329+
useEffect(() => {
330+
const newScreenName = decideRouteByCameraAvailablity(isCameraAvailable);
331+
332+
// Navigator screen already correct: no further action.
333+
if (initialRoute === newScreenName) {
334+
return;
335+
}
336+
337+
// Set initial route and navigate there according to new permission set
338+
setInitialRoute(newScreenName);
339+
navigation.replace(newScreenName);
340+
}, [isCameraAvailable]);
341+
342+
return (
343+
<Stack.Navigator
344+
initialRouteName={initialRoute}
345+
screenOptions={{
346+
headerShown: false,
347+
}}
348+
>
349+
<Stack.Screen name='RegisterCameraPermissionScreen' component={CameraPermissionScreen} />
350+
<Stack.Screen name='NanoContractRegisterQrCodeScreen' component={NanoContractRegisterQrCodeScreen} />
351+
<Stack.Screen name='NanoContractRegisterScreen' component={NanoContractRegisterScreen} />
352+
</Stack.Navigator>
353+
);
354+
};
355+
289356
const tabBarIconMap = {
290357
Home: 'icDashboard',
291358
Send: 'icSend',
@@ -397,6 +464,8 @@ const AppStack = () => {
397464
/>
398465
<Stack.Screen name='PaymentRequestDetail' component={PaymentRequestDetail} />
399466
<Stack.Screen name='RegisterToken' component={RegisterTokenStack} />
467+
<Stack.Screen name='RegisterNanoContract' component={RegisterNanoContractStack} />
468+
<Stack.Screen name='Register' component={RegisterTokenStack} />
400469
<Stack.Screen name='ChangeToken' component={ChangeToken} />
401470
<Stack.Screen name={NetworkSettingsFlowNav} component={NetworkSettingsFlowStack} />
402471
<Stack.Screen

src/components/NanoContract/RegisterNewNanoContractButton.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ import SimpleButton from '../SimpleButton';
1414
export const RegisterNanoContract = () => {
1515
const navigation = useNavigation();
1616
const navigatesToRegisterNanoContract = () => {
17-
navigation.navigate('NanoContractRegisterScreen');
17+
navigation.navigate('RegisterNanoContract');
1818
};
1919

2020
return (

src/screens/NanoContract/NanoContractRegisterScreen.js

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import {
2121
useSelector
2222
} from 'react-redux';
2323
import { t } from 'ttag';
24+
import QRCode from 'react-native-qrcode-svg';
2425
import HathorHeader from '../../components/HathorHeader';
2526
import { CircleInfoIcon } from '../../components/Icons/CircleInfo.icon';
2627
import NewHathorButton from '../../components/NewHathorButton';
@@ -61,7 +62,9 @@ function validate(formModel) {
6162
return invalidModel;
6263
}
6364

64-
export function NanoContractRegisterScreen({ navigation }) {
65+
export function NanoContractRegisterScreen({ navigation, route }) {
66+
const ncIdFromQrCode = route.params?.ncId;
67+
6568
const dispatch = useDispatch();
6669
const { address, error } = useSelector((state) => state.firstAddress);
6770
const registerState = useSelector((state) => ({
@@ -116,7 +119,7 @@ export function NanoContractRegisterScreen({ navigation }) {
116119
const { ncId } = formModel;
117120
dispatch(nanoContractRegisterRequest({ address, ncId }));
118121
},
119-
[formModel]
122+
[formModel, address]
120123
);
121124

122125
const handleFeedbackModalDismiss = () => {
@@ -129,6 +132,11 @@ export function NanoContractRegisterScreen({ navigation }) {
129132
};
130133

131134
useEffect(() => {
135+
if (ncIdFromQrCode) {
136+
// Set ncId in the input when given
137+
handleInputChange('ncId')(ncIdFromQrCode);
138+
}
139+
132140
if (!address) {
133141
dispatch(firstAddressRequest());
134142
}
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
/**
2+
* Copyright (c) Hathor Labs and its affiliates.
3+
*
4+
* This source code is licensed under the MIT license found in the
5+
* LICENSE file in the root directory of this source tree.
6+
*/
7+
8+
import React from 'react';
9+
import { View } from 'react-native';
10+
import { t } from 'ttag';
11+
12+
import HathorHeader from '../components/HathorHeader';
13+
import QRCodeReader from '../components/QRCodeReader';
14+
import SimpleButton from '../components/SimpleButton';
15+
import { COLORS } from '../styles/themes';
16+
17+
export const NanoContractRegisterQrCodeScreen = ({ navigation }) => {
18+
const onSuccess = (e) => {
19+
navigation.navigate('NanoContractRegisterScreen', { ncId: e.data });
20+
}
21+
22+
const renderHeaderRightElement = () => (
23+
<SimpleButton
24+
// translator: Used when the QR Code Scanner is opened, and user will manually
25+
// enter the information.
26+
title={t`Manual info`}
27+
onPress={() => navigation.navigate('NanoContractRegisterScreen')}
28+
/>
29+
);
30+
31+
return (
32+
<View style={{
33+
flex: 1,
34+
justifyContent: 'center',
35+
alignItems: 'center',
36+
backgroundColor: COLORS.lowContrastDetail,
37+
alignSelf: 'stretch',
38+
}}
39+
>
40+
<View style={{
41+
flex: 1,
42+
justifyContent: 'center',
43+
alignItems: 'center',
44+
alignSelf: 'stretch',
45+
}}
46+
>
47+
<HathorHeader
48+
title={t`Register Nano Contract`.toUpperCase()}
49+
onBackPress={() => navigation.pop()}
50+
rightElement={renderHeaderRightElement()}
51+
/>
52+
<View style={{
53+
flex: 1,
54+
justifyContent: 'center',
55+
alignItems: 'center',
56+
margin: 16,
57+
alignSelf: 'stretch',
58+
}}
59+
>
60+
<QRCodeReader
61+
navigation={navigation}
62+
onSuccess={onSuccess}
63+
bottomText={t`Scan the token QR code`}
64+
/>
65+
</View>
66+
</View>
67+
</View>
68+
);
69+
}
70+
71+
export default NanoContractRegisterQrCodeScreen;

0 commit comments

Comments
 (0)