react-native-fpay 0.2.9 → 0.3.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/module/FountainPayProvider.js +5 -1
- package/lib/module/FountainPayProvider.js.map +1 -1
- package/lib/module/core/api/index.js +22 -12
- package/lib/module/core/api/index.js.map +1 -1
- package/lib/module/engine/BLEReceiverService.js.map +1 -1
- package/lib/module/engine/FPEngine.js +24 -13
- package/lib/module/engine/FPEngine.js.map +1 -1
- package/lib/module/engine/useIsForeground.js +17 -0
- package/lib/module/engine/useIsForeground.js.map +1 -0
- package/lib/module/ui/components/AnimatedDots.js +68 -0
- package/lib/module/ui/components/AnimatedDots.js.map +1 -0
- package/lib/module/ui/components/ConfirmScreen.js +333 -0
- package/lib/module/ui/components/ConfirmScreen.js.map +1 -0
- package/lib/module/ui/modals/FPPaymentRequestModal.js +6 -8
- package/lib/module/ui/modals/FPPaymentRequestModal.js.map +1 -1
- package/lib/module/ui/modals/FPShell.js +7 -4
- package/lib/module/ui/modals/FPShell.js.map +1 -1
- package/lib/module/ui/screens/ReceiveScreen.js +379 -274
- package/lib/module/ui/screens/ReceiveScreen.js.map +1 -1
- package/lib/module/ui/screens/SendScreen.js +154 -45
- package/lib/module/ui/screens/SendScreen.js.map +1 -1
- package/lib/module/ui/screens/styles.js +89 -0
- package/lib/module/ui/screens/styles.js.map +1 -0
- package/lib/module/ui/screens/sub/receivePayment/Nfc/index.js +361 -0
- package/lib/module/ui/screens/sub/receivePayment/Nfc/index.js.map +1 -0
- package/lib/module/ui/screens/sub/receivePayment/Qr/index.js +338 -0
- package/lib/module/ui/screens/sub/receivePayment/Qr/index.js.map +1 -0
- package/lib/module/ui/screens/sub/receivePayment/Transfer/index.js +453 -0
- package/lib/module/ui/screens/sub/receivePayment/Transfer/index.js.map +1 -0
- package/lib/module/ui/screens/sub/{BluetoothSubScreen.js → sendPayment/BluetoothSubScreen.js} +25 -32
- package/lib/module/ui/screens/sub/sendPayment/BluetoothSubScreen.js.map +1 -0
- package/lib/module/ui/screens/sub/sendPayment/NFCSubScreen.js +354 -0
- package/lib/module/ui/screens/sub/sendPayment/NFCSubScreen.js.map +1 -0
- package/lib/module/ui/screens/sub/sendPayment/NQRSubScreen.js +440 -0
- package/lib/module/ui/screens/sub/sendPayment/NQRSubScreen.js.map +1 -0
- package/lib/module/ui/screens/sub/{ProximitySubScreen.js → sendPayment/ProximitySubScreen.js} +20 -111
- package/lib/module/ui/screens/sub/sendPayment/ProximitySubScreen.js.map +1 -0
- package/lib/module/ui/screens/sub/sendPayment/TransferSubScreen.js +327 -0
- package/lib/module/ui/screens/sub/sendPayment/TransferSubScreen.js.map +1 -0
- package/lib/typescript/src/FountainPayProvider.d.ts.map +1 -1
- package/lib/typescript/src/core/api/index.d.ts +20 -27
- package/lib/typescript/src/core/api/index.d.ts.map +1 -1
- package/lib/typescript/src/core/types/index.d.ts +56 -13
- package/lib/typescript/src/core/types/index.d.ts.map +1 -1
- package/lib/typescript/src/engine/BLEReceiverService.d.ts +2 -0
- package/lib/typescript/src/engine/BLEReceiverService.d.ts.map +1 -1
- package/lib/typescript/src/engine/FPEngine.d.ts +3 -1
- package/lib/typescript/src/engine/FPEngine.d.ts.map +1 -1
- package/lib/typescript/src/engine/useIsForeground.d.ts +2 -0
- package/lib/typescript/src/engine/useIsForeground.d.ts.map +1 -0
- package/lib/typescript/src/ui/components/AnimatedDots.d.ts +2 -0
- package/lib/typescript/src/ui/components/AnimatedDots.d.ts.map +1 -0
- package/lib/typescript/src/ui/components/ConfirmScreen.d.ts +10 -0
- package/lib/typescript/src/ui/components/ConfirmScreen.d.ts.map +1 -0
- package/lib/typescript/src/ui/components/OtpInput/Styles.d.ts +3 -3
- package/lib/typescript/src/ui/modals/FPPaymentRequestModal.d.ts.map +1 -1
- package/lib/typescript/src/ui/modals/FPShell.d.ts.map +1 -1
- package/lib/typescript/src/ui/screens/ReceiveScreen.d.ts +2 -9
- package/lib/typescript/src/ui/screens/ReceiveScreen.d.ts.map +1 -1
- package/lib/typescript/src/ui/screens/SendScreen.d.ts +4 -2
- package/lib/typescript/src/ui/screens/SendScreen.d.ts.map +1 -1
- package/lib/typescript/src/ui/screens/styles.d.ts +1390 -0
- package/lib/typescript/src/ui/screens/styles.d.ts.map +1 -0
- package/lib/typescript/src/ui/screens/sub/receivePayment/Nfc/index.d.ts +10 -0
- package/lib/typescript/src/ui/screens/sub/receivePayment/Nfc/index.d.ts.map +1 -0
- package/lib/typescript/src/ui/screens/sub/receivePayment/Qr/index.d.ts +10 -0
- package/lib/typescript/src/ui/screens/sub/receivePayment/Qr/index.d.ts.map +1 -0
- package/lib/typescript/src/ui/screens/sub/receivePayment/Transfer/index.d.ts +5 -0
- package/lib/typescript/src/ui/screens/sub/receivePayment/Transfer/index.d.ts.map +1 -0
- package/lib/typescript/src/ui/screens/sub/{BluetoothSubScreen.d.ts → sendPayment/BluetoothSubScreen.d.ts} +2 -11
- package/lib/typescript/src/ui/screens/sub/sendPayment/BluetoothSubScreen.d.ts.map +1 -0
- package/lib/typescript/src/ui/screens/sub/sendPayment/NFCSubScreen.d.ts +3 -0
- package/lib/typescript/src/ui/screens/sub/sendPayment/NFCSubScreen.d.ts.map +1 -0
- package/lib/typescript/src/ui/screens/sub/sendPayment/NQRSubScreen.d.ts +3 -0
- package/lib/typescript/src/ui/screens/sub/sendPayment/NQRSubScreen.d.ts.map +1 -0
- package/lib/typescript/src/ui/screens/sub/{ProximitySubScreen.d.ts → sendPayment/ProximitySubScreen.d.ts} +2 -10
- package/lib/typescript/src/ui/screens/sub/sendPayment/ProximitySubScreen.d.ts.map +1 -0
- package/lib/typescript/src/ui/screens/sub/sendPayment/TransferSubScreen.d.ts +3 -0
- package/lib/typescript/src/ui/screens/sub/sendPayment/TransferSubScreen.d.ts.map +1 -0
- package/package.json +1 -1
- package/src/FountainPayProvider.tsx +7 -1
- package/src/core/api/index.ts +34 -19
- package/src/core/types/index.ts +67 -13
- package/src/engine/BLEReceiverService.ts +2 -0
- package/src/engine/FPEngine.ts +29 -14
- package/src/engine/useIsForeground.ts +18 -0
- package/src/ui/components/AnimatedDots.tsx +81 -0
- package/src/ui/components/ConfirmScreen.tsx +421 -0
- package/src/ui/modals/FPPaymentRequestModal.tsx +7 -6
- package/src/ui/modals/FPShell.tsx +9 -9
- package/src/ui/screens/ReceiveScreen.tsx +266 -115
- package/src/ui/screens/SendScreen.tsx +141 -19
- package/src/ui/screens/styles.ts +101 -0
- package/src/ui/screens/sub/receivePayment/Nfc/index.tsx +418 -0
- package/src/ui/screens/sub/receivePayment/Qr/index.tsx +391 -0
- package/src/ui/screens/sub/receivePayment/Transfer/index.tsx +512 -0
- package/src/ui/screens/sub/{BluetoothSubScreen.tsx → sendPayment/BluetoothSubScreen.tsx} +27 -46
- package/src/ui/screens/sub/sendPayment/NFCSubScreen.tsx +302 -0
- package/src/ui/screens/sub/sendPayment/NQRSubScreen.tsx +490 -0
- package/src/ui/screens/sub/{ProximitySubScreen.tsx → sendPayment/ProximitySubScreen.tsx} +24 -44
- package/src/ui/screens/sub/sendPayment/TransferSubScreen.tsx +345 -0
- package/lib/module/ui/screens/sub/BluetoothSubScreen.js.map +0 -1
- package/lib/module/ui/screens/sub/NFCSubScreen.js +0 -164
- package/lib/module/ui/screens/sub/NFCSubScreen.js.map +0 -1
- package/lib/module/ui/screens/sub/NQRSubScreen.js +0 -131
- package/lib/module/ui/screens/sub/NQRSubScreen.js.map +0 -1
- package/lib/module/ui/screens/sub/ProximitySubScreen.js.map +0 -1
- package/lib/module/ui/screens/sub/TransferSubScreen.js +0 -353
- package/lib/module/ui/screens/sub/TransferSubScreen.js.map +0 -1
- package/lib/typescript/src/ui/screens/sub/BluetoothSubScreen.d.ts.map +0 -1
- package/lib/typescript/src/ui/screens/sub/NFCSubScreen.d.ts +0 -18
- package/lib/typescript/src/ui/screens/sub/NFCSubScreen.d.ts.map +0 -1
- package/lib/typescript/src/ui/screens/sub/NQRSubScreen.d.ts +0 -12
- package/lib/typescript/src/ui/screens/sub/NQRSubScreen.d.ts.map +0 -1
- package/lib/typescript/src/ui/screens/sub/ProximitySubScreen.d.ts.map +0 -1
- package/lib/typescript/src/ui/screens/sub/TransferSubScreen.d.ts +0 -11
- package/lib/typescript/src/ui/screens/sub/TransferSubScreen.d.ts.map +0 -1
- package/src/ui/screens/sub/NFCSubScreen.tsx +0 -86
- package/src/ui/screens/sub/NQRSubScreen.tsx +0 -62
- package/src/ui/screens/sub/TransferSubScreen.tsx +0 -147
|
@@ -0,0 +1,490 @@
|
|
|
1
|
+
import React, { useEffect, useState } from 'react';
|
|
2
|
+
import { View, Text, StyleSheet, ActivityIndicator, Image, TouchableOpacity, Dimensions, Animated, Easing, Alert, Vibration, Linking } from 'react-native';
|
|
3
|
+
import { nqrAPI } from '../../../../core/api';
|
|
4
|
+
import { FPButton } from '../../../components/FPButton';
|
|
5
|
+
import { C, R, S, F, shadow } from '../../../theme';
|
|
6
|
+
import type { FPCurrency, FPError, Props } from '../../../../core/types';
|
|
7
|
+
import { Camera, useCameraDevice, useCameraDevices, useCodeScanner } from 'react-native-vision-camera';
|
|
8
|
+
import styled from 'styled-components/native';
|
|
9
|
+
import { launchImageLibrary } from 'react-native-image-picker';
|
|
10
|
+
import QRKit from 'react-native-qr-kit';
|
|
11
|
+
import Ionicons from 'react-native-vector-icons/Ionicons';
|
|
12
|
+
import { useIsForeground } from '../../../../engine/useIsForeground';
|
|
13
|
+
|
|
14
|
+
const { width, height } = Dimensions.get('window');
|
|
15
|
+
|
|
16
|
+
// Styled Components
|
|
17
|
+
const Container = styled(View)`
|
|
18
|
+
flex: 1;
|
|
19
|
+
background-color: #000000;
|
|
20
|
+
`;
|
|
21
|
+
|
|
22
|
+
const Header = styled(View)`
|
|
23
|
+
flex-direction: row;
|
|
24
|
+
align-items: center;
|
|
25
|
+
justify-content: space-between;
|
|
26
|
+
padding: 12px 16px;
|
|
27
|
+
position: absolute !important;
|
|
28
|
+
top: 40px;
|
|
29
|
+
left: 0;
|
|
30
|
+
right: 0;
|
|
31
|
+
z-index: 10;
|
|
32
|
+
elevation: 4;
|
|
33
|
+
shadow-color: #000;
|
|
34
|
+
shadow-offset: 0px 2px;
|
|
35
|
+
shadow-opacity: 0.1;
|
|
36
|
+
shadow-radius: 4px;
|
|
37
|
+
z-index: 9999999999 !important;
|
|
38
|
+
`;
|
|
39
|
+
|
|
40
|
+
const HeaderButton = styled(TouchableOpacity)`
|
|
41
|
+
padding: 8px;
|
|
42
|
+
background-color: #fff;
|
|
43
|
+
width: 40px;
|
|
44
|
+
height: 40px;
|
|
45
|
+
border-radius: 50%;
|
|
46
|
+
justify-content: center;
|
|
47
|
+
align-items: center;
|
|
48
|
+
`;
|
|
49
|
+
|
|
50
|
+
const HeaderCenter = styled(View)`
|
|
51
|
+
flex-direction: row;
|
|
52
|
+
align-items: center;
|
|
53
|
+
gap: 8px;
|
|
54
|
+
`;
|
|
55
|
+
|
|
56
|
+
const HeaderRight = styled(View)`
|
|
57
|
+
flex-direction: row;
|
|
58
|
+
align-items: center;
|
|
59
|
+
gap: 8px;
|
|
60
|
+
`;
|
|
61
|
+
|
|
62
|
+
const CameraContainer = styled(View)`
|
|
63
|
+
flex: 1;
|
|
64
|
+
`;
|
|
65
|
+
|
|
66
|
+
const StyledCamera = styled(Camera)`
|
|
67
|
+
flex: 1;
|
|
68
|
+
`;
|
|
69
|
+
|
|
70
|
+
const CameraPlaceholder = styled(View)`
|
|
71
|
+
flex: 1;
|
|
72
|
+
background-color: #1a1a1a;
|
|
73
|
+
justify-content: center;
|
|
74
|
+
align-items: center;
|
|
75
|
+
`;
|
|
76
|
+
|
|
77
|
+
const PlaceholderText = styled(Text)`
|
|
78
|
+
color: #ffffff;
|
|
79
|
+
font-size: 16px;
|
|
80
|
+
text-align: center;
|
|
81
|
+
padding: 20px;
|
|
82
|
+
`;
|
|
83
|
+
|
|
84
|
+
const PermissionButton = styled(TouchableOpacity)`
|
|
85
|
+
background-color: #10b981;
|
|
86
|
+
padding: 12px 24px;
|
|
87
|
+
border-radius: 12px;
|
|
88
|
+
margin-top: 16px;
|
|
89
|
+
`;
|
|
90
|
+
|
|
91
|
+
const PermissionButtonText = styled(Text)`
|
|
92
|
+
color: #ffffff;
|
|
93
|
+
font-size: 14px;
|
|
94
|
+
font-weight: 600;
|
|
95
|
+
`;
|
|
96
|
+
|
|
97
|
+
const ScanOverlay = styled(View)`
|
|
98
|
+
position: absolute;
|
|
99
|
+
top: 150px;
|
|
100
|
+
left: 0;
|
|
101
|
+
right: 0;
|
|
102
|
+
justify-content: center;
|
|
103
|
+
align-items: center;
|
|
104
|
+
`;
|
|
105
|
+
|
|
106
|
+
const ScanOverlays = styled(View)`
|
|
107
|
+
position: absolute;
|
|
108
|
+
top: 200px;
|
|
109
|
+
left: 0;
|
|
110
|
+
right: 0;
|
|
111
|
+
bottom: ${height * 0.5}px;
|
|
112
|
+
justify-content: center;
|
|
113
|
+
align-items: center;
|
|
114
|
+
`;
|
|
115
|
+
const ScanFrame = styled(View)`
|
|
116
|
+
width: ${width * 0.75}px;
|
|
117
|
+
height: ${width * 0.75}px;
|
|
118
|
+
position: relative;
|
|
119
|
+
`;
|
|
120
|
+
|
|
121
|
+
const CornerBorder = styled(View)<{ position: string }>`
|
|
122
|
+
position: absolute;
|
|
123
|
+
width: 40px;
|
|
124
|
+
height: 40px;
|
|
125
|
+
${props => {
|
|
126
|
+
switch(props.position) {
|
|
127
|
+
case 'topLeft':
|
|
128
|
+
return 'top: 0; left: 0; border-top-width: 4px; border-left-width: 4px;';
|
|
129
|
+
case 'topRight':
|
|
130
|
+
return 'top: 0; right: 0; border-top-width: 4px; border-right-width: 4px;';
|
|
131
|
+
case 'bottomLeft':
|
|
132
|
+
return 'bottom: 0; left: 0; border-bottom-width: 4px; border-left-width: 4px;';
|
|
133
|
+
case 'bottomRight':
|
|
134
|
+
return 'bottom: 0; right: 0; border-bottom-width: 4px; border-right-width: 4px;';
|
|
135
|
+
default:
|
|
136
|
+
return '';
|
|
137
|
+
}
|
|
138
|
+
}}
|
|
139
|
+
border-color: #10b981;
|
|
140
|
+
`;
|
|
141
|
+
|
|
142
|
+
const ScanLineContainer = styled(View)`
|
|
143
|
+
width: 100%;
|
|
144
|
+
height: 100%;
|
|
145
|
+
overflow: hidden;
|
|
146
|
+
`;
|
|
147
|
+
|
|
148
|
+
const ScanLine = styled(View)`
|
|
149
|
+
width: 100%;
|
|
150
|
+
height: 2px;
|
|
151
|
+
background-color: #10b981;
|
|
152
|
+
shadow-color: #10b981;
|
|
153
|
+
shadow-offset: 0px 0px;
|
|
154
|
+
shadow-opacity: 0.8;
|
|
155
|
+
shadow-radius: 4px;
|
|
156
|
+
elevation: 5;
|
|
157
|
+
`;
|
|
158
|
+
|
|
159
|
+
const ScanInstructionText = styled(Text)`
|
|
160
|
+
color: #ffffff;
|
|
161
|
+
font-size: 16px;
|
|
162
|
+
font-weight: 600;
|
|
163
|
+
text-align: center;
|
|
164
|
+
margin-top: 24px;
|
|
165
|
+
text-shadow-color: rgba(0, 0, 0, 0.75);
|
|
166
|
+
text-shadow-offset: 0px 1px;
|
|
167
|
+
text-shadow-radius: 3px;
|
|
168
|
+
`;
|
|
169
|
+
|
|
170
|
+
const ScannedDataContainer = styled(View)`
|
|
171
|
+
position: absolute;
|
|
172
|
+
top: 100px;
|
|
173
|
+
left: 20px;
|
|
174
|
+
right: 20px;
|
|
175
|
+
background-color: rgba(16, 185, 129, 0.95);
|
|
176
|
+
padding: 16px;
|
|
177
|
+
border-radius: 12px;
|
|
178
|
+
z-index: 5;
|
|
179
|
+
`;
|
|
180
|
+
|
|
181
|
+
const ScannedDataText = styled(Text)`
|
|
182
|
+
color: #ffffff;
|
|
183
|
+
font-size: 14px;
|
|
184
|
+
font-weight: 600;
|
|
185
|
+
`;
|
|
186
|
+
|
|
187
|
+
|
|
188
|
+
export function NQRSubScreen({
|
|
189
|
+
amount,
|
|
190
|
+
currency,
|
|
191
|
+
onClose,
|
|
192
|
+
onProcessTransaction,
|
|
193
|
+
onError
|
|
194
|
+
}: Props) {
|
|
195
|
+
const [hasPermission, setHasPermission] = useState<boolean | null>(null);
|
|
196
|
+
const [scannedData, setScannedData] = useState<string | null>(null);
|
|
197
|
+
const [isScanning, setIsScanning] = useState(true);
|
|
198
|
+
const [torch, setTorch] = useState(false);
|
|
199
|
+
const [layoutReady, setLayoutReady] = useState(false);
|
|
200
|
+
|
|
201
|
+
|
|
202
|
+
const [isActive, setIsActive] = useState(false);
|
|
203
|
+
const isForeground = useIsForeground();
|
|
204
|
+
|
|
205
|
+
|
|
206
|
+
const lineAnim = React.useRef(new Animated.Value(0)).current;
|
|
207
|
+
const lineWidth = 2;
|
|
208
|
+
|
|
209
|
+
|
|
210
|
+
const device = useCameraDevice('back', {
|
|
211
|
+
physicalDevices: ['wide-angle-camera'],
|
|
212
|
+
});
|
|
213
|
+
|
|
214
|
+
useEffect(() => {
|
|
215
|
+
checkCameraPermission();
|
|
216
|
+
|
|
217
|
+
return () => {
|
|
218
|
+
setIsActive(false);
|
|
219
|
+
};
|
|
220
|
+
}, []);
|
|
221
|
+
|
|
222
|
+
useEffect(() => {
|
|
223
|
+
// if (scanned) {
|
|
224
|
+
Animated.loop(
|
|
225
|
+
Animated.sequence([
|
|
226
|
+
Animated.timing(lineAnim, {
|
|
227
|
+
toValue: qrSize - lineWidth, // Move to right edge
|
|
228
|
+
duration: 1500,
|
|
229
|
+
easing: Easing.inOut(Easing.linear),
|
|
230
|
+
useNativeDriver: true,
|
|
231
|
+
}),
|
|
232
|
+
Animated.timing(lineAnim, {
|
|
233
|
+
toValue: 0, // Move back to left
|
|
234
|
+
duration: 1500,
|
|
235
|
+
easing: Easing.inOut(Easing.linear),
|
|
236
|
+
useNativeDriver: true,
|
|
237
|
+
}),
|
|
238
|
+
])
|
|
239
|
+
).start();
|
|
240
|
+
// }
|
|
241
|
+
}, []);
|
|
242
|
+
|
|
243
|
+
useEffect(() => {
|
|
244
|
+
if (hasPermission && device && isForeground) {
|
|
245
|
+
const timer = setTimeout(() => {
|
|
246
|
+
setIsActive(true);
|
|
247
|
+
}, 500);
|
|
248
|
+
|
|
249
|
+
return () => clearTimeout(timer);
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
return undefined;
|
|
253
|
+
}, [hasPermission, device, isForeground]);
|
|
254
|
+
|
|
255
|
+
const checkCameraPermission = async () => {
|
|
256
|
+
try {
|
|
257
|
+
const permission = await Camera.getCameraPermissionStatus();
|
|
258
|
+
console.log('Current permission status:', permission);
|
|
259
|
+
|
|
260
|
+
if (permission === 'granted') {
|
|
261
|
+
setHasPermission(true);
|
|
262
|
+
} else if (permission === 'not-determined') {
|
|
263
|
+
const newPermission = await Camera.requestCameraPermission();
|
|
264
|
+
console.log('New permission status:', newPermission);
|
|
265
|
+
setHasPermission(newPermission === 'granted');
|
|
266
|
+
} else {
|
|
267
|
+
setHasPermission(false);
|
|
268
|
+
}
|
|
269
|
+
} catch (error) {
|
|
270
|
+
console.error('Permission check error:', error);
|
|
271
|
+
setHasPermission(false);
|
|
272
|
+
}
|
|
273
|
+
};
|
|
274
|
+
|
|
275
|
+
const requestPermission = async () => {
|
|
276
|
+
try {
|
|
277
|
+
const permission = await Camera.requestCameraPermission();
|
|
278
|
+
console.log('Requested permission:', permission);
|
|
279
|
+
|
|
280
|
+
if (permission === 'denied') {
|
|
281
|
+
Alert.alert(
|
|
282
|
+
'Permission Required',
|
|
283
|
+
'Camera permission is required to scan QR codes. Please enable it in settings.',
|
|
284
|
+
[
|
|
285
|
+
{ text: 'Cancel', style: 'cancel' },
|
|
286
|
+
{ text: 'Open Settings', onPress: () => Linking.openSettings() }
|
|
287
|
+
]
|
|
288
|
+
);
|
|
289
|
+
setHasPermission(false);
|
|
290
|
+
} else if (permission === 'granted') {
|
|
291
|
+
setHasPermission(true);
|
|
292
|
+
}
|
|
293
|
+
} catch (error) {
|
|
294
|
+
console.error('Permission request error:', error);
|
|
295
|
+
setHasPermission(false);
|
|
296
|
+
}
|
|
297
|
+
};
|
|
298
|
+
|
|
299
|
+
const handleQrData=(data: any)=>{
|
|
300
|
+
console.log('QR Code Data:', data);
|
|
301
|
+
const payload={
|
|
302
|
+
accountNo: data?.accountNo,
|
|
303
|
+
bank: data?.bankName,
|
|
304
|
+
accountDetail: data?.accountName,
|
|
305
|
+
channel: 'qr',
|
|
306
|
+
amount: data?.amount,
|
|
307
|
+
remark: data?.description
|
|
308
|
+
}
|
|
309
|
+
console.log('Qr details:', payload);
|
|
310
|
+
onProcessTransaction?.(payload as any);
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
const scanQrFromGallery = async () => {
|
|
314
|
+
try {
|
|
315
|
+
// Open gallery
|
|
316
|
+
const result: any = await launchImageLibrary({
|
|
317
|
+
mediaType: 'photo',
|
|
318
|
+
quality: 1,
|
|
319
|
+
});
|
|
320
|
+
|
|
321
|
+
if (result.didCancel || !result.assets || !result.assets[0].uri) {
|
|
322
|
+
return;
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
const imageUri = result.assets[0].uri;
|
|
326
|
+
const barcodes: any = await QRKit.decodeQR(imageUri);
|
|
327
|
+
if (barcodes?.success) {
|
|
328
|
+
Vibration.vibrate(100);
|
|
329
|
+
|
|
330
|
+
await handleQrData(JSON.parse(barcodes?.data));
|
|
331
|
+
} else {
|
|
332
|
+
Alert.alert('Error:', barcodes?.message)
|
|
333
|
+
}
|
|
334
|
+
} catch (error) {
|
|
335
|
+
console.error('QR Scan Error:', error);
|
|
336
|
+
}
|
|
337
|
+
};
|
|
338
|
+
|
|
339
|
+
|
|
340
|
+
const codeScanner = useCodeScanner({
|
|
341
|
+
codeTypes: ['qr', 'ean-13'],
|
|
342
|
+
onCodeScanned: (codes: any) => {
|
|
343
|
+
if (isScanning && codes.length > 0) {
|
|
344
|
+
const qrData: any = codes[0].value;
|
|
345
|
+
setScannedData(qrData);
|
|
346
|
+
setIsScanning(false);
|
|
347
|
+
|
|
348
|
+
// Vibrate on scan (optional)
|
|
349
|
+
Vibration.vibrate(100);
|
|
350
|
+
console.log('Scanned QR Code:', JSON.parse(qrData));
|
|
351
|
+
// Show alert with scanned data
|
|
352
|
+
handleQrData(JSON.parse(qrData));
|
|
353
|
+
}
|
|
354
|
+
},
|
|
355
|
+
});
|
|
356
|
+
|
|
357
|
+
// useEffect(() => {
|
|
358
|
+
// setLoading(true);
|
|
359
|
+
// nqrAPI.generate({ amount: amount ? amount * 100 : undefined, currency })
|
|
360
|
+
// .then(setQr).catch(e => onError?.(e)).finally(() => setLoading(false));
|
|
361
|
+
// }, []);
|
|
362
|
+
|
|
363
|
+
const renderCamera = () => {
|
|
364
|
+
console.log('Render camera - hasPermission:', hasPermission, 'device:', !!device, 'isActive:', isActive);
|
|
365
|
+
|
|
366
|
+
if (hasPermission === null) {
|
|
367
|
+
return (
|
|
368
|
+
<CameraPlaceholder>
|
|
369
|
+
<PlaceholderText>Checking camera permission...</PlaceholderText>
|
|
370
|
+
</CameraPlaceholder>
|
|
371
|
+
);
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
if (!hasPermission) {
|
|
375
|
+
return (
|
|
376
|
+
<CameraPlaceholder>
|
|
377
|
+
<PlaceholderText>
|
|
378
|
+
Camera permission is required to scan QR codes
|
|
379
|
+
</PlaceholderText>
|
|
380
|
+
<PermissionButton onPress={requestPermission}>
|
|
381
|
+
<PermissionButtonText>Grant Permission</PermissionButtonText>
|
|
382
|
+
</PermissionButton>
|
|
383
|
+
</CameraPlaceholder>
|
|
384
|
+
);
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
if (!device) {
|
|
388
|
+
return (
|
|
389
|
+
<CameraPlaceholder>
|
|
390
|
+
<PlaceholderText>Loading camera...</PlaceholderText>
|
|
391
|
+
</CameraPlaceholder>
|
|
392
|
+
);
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
if (!isActive) {
|
|
396
|
+
return (
|
|
397
|
+
<CameraPlaceholder>
|
|
398
|
+
<PlaceholderText>Initializing camera...</PlaceholderText>
|
|
399
|
+
</CameraPlaceholder>
|
|
400
|
+
);
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
return (
|
|
404
|
+
<StyledCamera
|
|
405
|
+
device={device}
|
|
406
|
+
isActive={true}
|
|
407
|
+
codeScanner={codeScanner}
|
|
408
|
+
enableZoomGesture={true}
|
|
409
|
+
torch={torch ? 'on' : 'off'}
|
|
410
|
+
/>
|
|
411
|
+
);
|
|
412
|
+
};
|
|
413
|
+
|
|
414
|
+
return (
|
|
415
|
+
<Container>
|
|
416
|
+
|
|
417
|
+
{/* Header */}
|
|
418
|
+
<Header>
|
|
419
|
+
<HeaderButton onPress={onClose}>
|
|
420
|
+
<Ionicons name="close" size={24} />
|
|
421
|
+
</HeaderButton>
|
|
422
|
+
|
|
423
|
+
<HeaderCenter>
|
|
424
|
+
<Text style={{ fontWeight: 'bold', fontSize: 16, color:'#FFF' }}>QRIS</Text>
|
|
425
|
+
<Text style={{ fontSize: 10, color: '#FFF' }}>QB Code Standar</Text>
|
|
426
|
+
</HeaderCenter>
|
|
427
|
+
|
|
428
|
+
<HeaderRight>
|
|
429
|
+
<HeaderButton onPress={scanQrFromGallery}>
|
|
430
|
+
<Ionicons name="image-outline" size={22} />
|
|
431
|
+
</HeaderButton>
|
|
432
|
+
<HeaderButton>
|
|
433
|
+
<Ionicons name="settings-outline" size={22} />
|
|
434
|
+
</HeaderButton>
|
|
435
|
+
</HeaderRight>
|
|
436
|
+
</Header>
|
|
437
|
+
|
|
438
|
+
<CameraContainer onLayout={() => setLayoutReady(true)}>
|
|
439
|
+
{renderCamera()}
|
|
440
|
+
|
|
441
|
+
{/* Scan Overlay*/}
|
|
442
|
+
{hasPermission && device && isActive && (
|
|
443
|
+
<ScanOverlay pointerEvents="none">
|
|
444
|
+
<ScanFrame>
|
|
445
|
+
<CornerBorder position="topLeft" />
|
|
446
|
+
<CornerBorder position="topRight" />
|
|
447
|
+
<CornerBorder position="bottomLeft" />
|
|
448
|
+
<CornerBorder position="bottomRight" />
|
|
449
|
+
<Animated.View
|
|
450
|
+
style={[
|
|
451
|
+
styles.scanningLine,
|
|
452
|
+
{
|
|
453
|
+
transform: [{ translateX: lineAnim }],
|
|
454
|
+
},
|
|
455
|
+
]}
|
|
456
|
+
/>
|
|
457
|
+
</ScanFrame>
|
|
458
|
+
<ScanInstructionText>
|
|
459
|
+
{isScanning ? 'Point camera at QR code' : 'QR Code Detected!'}
|
|
460
|
+
</ScanInstructionText>
|
|
461
|
+
</ScanOverlay>
|
|
462
|
+
)}
|
|
463
|
+
|
|
464
|
+
{/* Scanned Data Display */}
|
|
465
|
+
{scannedData && (
|
|
466
|
+
<ScannedDataContainer>
|
|
467
|
+
<ScannedDataText>Scanned: {scannedData}</ScannedDataText>
|
|
468
|
+
</ScannedDataContainer>
|
|
469
|
+
)}
|
|
470
|
+
</CameraContainer>
|
|
471
|
+
{/* <StatusBarBlurBackground /> */}
|
|
472
|
+
|
|
473
|
+
|
|
474
|
+
</Container>
|
|
475
|
+
);
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
const qrSize = width * 0.75;
|
|
479
|
+
const styles = StyleSheet.create({
|
|
480
|
+
|
|
481
|
+
scanningLine: {
|
|
482
|
+
height: qrSize,
|
|
483
|
+
width: 2,
|
|
484
|
+
backgroundColor: '#10b981',
|
|
485
|
+
position: 'absolute',
|
|
486
|
+
top: 0,
|
|
487
|
+
left: 0,
|
|
488
|
+
borderRadius: 1,
|
|
489
|
+
},
|
|
490
|
+
});
|
|
@@ -1,16 +1,13 @@
|
|
|
1
1
|
import React, { useState, useEffect, useRef } from 'react';
|
|
2
2
|
import styled from 'styled-components/native';
|
|
3
3
|
import Svg, { Path } from "react-native-svg";
|
|
4
|
-
import { View, Text,
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import
|
|
8
|
-
import
|
|
9
|
-
import { FPEngine } from '../../../engine/FPEngine';
|
|
10
|
-
import NearbyUsersService from '../../../engine/NearbyUsersService';
|
|
11
|
-
import { PulseAnimation } from '../../components/PulseAnimation';
|
|
4
|
+
import { View, Text, TouchableOpacity, StyleSheet, Animated, Easing } from 'react-native';
|
|
5
|
+
import { C, R, S, F, shadow } from '../../../theme';
|
|
6
|
+
import type { FPError, Props, FPSendPaymentRequest } from '../../../../core/types';
|
|
7
|
+
import NearbyUsersService from '../../../../engine/NearbyUsersService';
|
|
8
|
+
import { PulseAnimation } from '../../../components/PulseAnimation';
|
|
12
9
|
import Ionicons from 'react-native-vector-icons/Ionicons';
|
|
13
|
-
import LoadingAnimation from '
|
|
10
|
+
import LoadingAnimation from '../../../components/LoadingAnimation';
|
|
14
11
|
|
|
15
12
|
|
|
16
13
|
const Container = styled(View)`
|
|
@@ -221,15 +218,13 @@ const ProximityIcon =({ color = "#111", size = 22 })=>(
|
|
|
221
218
|
</Svg>
|
|
222
219
|
)
|
|
223
220
|
|
|
224
|
-
interface Props {
|
|
225
|
-
amount: number;
|
|
226
|
-
currency: FPCurrency;
|
|
227
|
-
onDone: () => void;
|
|
228
|
-
onSuccess?: (tx: FPTransaction) => void;
|
|
229
|
-
onError?: (err: FPError) => void;
|
|
230
|
-
}
|
|
231
221
|
|
|
232
|
-
export function ProximitySubScreen({
|
|
222
|
+
export function ProximitySubScreen({
|
|
223
|
+
amount,
|
|
224
|
+
currency,
|
|
225
|
+
onClose,
|
|
226
|
+
onProcessTransaction,
|
|
227
|
+
onError }: Props) {
|
|
233
228
|
|
|
234
229
|
const [nearbyUsers, setNearbyUsers] = useState([]);
|
|
235
230
|
const [loading, setLoading] = useState(false);
|
|
@@ -250,14 +245,17 @@ export function ProximitySubScreen({ amount, currency, onDone, onSuccess, onErro
|
|
|
250
245
|
|
|
251
246
|
const handlePayment = async (selected: any) => {
|
|
252
247
|
if (!selected) return;
|
|
253
|
-
|
|
254
|
-
setLoading(true);
|
|
255
248
|
try {
|
|
256
|
-
const
|
|
257
|
-
|
|
258
|
-
|
|
249
|
+
const payload: FPSendPaymentRequest = {
|
|
250
|
+
amount: amount,
|
|
251
|
+
currency: currency,
|
|
252
|
+
channel: 'proximity',
|
|
253
|
+
narration: `Send ${currency}${amount} to ${selected.displayName}`,
|
|
254
|
+
recipient: selected
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
onProcessTransaction?.(payload);
|
|
259
258
|
} catch (e) { onError?.(e as FPError); }
|
|
260
|
-
finally { setLoading(false); }
|
|
261
259
|
};
|
|
262
260
|
|
|
263
261
|
useEffect(() => {
|
|
@@ -295,11 +293,10 @@ export function ProximitySubScreen({ amount, currency, onDone, onSuccess, onErro
|
|
|
295
293
|
|
|
296
294
|
return (
|
|
297
295
|
<Container>
|
|
298
|
-
{/* Status Bar */}
|
|
299
|
-
{loading && (
|
|
300
|
-
<LoadingAnimation />
|
|
301
|
-
)}
|
|
302
296
|
<Header>
|
|
297
|
+
<HeaderButton onPress={onClose}>
|
|
298
|
+
<Ionicons name="close" size={24} />
|
|
299
|
+
</HeaderButton>
|
|
303
300
|
<HeaderCenter>
|
|
304
301
|
<Text style={{ fontWeight: 'bold', fontSize: 16, color:'#FFF' }}>Proximity</Text>
|
|
305
302
|
<Text style={{ fontSize: 10, color: '#FFF' }}>Transfer</Text>
|
|
@@ -373,20 +370,3 @@ export function ProximitySubScreen({ amount, currency, onDone, onSuccess, onErro
|
|
|
373
370
|
</Container>
|
|
374
371
|
);
|
|
375
372
|
}
|
|
376
|
-
|
|
377
|
-
const st = StyleSheet.create({
|
|
378
|
-
wrap: { flex: 1, padding: S.lg },
|
|
379
|
-
back: { color: C.brand, fontSize: F.md, fontWeight: '600', marginBottom: S.md },
|
|
380
|
-
title: { fontSize: F.xl, fontWeight: '800', color: C.ink, marginBottom: S.lg },
|
|
381
|
-
peerCard: { flexDirection: 'row', alignItems: 'center', backgroundColor: C.surface, borderRadius: R.lg, padding: S.md, borderWidth: 2, borderColor: 'transparent' },
|
|
382
|
-
peerSelected: { borderColor: C.brand, backgroundColor: '#EEF4FF' },
|
|
383
|
-
avatar: { width: 44, height: 44, borderRadius: 22, backgroundColor: C.brand, justifyContent: 'center', alignItems: 'center', marginRight: S.md },
|
|
384
|
-
avatarText: { color: C.white, fontWeight: '800', fontSize: F.lg },
|
|
385
|
-
peerName: { fontSize: F.md, fontWeight: '700', color: C.ink },
|
|
386
|
-
peerBank: { fontSize: F.sm, color: C.muted },
|
|
387
|
-
dist: { fontSize: F.xs, color: C.brand },
|
|
388
|
-
check: { color: C.brand, fontWeight: '800' },
|
|
389
|
-
empty: { textAlign: 'center', color: C.muted, padding: S.xl },
|
|
390
|
-
footer: { backgroundColor: C.surface, borderRadius: R.lg, padding: S.lg, marginTop: S.md },
|
|
391
|
-
footerText: { fontSize: F.md, color: C.ink, fontWeight: '600', marginBottom: S.md },
|
|
392
|
-
});
|