react-native-timacare 3.1.38 → 3.1.39

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.
Files changed (62) hide show
  1. package/lib/commonjs/navigation/primary-navigator.js +1 -1
  2. package/lib/commonjs/navigation/primary-navigator.js.flow +6 -0
  3. package/lib/commonjs/navigation/primary-navigator.js.map +1 -1
  4. package/lib/commonjs/screens/camera/CCCDCameraScreen.js +2 -0
  5. package/lib/commonjs/screens/camera/CCCDCameraScreen.js.flow +273 -0
  6. package/lib/commonjs/screens/camera/CCCDCameraScreen.js.map +1 -0
  7. package/lib/commonjs/screens/camera/index.js +1 -1
  8. package/lib/commonjs/screens/camera/index.js.flow +59 -78
  9. package/lib/commonjs/screens/camera/index.js.map +1 -1
  10. package/lib/commonjs/screens/home/index.js +1 -1
  11. package/lib/commonjs/screens/home/index.js.flow +2078 -2132
  12. package/lib/commonjs/screens/home/index.js.map +1 -1
  13. package/lib/commonjs/screens/nationalID/index.js +1 -1
  14. package/lib/commonjs/screens/nationalID/index.js.flow +25 -45
  15. package/lib/commonjs/screens/nationalID/index.js.map +1 -1
  16. package/lib/commonjs/screens/nationalIDBack/index.js +1 -1
  17. package/lib/commonjs/screens/nationalIDBack/index.js.flow +24 -45
  18. package/lib/commonjs/screens/nationalIDBack/index.js.map +1 -1
  19. package/lib/commonjs/screens/register/index.js +1 -1
  20. package/lib/commonjs/screens/register/index.js.flow +1 -1
  21. package/lib/commonjs/screens/toan-trinh-so/RegisterCamera.js +1 -1
  22. package/lib/commonjs/screens/toan-trinh-so/RegisterCamera.js.flow +0 -1
  23. package/lib/commonjs/screens/toan-trinh-so/RegisterCamera.js.map +1 -1
  24. package/lib/commonjs/services/api/api-config.js +1 -1
  25. package/lib/commonjs/services/api/api-config.js.flow +1 -1
  26. package/lib/commonjs/services/api/api-config.js.map +1 -1
  27. package/lib/module/navigation/primary-navigator.js +1 -1
  28. package/lib/module/navigation/primary-navigator.js.map +1 -1
  29. package/lib/module/screens/camera/CCCDCameraScreen.js +2 -0
  30. package/lib/module/screens/camera/CCCDCameraScreen.js.map +1 -0
  31. package/lib/module/screens/camera/index.js +1 -1
  32. package/lib/module/screens/camera/index.js.map +1 -1
  33. package/lib/module/screens/home/index.js +1 -1
  34. package/lib/module/screens/home/index.js.map +1 -1
  35. package/lib/module/screens/nationalID/index.js +1 -1
  36. package/lib/module/screens/nationalID/index.js.map +1 -1
  37. package/lib/module/screens/nationalIDBack/index.js +1 -1
  38. package/lib/module/screens/nationalIDBack/index.js.map +1 -1
  39. package/lib/module/screens/register/index.js +1 -1
  40. package/lib/module/screens/toan-trinh-so/RegisterCamera.js +1 -1
  41. package/lib/module/screens/toan-trinh-so/RegisterCamera.js.map +1 -1
  42. package/lib/module/services/api/api-config.js +1 -1
  43. package/lib/module/services/api/api-config.js.map +1 -1
  44. package/lib/typescript/navigation/primary-navigator.d.ts +1 -0
  45. package/lib/typescript/navigation/primary-navigator.d.ts.map +1 -1
  46. package/lib/typescript/screens/camera/CCCDCameraScreen.d.ts +2 -0
  47. package/lib/typescript/screens/camera/CCCDCameraScreen.d.ts.map +1 -0
  48. package/lib/typescript/screens/camera/index.d.ts.map +1 -1
  49. package/lib/typescript/screens/home/index.d.ts.map +1 -1
  50. package/lib/typescript/screens/nationalID/index.d.ts.map +1 -1
  51. package/lib/typescript/screens/nationalIDBack/index.d.ts.map +1 -1
  52. package/lib/typescript/screens/toan-trinh-so/RegisterCamera.d.ts.map +1 -1
  53. package/package.json +2 -1
  54. package/src/navigation/primary-navigator.tsx +6 -0
  55. package/src/screens/camera/CCCDCameraScreen.tsx +273 -0
  56. package/src/screens/camera/index.tsx +59 -78
  57. package/src/screens/home/index.tsx +2078 -2132
  58. package/src/screens/nationalID/index.tsx +25 -45
  59. package/src/screens/nationalIDBack/index.tsx +24 -45
  60. package/src/screens/register/index.tsx +1 -1
  61. package/src/screens/toan-trinh-so/RegisterCamera.tsx +0 -1
  62. package/src/services/api/api-config.ts +1 -1
