react-native-timacare 3.3.16 → 3.3.17

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.
@@ -1,256 +1,200 @@
1
- //@ts-nocheck
1
+ // @ts-nocheck
2
2
  import React, { useEffect, useRef, useState } from 'react';
3
3
  import {
4
+ ActivityIndicator,
4
5
  Alert,
5
6
  Dimensions,
6
- Image,
7
7
  Platform,
8
8
  StyleSheet,
9
9
  TouchableOpacity,
10
10
  View,
11
11
  } from 'react-native';
12
- import {
13
- Camera,
14
- useCameraDevice,
15
- useCameraFormat,
16
- useCameraPermission,
17
- } from 'react-native-vision-camera';
12
+ import { RNCamera } from 'react-native-camera';
18
13
  import { IconBackWhite, TakePhotoSvg } from '../../assets/icons';
19
14
  import { MText } from '../../components/MText';
20
- import { useNavigation } from '@react-navigation/native';
15
+ import { useNavigation, useIsFocused } from '@react-navigation/native';
21
16
  import { useSafeAreaInsets } from 'react-native-safe-area-context';
22
- import RNImageManipulator from '@oguzhnatly/react-native-image-manipulator';
23
-
24
- const { width: screenWidth, height: screenHeight } = Dimensions.get('window');
25
-
26
- const getImageSize = (
27
- uri: string
28
- ): Promise<{ width: number; height: number }> =>
29
- new Promise((resolve, reject) => {
30
- Image.getSize(
31
- uri,
32
- (width, height) => resolve({ width, height }),
33
- (err) => reject(err)
34
- );
35
- });
36
-
37
- const clamp = (value: number, min: number, max: number) =>
38
- Math.min(Math.max(value, min), max);
39
-
40
- const getSnapshotCropRect = ({
41
- imageWidth,
42
- imageHeight,
43
- previewWidth,
44
- previewHeight,
45
- frameX,
46
- frameY,
47
- frameWidth,
48
- frameHeight,
49
- }: {
50
- imageWidth: number;
51
- imageHeight: number;
52
- previewWidth: number;
53
- previewHeight: number;
54
- frameX: number;
55
- frameY: number;
56
- frameWidth: number;
57
- frameHeight: number;
58
- }) => {
59
- const scaleX = imageWidth / previewWidth;
60
- const scaleY = imageHeight / previewHeight;
61
- const originX = clamp(frameX * scaleX, 0, imageWidth);
62
- const originY = clamp(frameY * scaleY, 0, imageHeight);
63
- const width = clamp(frameWidth * scaleX, 1, imageWidth - originX);
64
- const height = clamp(frameHeight * scaleY, 1, imageHeight - originY);
65
-
66
- return {
67
- originX: Math.round(originX),
68
- originY: Math.round(originY),
69
- width: Math.round(width),
70
- height: Math.round(height),
71
- };
72
- };
17
+ import { commonStyles } from '../CommonStyles';
18
+ import {
19
+ request,
20
+ PERMISSIONS,
21
+ RESULTS,
22
+ openSettings,
23
+ } from 'react-native-permissions';
73
24
 