@@ -75,6 +75,7 @@ import TTSSignLoan from '../screens/toan-trinh-so/TTSSignLoan';
75
75
  import TTSOTP from '../screens/toan-trinh-so/TTSOTP';
76
76
  import RegisterBack from '../screens/toan-trinh-so/RegisterBack';
77
77
  import { Api } from '../services/api';
78
+ import CCCDCameraScreen from '../screens/camera/CCCDCameraScreen';
78
79
 
79
80
  export const ScreenNames = {
80
81
  Splash: 'Splash',
@@ -148,6 +149,7 @@ export const ScreenNames = {
148
149
  TTSSignLoan: 'TTSSignLoan',
149
150
  TTSOTP: 'TTSOTP',
150
151
  RegisterBack: 'RegisterBack',
152
+ CCCDCameraScreen: 'CCCDCameraScreen',
151
153
  };
152
154
 
153
155
  const Stack = createNativeStackNavigator();
@@ -309,6 +311,10 @@ export function PrimaryNavigator(props) {
309
311
  <Stack.Screen name={ScreenNames.TTSSignLoan} component={TTSSignLoan} />
310
312
  <Stack.Screen name={ScreenNames.TTSOTP} component={TTSOTP} />
311
313
  <Stack.Screen name={ScreenNames.RegisterBack} component={RegisterBack} />
314
+ <Stack.Screen
315
+ name={ScreenNames.CCCDCameraScreen}
316
+ component={CCCDCameraScreen}
317
+ />
312
318
  </Stack.Navigator>
313
319
  );
314
320
  }
@@ -0,0 +1,273 @@
1
+ //@ts-nocheck
2
+ import React, { useEffect, useRef, useState } from 'react';
3
+ import {
4
+ Alert,
5
+ Dimensions,
6
+ Image,
7
+ Platform,
8
+ StyleSheet,
9
+ TouchableOpacity,
10
+ View,
11
+ } from 'react-native';
12
+ import { Camera, useCameraDevices } from 'react-native-vision-camera';
13
+ import { IconBackWhite, TakePhotoSvg } from '../../assets/icons';
14
+ import { MText } from '../../components/MText';
15
+ import ImageEditor from '@react-native-community/image-editor';
16
+ import { CommonActions, useNavigation } from '@react-navigation/native';
17
+ import DeviceInfo from 'react-native-device-info';
18
+ import {
19
+ request,
20
+ PERMISSIONS,
21
+ RESULTS,
22
+ openSettings,
23
+ } from 'react-native-permissions';
24
+
25
+ const { width: screenWidth, height: screenHeight } = Dimensions.get('window');
26
+ const frameWidth = screenWidth * 0.85;
27
+ const frameHeight = frameWidth * 0.63; // tỷ lệ giống CCCD
28
+ const frameLeft = (screenWidth - frameWidth) / 2;
29
+ const frameTop = ((screenWidth * 4) / 3 - frameHeight) / 2;
30
+ export default function CCCDCameraScreen(props) {
31
+ const navigation = useNavigation();
32
+ const camera = useRef(null);
33
+ const devices = useCameraDevices();
34
+ const device = devices.back;
35
+ const [cameraActive, setCameraActive] = useState(true);
36
+ const [passPermission, setPassPermission] = useState(false);
37
+
38
+ const requestPermissions = () => {
39
+ request(
40
+ Platform.OS === 'ios'
41
+ ? PERMISSIONS.IOS.CAMERA
42
+ : PERMISSIONS.ANDROID.CAMERA
43
+ ).then((result) => {
44
+ switch (result) {
45
+ case RESULTS.UNAVAILABLE:
46
+ console.log(
47
+ 'This feature is not available (on this device / in this context)'
48
+ );
49
+ Alert.alert(
50
+ 'Thông báo',
51
+ 'Máy ảnh trên thiết bị không tương thích.\nVui lòng kiểm tra lại thiết bị!',
52
+ [{ text: 'Đồng ý' }]
53
+ );
54
+ break;
55
+ case RESULTS.DENIED:
56
+ console.log(
57
+ 'The permission has not been requested / is denied but requestable'
58
+ );
59
+ Alert.alert(
60
+ 'Thông báo',
61
+ 'Bạn đã từ chối quyền máy ảnh. Vui lòng cấp quyền máy ảnh để tiếp tục!',
62
+ [
63
+ {
64
+ text: 'Đồng ý',
65
+ onPress: () => {
66
+ if (Platform.OS === 'ios') {
67
+ openSettings();
68
+ } else {
69
+ requestPermissions();
70
+ }
71
+ },
72
+ },
73
+ ]
74
+ );
75
+ break;
76
+ case RESULTS.LIMITED:
77
+ console.log('The permission is limited: some actions are possible');
78
+ Alert.alert(
79
+ 'Thông báo',
80
+ 'Quyền máy ảnh bị hạn chế. Vui lòng kiểm tra lại để tiếp tục!',
81
+ [
82
+ {
83
+ text: 'Đồng ý',
84
+ onPress: () => {
85
+ if (Platform.OS === 'ios') {
86
+ openSettings();
87
+ } else {
88
+ requestPermissions();
89
+ }
90
+ },
91
+ },
92
+ ]
93
+ );
94
+ break;
95
+ case RESULTS.GRANTED:
96
+ console.log('The permission is granted');
97
+ setPassPermission(true);
98
+ break;
99
+ case RESULTS.BLOCKED:
100
+ console.log('The permission is denied and not requestable anymore');
101
+ Alert.alert(
102
+ 'Thông báo',
103
+ 'Bạn đã từ chối quyền máy ảnh. Vui lòng cấp quyền máy ảnh để tiếp tục!',
104
+ [
105
+ {
106
+ text: 'Đồng ý',
107
+ },
108
+ ]
109
+ );
110
+ break;
111
+ }
112
+ });
113
+ };
114
+
115
+ useEffect(() => {
116
+ requestPermissions();
117
+ }, []);
118
+
119
+ const takePicture = async () => {
120
+ if (camera.current == null) return;
121
+
122
+ const photo = await camera.current.takePhoto({
123
+ skipMetadata: true,
124
+ });
125
+
126
+ Image.getSize(`file://${photo.path}`, async (width, height) => {
127
+ const previewHeight = (screenWidth * 4) / 3; // Tỷ lệ khung preview camera
128
+
129
+ // Tính scale giữa ảnh thật và khung preview
130
+ const scaleX = width / screenWidth;
131
+ const scaleY = height / previewHeight;
132
+
133
+ // Tính tọa độ vùng cần crop trên ảnh gốc
134
+ const cropData = {
135
+ offset: {
136
+ x: frameLeft * scaleX,
137
+ y: frameTop * scaleY,
138
+ },
139
+ size: {
140
+ width: frameWidth * scaleX,
141
+ height: frameHeight * scaleY,
142
+ },
143
+ displaySize: {
144
+ width: frameWidth * scaleX,
145
+ height: frameHeight * scaleY,
146
+ },
147
+ resizeMode: 'contain',
148
+ };
149
+
150
+ try {
151
+ const croppedUri = await ImageEditor.cropImage(
152
+ `file://${photo.path}`,
153
+ cropData
154
+ );
155
+ setCameraActive(false);
156
+ navigation.goBack();
157
+ if (props.route.params.callback) {
158
+ props.route.params.callback(croppedUri.uri);
159
+ }
160
+ } catch (err) {
161
+ console.error(err);
162
+ }
163
+ });
164
+ };
165
+
166
+ if (!device || !passPermission)
167
+ return <View style={{ flex: 1, backgroundColor: '#000' }} />;
168
+ return (
169
+ <View style={styles.container}>
170
+ <View style={styles.topContainer}>
171
+ <TouchableOpacity
172
+ onPress={() => {
173
+ navigation.goBack();
174
+ }}
175
+ style={[
176
+ { width: 48, height: 48, zIndex: 1000, alignItems: 'center' },
177
+ ]}
178
+ >
179
+ <IconBackWhite />
180
+ </TouchableOpacity>
181
+ </View>
182
+ <Camera
183
+ style={styles.camera}
184
+ device={device}
185
+ isActive={cameraActive}
186
+ photo={true}
187
+ ref={camera}
188
+ />
189
+ <View style={styles.overlay}>
190
+ <View style={styles.darkOverlayTop} />
191
+ <View style={styles.row}>
192
+ <View style={styles.darkOverlaySide} />
193
+ <View style={styles.frameBorder} />
194
+ <View style={styles.darkOverlaySide} />
195
+ </View>
196
+ <View style={styles.darkOverlayBottom} />
197
+ </View>
198
+ <View style={styles.captureButton}>
199
+ <MText
200
+ style={{
201
+ color: '#fff',
202
+ marginBottom: 16,
203
+ textAlign: 'center',
204
+ paddingHorizontal: 24,
205
+ }}
206
+ >
207
+ Vui lòng đặt giấy tờ nằm vừa khung hình chữ nhật, chụp đủ ánh sáng và
208
+ rõ nét.
209
+ </MText>
210
+ <TouchableOpacity onPress={takePicture}>
211
+ <TakePhotoSvg />
212
+ </TouchableOpacity>
213
+ </View>
214
+ </View>
215
+ );
216
+ }
217
+
218
+ const overlayColor = 'rgba(22, 22, 22, 0.7)';
219
+ const styles = StyleSheet.create({
220
+ container: { flex: 1, backgroundColor: '#000' },
221
+ camera: { flex: 1 },
222
+ overlay: {
223
+ ...StyleSheet.absoluteFillObject,
224
+ justifyContent: 'center',
225
+ alignItems: 'center',
226
+ },
227
+ frameBorder: {
228
+ width: frameWidth,
229
+ height: frameHeight,
230
+ borderColor: '#FDFDFD',
231
+ borderWidth: 2,
232
+ },
233
+ row: {
234
+ flexDirection: 'row',
235
+ alignItems: 'center',
236
+ },
237
+ darkOverlayTop: {
238
+ width: '100%',
239
+ height: (screenHeight - frameHeight) / 2,
240
+ backgroundColor: overlayColor,
241
+ },
242
+ darkOverlayBottom: {
243
+ width: '100%',
244
+ height: (screenHeight - frameHeight) / 2,
245
+ backgroundColor: overlayColor,
246
+ },
247
+ darkOverlaySide: {
248
+ width: (screenWidth - frameWidth) / 2,
249
+ height: frameHeight,
250
+ backgroundColor: overlayColor,
251
+ },
252
+ captureContainer: {
253
+ position: 'absolute',
254
+ bottom: 40,
255
+ width: '100%',
256
+ alignItems: 'center',
257
+ },
258
+ captureButton: {
259
+ position: 'absolute',
260
+ bottom: 30,
261
+ alignSelf: 'center',
262
+ flexDirection: 'column',
263
+ alignItems: 'center',
264
+ },
265
+ topContainer: {
266
+ position: 'absolute',
267
+ top: DeviceInfo.hasNotch() ? 60 : 24,
268
+ },
269
+ textContainer: {
270
+ position: 'absolute',
271
+ top: DeviceInfo.hasNotch() ? 60 : 24,
272
+ },
273
+ });
@@ -10,13 +10,16 @@ import {
10
10
  Platform,
11
11
  SafeAreaView,
12
12
  ScrollView,
13
- StyleSheet,
14
13
  TextStyle,
15
14
  TouchableOpacity,
16
15
  View,
17
16
  ViewStyle,
18
17
  } from 'react-native';