74
25
  export default function CCCDCameraScreen(props) {
75
26
  const insets = useSafeAreaInsets();
76
27
  const navigation = useNavigation();
77
- const camera = useRef(null);
78
- const device = useCameraDevice('back');
79
- const [cameraActive, setCameraActive] = useState(true);
80
- const { hasPermission, requestPermission } = useCameraPermission();
81
- const [previewLayout, setPreviewLayout] = useState({
82
- width: screenWidth,
83
- height: screenHeight,
84
- });
85
-
86
- const format = useCameraFormat(device, [
87
- {
88
- photoAspectRatio: previewLayout.height / previewLayout.width,
89
- },
90
- {
91
- videoAspectRatio: previewLayout.height / previewLayout.width,
92
- },
93
- {
94
- photoResolution: 'max',
95
- },
96
- ]);
97
-
98
- useEffect(() => {
99
- if (!hasPermission) requestPermission();
100
- }, [hasPermission, requestPermission]);
28
+ const isFocused = useIsFocused();
29
+ const myCamera = useRef(null);
30
+ const [passPermission, setPassPermission] = useState(false);
31
+ const [shouldRenderCamera, setShouldRenderCamera] = useState(false);
101
32
 
102
33
  const takePicture = async () => {
103
- if (camera.current == null) return;
34
+ if (myCamera.current == null) return;
104
35
  try {
105
- const snapshot = await camera.current.takeSnapshot({
106
- quality: 100,
107
- });
108
- const imageUri = `file://${snapshot.path}`;
109
- const normalized = await RNImageManipulator.manipulate(imageUri, [], {
110
- format: 'jpg',
111
- compress: 1,
112
- });
113
- const { width: imageWidth, height: imageHeight } = await getImageSize(
114
- normalized.uri
115
- );
116
-
117
- const previewWidth = previewLayout.width;
118
- const previewHeight = previewLayout.height;
119
-
120
- const frameWidth = previewWidth * 0.85;
121
- const frameHeight = frameWidth * 0.63;
122
- const frameX = (previewWidth - frameWidth) / 2;
123
- const frameY = (previewHeight - frameHeight) / 2;
124
-
125
- const cropRect = getSnapshotCropRect({
126
- imageWidth,
127
- imageHeight,
128
- previewWidth,
129
- previewHeight,
130
- frameX,
131
- frameY,
132
- frameWidth,
133
- frameHeight,
134
- });
135
-
136
- const result = await RNImageManipulator.manipulate(
137
- normalized.uri,
138
- [
139
- {
140
- crop: cropRect,
141
- },
142
- ],
143
- { format: 'jpg', compress: 1 }
144
- );
145
- setCameraActive(false);
36
+ const options = {
37
+ base64: true,
38
+ pauseAfterCapture: true,
39
+ fixOrientation: false,
40
+ forceUpOrientation: false,
41
+ orientation: RNCamera.Constants.Orientation.portrait,
42
+ quality: 0.9,
43
+ pictureSize: '1280x720',
44
+ };
45
+ const data = await myCamera.current.takePictureAsync(options);
146
46
  navigation.goBack();
147
47
  if (props.route?.params?.callback) {
148
- props.route.params.callback(result?.uri);
48
+ props.route.params.callback(data.uri);
149
49
  }
150
50
  } catch (err) {
151
51
  console.error(err);
152
52
  }
153
53
  };
154
54
 
155
- if (!device || !hasPermission)
55
+ const requestPermissions = () => {
56
+ request(
57
+ Platform.OS === 'ios'
58
+ ? PERMISSIONS.IOS.CAMERA
59
+ : PERMISSIONS.ANDROID.CAMERA
60
+ ).then((result) => {
61
+ switch (result) {
62
+ case RESULTS.UNAVAILABLE:
63
+ console.log(
64
+ 'This feature is not available (on this device / in this context)'
65
+ );
66
+ Alert.alert(
67
+ 'Thông báo',
68
+ '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ị!',
69
+ [{ text: 'Đồng ý' }]
70
+ );
71
+ break;
72
+ case RESULTS.DENIED:
73
+ console.log(
74
+ 'The permission has not been requested / is denied but requestable'
75
+ );
76
+ Alert.alert(
77
+ 'Thông báo',
78
+ 'Bạn đã từ chối quyền máy ảnh. Vui lòng cấp quyền máy ảnh để tiếp tục!',
79
+ [
80
+ {
81
+ text: 'Đồng ý',
82
+ onPress: () => {
83
+ if (Platform.OS === 'ios') {
84
+ openSettings();
85
+ } else {
86
+ requestPermissions();
87
+ }
88
+ },
89
+ },
90
+ ]
91
+ );
92
+ break;
93
+ case RESULTS.LIMITED:
94
+ console.log('The permission is limited: some actions are possible');
95
+ Alert.alert(
96
+ 'Thông báo',
97
+ 'Quyền máy ảnh bị hạn chế. Vui lòng kiểm tra lại để tiếp tục!',
98
+ [
99
+ {
100
+ text: 'Đồng ý',
101
+ onPress: () => {
102
+ if (Platform.OS === 'ios') {
103
+ openSettings();
104
+ } else {
105
+ requestPermissions();
106
+ }
107
+ },
108
+ },
109
+ ]
110
+ );
111
+ break;
112
+ case RESULTS.GRANTED:
113
+ console.log('The permission is granted');
114
+ setPassPermission(true);
115
+ break;
116
+ case RESULTS.BLOCKED:
117
+ console.log('The permission is denied and not requestable anymore');
118
+ Alert.alert(
119
+ 'Thông báo',
120
+ 'Bạn đã từ chối quyền máy ảnh. Vui lòng cấp quyền máy ảnh để tiếp tục!',
121
+ [
122
+ {
123
+ text: 'Đồng ý',
124
+ onPress: () => {
125
+ if (Platform.OS === 'ios') {
126
+ openSettings();
127
+ } else {
128
+ openSettings();
129
+ }
130
+ },
131
+ },
132
+ ]
133
+ );
134
+ break;
135
+ }
136
+ });
137
+ };
138
+
139
+ useEffect(() => {
140
+ requestPermissions();
141
+ }, []);
142
+
143
+ useEffect(() => {
144
+ let timeout: NodeJS.Timeout;
145
+ if (isFocused) {
146
+ // Delay mounting camera to give Android time to release the previous one
147
+ timeout = setTimeout(() => {
148
+ setShouldRenderCamera(true);
149
+ }, 500); // thử 300ms–500ms nếu cần
150
+ } else {
151
+ setShouldRenderCamera(false);
152
+ }
153
+
154
+ return () => clearTimeout(timeout);
155
+ }, [isFocused]);
156
+
157
+ if (!passPermission && !shouldRenderCamera) {
156
158
  return (
157
- <View
158
- style={{
159
- flex: 1,
160
- backgroundColor: '#000',
161
- alignItems: 'center',
162
- justifyContent: 'center',
163
- }}
164
- >
165
- <MText style={{ color: '#fff' }}>
166
- Vui lòng cấp quyền truy cập Camera
167
- </MText>
159
+ <View style={commonStyles.fill}>
160
+ <ActivityIndicator size="large" color="#0000ff" />
168
161
  </View>
169
162
  );
170
-
171
- const frameWidth = previewLayout.width * 0.85;
172
- const frameHeight = frameWidth * 0.63;
173
- const frameX = (previewLayout.width - frameWidth) / 2;
174
- const frameY = (previewLayout.height - frameHeight) / 2;
163
+ }
175
164
 
176
165
  return (
177
166
  <View
178
- style={$container}
179
- onLayout={(e) => {
180
- const { width, height } = e.nativeEvent.layout;
181
- if (width > 0 && height > 0) setPreviewLayout({ width, height });
182
- }}
167
+ style={{ flex: 1, backgroundColor: 'black' }}
168
+ pointerEvents={'box-none'}
183
169
  >
184
- <Camera
185
- style={{ flex: 1 }}
186
- device={device}
187
- isActive={cameraActive}
188
- photo={false}
189
- video={true}
190
- outputOrientation="preview"
191
- ref={camera}
192
- format={format}
193
- resizeMode="cover"
194
- />
195
- <View style={$overlay} pointerEvents="none">
196
- <View style={[$darkOverlayTop, { height: frameY }]} />
197
- <View style={$row}>
198
- <View
199
- style={[$darkOverlaySide, { width: frameX, height: frameHeight }]}
200
- />
201
- <View
202
- style={[$frameBorder, { width: frameWidth, height: frameHeight }]}
203
- >
204
- {/* Top Left */}
205
- <Corner
206
- style={{
207
- top: 0,
208
- left: 0,
209
- borderTopWidth: CORNER_WIDTH,
210
- borderLeftWidth: CORNER_WIDTH,
211
- borderColor: BORDER_COLOR,
212
- }}
213
- />
214
-
215
- {/* Top Right */}
216
- <Corner
217
- style={{
218
- top: 0,
219
- right: 0,
220
- borderTopWidth: CORNER_WIDTH,
221
- borderRightWidth: CORNER_WIDTH,
222
- borderColor: BORDER_COLOR,
223
- }}
224
- />
225
-
226
- {/* Bottom Left */}
227
- <Corner
228
- style={{
229
- bottom: 0,
230
- left: 0,
231
- borderBottomWidth: CORNER_WIDTH,
232
- borderLeftWidth: CORNER_WIDTH,
233
- borderColor: BORDER_COLOR,
234
- }}
235
- />
236
-
237
- {/* Bottom Right */}
238
- <Corner
239
- style={{
240
- bottom: 0,
241
- right: 0,
242
- borderBottomWidth: CORNER_WIDTH,
243
- borderRightWidth: CORNER_WIDTH,
244
- borderColor: BORDER_COLOR,
245
- }}
246
- />
247
- </View>
248
- <View
249
- style={[$darkOverlaySide, { width: frameX, height: frameHeight }]}
250
- />
251
- </View>
252
- <View style={[$darkOverlayBottom, { height: frameY }]} />
170
+ <View
171
+ style={[
172
+ commonStyles.alignCenter,
173
+ commonStyles.fill,
174
+ commonStyles.justifyCenter,
175
+ {
176
+ position: 'absolute',
177
+ width: Dimensions.get('window').width,
178
+ height: Dimensions.get('window').height,
179
+ },
180
+ ]}
181
+ >
182
+ <RNCamera
183
+ ref={myCamera}
184
+ defaultVideoQuality={RNCamera.Constants.VideoQuality['720p']}
185
+ style={{
186
+ width: Dimensions.get('window').width,
187
+ height: 260,
188
+ overflow: 'hidden',
189
+ }}
190
+ maxZoom={1}
191
+ >
192
+ {({ camera, status, recordAudioPermissionStatus }) => {
193
+ if (status !== 'READY') return <ActivityIndicator />;
194
+ }}
195
+ </RNCamera>
253
196
  </View>
197
+
254
198
  <View style={[$back, { top: insets.top + 16 }]}>
255
199
  <TouchableOpacity
256
200
  onPress={() => {
@@ -308,23 +252,6 @@ export default function CCCDCameraScreen(props) {
308
252
  );
309
253
  }
310
254
 
311
- const CORNER_SIZE = 20;
312
- const CORNER_WIDTH = 2;
313
- const BORDER_COLOR = '#FDFDFD';
314
-
315
- const Corner = ({ style }: { style: ViewStyle }) => (
316
- <View
317
- style={[
318
- style,
319
- {
320
- position: 'absolute',
321
- width: CORNER_SIZE,
322
- height: CORNER_SIZE,
323
- },
324
- ]}
325
- />
326
- );
327
-
328
255
  const overlayColor = 'rgba(22, 22, 22, 0.7)';
329
256
  const $back: ViewStyle = {
330
257
  position: 'absolute',
@@ -300,7 +300,7 @@ export const OCR = observer(function OCR(props: any) {
300
300
  fixOrientation: false,
301
301
  forceUpOrientation: false,
302
302
  orientation: RNCamera.Constants.Orientation.portrait,
303
- quality: 0.8,
303
+ quality: 0.9,
304
304
  pictureSize: '1280x720',
305
305
  };
306
306
  const data = await myCamera.current.takePictureAsync(options);