19
- import { CommonActions, useNavigation } from '@react-navigation/native';
18
+ import {
19
+ CommonActions,
20
+ useNavigation,
21
+ useIsFocused,
22
+ } from '@react-navigation/native';
20
23
  import { observer } from 'mobx-react-lite';
21
24
  import { commonStyles } from '../CommonStyles';
22
25
  import MButton from '../../components/MButton';
@@ -46,15 +49,11 @@ import { MText } from '../../components/MText';
46
49
  import Modal from 'react-native-modal';
47
50
  import LinearGradient from 'react-native-linear-gradient';
48
51
 
49
- const screenWidth = Dimensions.get('window').width;
50
- const screenHeight = Dimensions.get('window').height;
51
-
52
- const frameWidth = screenWidth * 0.85;
53
- const frameHeight = screenWidth * 0.54;
54
-
55
52
  const cameraStore = new Store();
56
53
  export const Camera = observer(function Camera(props: any) {
57
54
  const navigation = useNavigation();
55
+ const isFocused = useIsFocused();
56
+ const [shouldRenderCamera, setShouldRenderCamera] = useState(false);
58
57
  const [passPermission, setPassPermission] = useState(false);
59
58
  const [flash, setFlash] = useState(false);
60
59
  const [front, setFront] = useState(props.route.params.front);
@@ -67,14 +66,17 @@ export const Camera = observer(function Camera(props: any) {
67
66
  const takePhoto = async () => {
68
67
  setTaking(true);
69
68
  const options = {
69
+ width: 720,
70
70
  base64: true,
71
+ pauseAfterCapture: true,
71
72
  fixOrientation: false,
72
73
  forceUpOrientation: false,
73
74
  orientation: RNCamera.Constants.Orientation.portrait,
74
- skipProcessing: true,
75
75
  };
76
76
  const data = await myCamera.current.takePictureAsync(options);
77
+
77
78
  setTaking(false);
79
+ // eslint-disable-next-line
78
80
  navigation.goBack();
79
81
  if (props.route.params.callback) {
80
82
  props.route.params.callback(data.uri);
@@ -189,12 +191,26 @@ export const Camera = observer(function Camera(props: any) {
189
191
  }
190
192
  };
191
193
 
194
+ useEffect(() => {
195
+ let timeout: NodeJS.Timeout;
196
+ if (isFocused) {
197
+ // Delay mounting camera to give Android time to release the previous one
198
+ timeout = setTimeout(() => {
199
+ setShouldRenderCamera(true);
200
+ }, 500); // thử 300ms–500ms nếu cần
201
+ } else {
202
+ setShouldRenderCamera(false);
203
+ }
204
+
205
+ return () => clearTimeout(timeout);
206
+ }, [isFocused]);
207
+
192
208
  return (
193
209
  <View
194
210
  style={[commonStyles.fill, { backgroundColor: 'black' }]}
195
211
  pointerEvents={'box-none'}
196
212
  >
197
- {passPermission && (
213
+ {passPermission && shouldRenderCamera && (
198
214
  <View
199
215
  style={[
200
216
  commonStyles.alignCenter,
@@ -210,15 +226,12 @@ export const Camera = observer(function Camera(props: any) {
210
226
  <RNCamera
211
227
  ref={myCamera}
212
228
  defaultVideoQuality={RNCamera.Constants.VideoQuality['720p']}
213
- style={
214
- props.route.params.selfie
215
- ? {
216
- width: Dimensions.get('window').width,
217
- height: (Dimensions.get('window').width * 4) / 3.1,
218
- }
219
- : StyleSheet.absoluteFillObject
220
- }
229
+ style={{
230
+ width: Dimensions.get('window').width,
231
+ height: (Dimensions.get('window').width * 4) / 3.1,
232
+ }}
221
233
  maxZoom={1}
234
+ zoom={props.route.params.selfie ? 0 : 0.005}
222
235
  type={
223
236
  front
224
237
  ? RNCamera.Constants.Type.front
@@ -229,64 +242,22 @@ export const Camera = observer(function Camera(props: any) {
229
242
  ? RNCamera.Constants.FlashMode.on
230
243
  : RNCamera.Constants.FlashMode.off
231
244
  }
245
+ androidCameraPermissionOptions={{
246
+ title: 'Permission to use camera',
247
+ message: 'We need your permission to use your camera',
248
+ buttonPositive: 'Ok',
249
+ buttonNegative: 'Cancel',
250
+ }}
251
+ androidRecordAudioPermissionOptions={{
252
+ title: 'Permission to use audio recording',
253
+ message: 'We need your permission to use your audio',
254
+ buttonPositive: 'Ok',
255
+ buttonNegative: 'Cancel',
256
+ }}
232
257
  >
233
258
  {({ camera, status, recordAudioPermissionStatus }) => {
234
259
  if (status !== 'READY') return <ActivityIndicator />;
235
260
  }}
236
- {!props.route.params.selfie && (
237
- <View
238
- style={{
239
- ...StyleSheet.absoluteFillObject,
240
- justifyContent: 'center',
241
- alignItems: 'center',
242
- }}
243
- >
244
- <View
245
- style={{
246
- width: '100%',
247
- height: (screenHeight - frameHeight) / 2,
248
- backgroundColor: 'rgba(0, 0, 0, 0.6)',
249
- }}
250
- />
251
- <View
252
- style={{
253
- flexDirection: 'row',
254
- alignItems: 'center',
255
- }}
256
- >
257
- <View
258
- style={{
259
- width: (screenWidth - frameWidth) / 2,
260
- height: frameHeight,
261
- backgroundColor: 'rgba(0, 0, 0, 0.6)',
262
- }}
263
- />
264
- <View
265
- style={{
266
- width: frameWidth,
267
- height: frameHeight,
268
- borderColor: '#FDFDFD',
269
- borderWidth: 2,
270
- borderRadius: 6,
271
- }}
272
- />
273
- <View
274
- style={{
275
- width: (screenWidth - frameWidth) / 2,
276
- height: frameHeight,
277
- backgroundColor: 'rgba(0, 0, 0, 0.6)',
278
- }}
279
- />
280
- </View>
281
- <View
282
- style={{
283
- width: '100%',
284
- height: (screenHeight - frameHeight) / 2,
285
- backgroundColor: 'rgba(0, 0, 0, 0.6)',
286
- }}
287
- />
288
- </View>
289
- )}
290
261
  </RNCamera>
291
262
  </View>
292
263
  )}
@@ -331,7 +302,7 @@ export const Camera = observer(function Camera(props: any) {
331
302
  {
332
303
  color: 'white',
333
304
  textAlign: 'center',
334
-
305
+ backgroundColor: '#00000099',
335
306
  paddingVertical: 12,
336
307
  },
337
308
  ]}
@@ -356,7 +327,21 @@ export const Camera = observer(function Camera(props: any) {
356
327
  sáng đủ rõ nét khuôn mặt
357
328
  </MText>
358
329
  )}
359
-
330
+ {/* {(Platform.OS === 'ios' ? false : !cameraStore.isFace) && selfie && (
331
+ <MText
332
+ style={[
333
+ commonStyles.textNormal,
334
+ {
335
+ color: 'white',
336
+ textAlign: 'center',
337
+ backgroundColor: '#00000099',
338
+ paddingVertical: 12,
339
+ },
340
+ ]}
341
+ >
342
+ Vui lòng hướng khuôn mặt vào máy ảnh
343
+ </MText>
344
+ )} */}
360
345
  {taking ? (
361
346
  <View
362
347
  style={[
@@ -374,10 +359,6 @@ export const Camera = observer(function Camera(props: any) {
374
359
  commonStyles.alignCenter,
375
360
  commonStyles.justifyCenter,
376
361
  {
377
- flex: 0,
378
-
379
- paddingHorizontal: 20,
380
- alignSelf: 'center',
381
362
  margin: 24,
382
363
  },
383
364
  ]}