Skip to content

Commit 24ada1e

Browse files
authored
Merge pull request #217 from monkvision/feature/capture-enhancements
Feature capture enhancements
2 parents 1e41ddb + 48c5c1b commit 24ada1e

File tree

11 files changed

+489
-266
lines changed

11 files changed

+489
-266
lines changed

packages/camera/src/components/Capture/hooks.js

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -165,7 +165,7 @@ export function useStartUploadAsync({ inspectionId, sights, uploads, task, onFin
165165
});
166166

167167
// call onFinish callback when capturing the last picture
168-
if (ids[ids.length - 1] === id) { onFinish(); }
168+
if (ids[ids.length - 1] === id) { onFinish(); log([`Capture tour has been finished`]); }
169169

170170
const fileType = Platform.OS === 'web' ? 'webp' : 'jpg';
171171
const filename = `${id}-${inspectionId}.${fileType}`;
@@ -204,13 +204,19 @@ export function useStartUploadAsync({ inspectionId, sights, uploads, task, onFin
204204

205205
const result = await monkApi.images.addOne({ inspectionId, data });
206206

207+
// call onFinish callback when capturing the last picture
208+
if (ids[ids.length - 1] === id) { onFinish(); log([`Capture tour has been finished`]); }
209+
207210
dispatch({
208211
type: Actions.uploads.UPDATE_UPLOAD,
209212
payload: { id, status: 'fulfilled', error: null },
210213
});
211214

212215
return result;
213216
} catch (err) {
217+
// call onFinish callback when capturing the last picture
218+
if (ids[ids.length - 1] === id) { onFinish(); log([`Capture tour has been finished`]); }
219+
214220
dispatch({
215221
type: Actions.uploads.UPDATE_UPLOAD,
216222
increment: true,

packages/camera/src/components/Capture/index.js

Lines changed: 70 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,9 @@ import Controls from '../Controls';
1313
import Layout from '../Layout';
1414
import Overlay from '../Overlay';
1515
import Sights from '../Sights';
16+
import UploadCenter from '../UploadCenter';
1617

18+
import Constants from '../../const';
1719
import log from '../../utils/log';
1820

1921
import {
@@ -42,23 +44,32 @@ const styles = StyleSheet.create({
4244
/**
4345
* @param controls
4446
* @param controlsContainerStyle
47+
* @param enableComplianceCheck
4548
* @param footer
4649
* @param fullscreen
4750
* @param initialState
4851
* @param inspectionId
52+
* @param isSubmitting
4953
* @param loading
5054
* @param navigationOptions
5155
* @param offline
5256
* @param onChange
5357
* @param onReady
58+
* @param onCaptureTourFinish
59+
* @param onCaptureTourStart
60+
* @param onComplianceCheckFinish
61+
* @param onComplianceCheckStart
62+
* @param onStartUploadPicture
63+
* @param onFinishUploadPicture
64+
* @param onRetakeAll
5465
* @param onFinish
5566
* @param primaryColor
5667
* @param sightIds
5768
* @param sightsContainerStyle
5869
* @param style
70+
* @param submitButtonLabel
5971
* @param thumbnailStyle
6072
* @param uploads
61-
* @param RenderOnFinish
6273
* @param submitButtonProps
6374
* @param task
6475
* @return {JSX.Element}
@@ -67,23 +78,30 @@ const styles = StyleSheet.create({
6778
export default function Capture({
6879
controls,
6980
controlsContainerStyle,
81+
enableComplianceCheck,
7082
footer,
7183
fullscreen,
7284
initialState,
7385
inspectionId,
86+
isSubmitting,
7487
loading,
7588
navigationOptions,
7689
offline,
7790
onChange,
91+
onCaptureTourFinish,
92+
onCaptureTourStart,
93+
onComplianceCheckFinish,
94+
onComplianceCheckStart,
7895
onReady,
79-
onFinish,
96+
onRetakeAll,
97+
onStartUploadPicture,
98+
onFinishUploadPicture,
8099
orientationBlockerProps,
81100
primaryColor,
82-
renderOnFinish: RenderOnFinish,
83101
sightIds,
84102
sightsContainerStyle,
85103
style,
86-
submitButtonProps,
104+
submitButtonLabel,
87105
task,
88106
thumbnailStyle,
89107
uploads,
@@ -156,7 +174,13 @@ export default function Capture({
156174
const createDamageDetectionAsync = useCreateDamageDetectionAsync();
157175
const takePictureAsync = useTakePictureAsync({ camera });
158176
const setPictureAsync = useSetPictureAsync({ current, sights, uploads });
159-
const startUploadAsync = useStartUploadAsync({ inspectionId, sights, uploads, task, onFinish });
177+
const startUploadAsync = useStartUploadAsync({
178+
inspectionId,
179+
sights,
180+
uploads,
181+
task,
182+
onFinish: onCaptureTourFinish,
183+
});
160184
const checkComplianceAsync = useCheckComplianceAsync({
161185
compliance,
162186
inspectionId,
@@ -222,10 +246,13 @@ export default function Capture({
222246
}, [tour, sightIds]);
223247

224248
useEffect(() => {
225-
if (tourHasFinished) {
226-
log([`Capture tour has been finished`]);
227-
}
228-
}, [camera, tourHasFinished, onFinish]);
249+
if (enableComplianceCheck) { log([`Compliance check is enabled`]); }
250+
}, [enableComplianceCheck]);
251+
252+
useEffect(() => {
253+
log([`Capture tour has been started`]);
254+
onCaptureTourStart();
255+
}, [onCaptureTourStart]);
229256

230257
// END EFFECTS //
231258
// RENDERING //
@@ -252,8 +279,12 @@ export default function Capture({
252279
containerStyle={controlsContainerStyle}
253280
elements={controls}
254281
state={states}
282+
enableComplianceCheck={enableComplianceCheck}
283+
onStartUploadPicture={onStartUploadPicture}
284+
onFinishUploadPicture={onFinishUploadPicture}
255285
/>
256-
), [api, controls, controlsContainerStyle, states]);
286+
), [api, controlsContainerStyle, controls, states, enableComplianceCheck,
287+
onStartUploadPicture, onFinishUploadPicture]);
257288

258289
const children = useMemo(() => (
259290
<>
@@ -274,14 +305,19 @@ export default function Capture({
274305
</>
275306
), [isReady, loading, overlay, primaryColor]);
276307

277-
if (tourHasFinished && RenderOnFinish) {
308+
if (enableComplianceCheck && tourHasFinished) {
278309
return (
279-
<RenderOnFinish
310+
<UploadCenter
280311
{...states}
281-
submitButtonProps={submitButtonProps}
312+
isSubmitting={isSubmitting}
313+
onComplianceCheckFinish={onComplianceCheckFinish}
314+
onComplianceCheckStart={onComplianceCheckStart}
315+
onRetakeAll={onRetakeAll}
316+
submitButtonLabel={submitButtonLabel}
282317
task={task}
283318
inspectionId={inspectionId}
284319
checkComplianceAsync={checkComplianceAsync}
320+
navigationOptions={navigationOptions}
285321
/>
286322
);
287323
}
@@ -313,26 +349,7 @@ export default function Capture({
313349
// END RENDERING //
314350
}
315351

316-
Capture.defaultSightIds = [
317-
'xsuH1g5T', // Beauty Shot
318-
'xfbBpq3Q', // Front Bumper Side Left
319-
'LE9h1xh0', // Front Fender Left
320-
'IVcF1dOP', // Doors Left
321-
'm1rhrZ88', // Front Roof Left
322-
'GvCtVnoD', // Rear Lateral Left
323-
'3vKXafwc', // Rear Fender Left
324-
'XyeyZlaU', // Rear
325-
'Cce1KCd3', // Rear Fender Right
326-
'AoO-nOoM', // Rear Lateral Right
327-
'Pzgw0WGe', // Doors Right
328-
'jqJOb6Ov', // Front Fender Right
329-
'CELBsvYD', // Front Bumper Side Right
330-
'vLcBGkeh', // Front
331-
'IqwSM3', // Front seats
332-
'rSvk2C', // Dashboard
333-
'rj5mhm', // Back seats
334-
'qhKA2z', // Trunk
335-
];
352+
Capture.defaultSightIds = Constants.defaultSightIds;
336353

337354
Capture.propTypes = {
338355
controls: PropTypes.arrayOf(PropTypes.shape({
@@ -341,6 +358,7 @@ Capture.propTypes = {
341358
onPress: PropTypes.func,
342359
})),
343360
controlsContainerStyle: PropTypes.objectOf(PropTypes.any),
361+
enableComplianceCheck: PropTypes.bool,
344362
footer: PropTypes.element,
345363
fullscreen: PropTypes.objectOf(PropTypes.any),
346364
initialState: PropTypes.shape({
@@ -350,6 +368,7 @@ Capture.propTypes = {
350368
uploads: PropTypes.objectOf(PropTypes.any),
351369
}),
352370
inspectionId: PropTypes.string,
371+
isSubmitting: PropTypes.bool,
353372
loading: PropTypes.bool,
354373
navigationOptions: PropTypes.shape({
355374
allowNavigate: PropTypes.bool,
@@ -359,15 +378,22 @@ Capture.propTypes = {
359378
retakeMinTry: PropTypes.number,
360379
}),
361380
offline: PropTypes.objectOf(PropTypes.any),
381+
onCaptureTourFinish: PropTypes.func,
382+
onCaptureTourStart: PropTypes.func,
362383
onChange: PropTypes.func,
384+
onComplianceCheckFinish: PropTypes.func,
385+
onComplianceCheckStart: PropTypes.func,
363386
onFinish: PropTypes.func,
387+
onFinishUploadPicture: PropTypes.func,
364388
onReady: PropTypes.func,
389+
onRetakeAll: PropTypes.func,
390+
onStartUploadPicture: PropTypes.func,
365391
orientationBlockerProps: PropTypes.shape({ title: PropTypes.string }),
366392
primaryColor: PropTypes.string,
367393
renderOnFinish: PropTypes.func,
368394
sightIds: PropTypes.arrayOf(PropTypes.string),
369395
sightsContainerStyle: PropTypes.objectOf(PropTypes.any),
370-
submitButtonProps: PropTypes.shape({ onPress: PropTypes.func.isRequired }),
396+
submitButtonLabel: PropTypes.string,
371397
task: PropTypes.oneOfType([PropTypes.string, PropTypes.object]),
372398
thumbnailStyle: PropTypes.objectOf(PropTypes.any),
373399
uploads: PropTypes.shape({
@@ -404,15 +430,24 @@ Capture.defaultProps = {
404430
retakeMinTry: 1,
405431
},
406432
offline: null,
433+
onCaptureTourFinish: () => {},
434+
onCaptureTourStart: () => {},
407435
onChange: () => {},
436+
onComplianceCheckFinish: () => {},
437+
onComplianceCheckStart: () => {},
408438
onFinish: () => {},
439+
onFinishUploadPicture: () => {},
409440
onReady: () => {},
441+
onStartUploadPicture: () => {},
442+
onRetakeAll: () => {},
410443
orientationBlockerProps: null,
411444
primaryColor: '#FFF',
412445
renderOnFinish: null,
413446
sightIds: Capture.defaultSightIds,
414447
sightsContainerStyle: {},
415-
submitButtonProps: {},
448+
enableComplianceCheck: false,
449+
isSubmitting: false,
450+
submitButtonLabel: 'Skip retaking',
416451
task: 'damage_detection',
417452
thumbnailStyle: {},
418453
uploads: {
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
import { useCallback } from 'react';
2+
import Actions from '../../actions';
3+
4+
const useHandlers = ({ onStartUploadPicture, onFinishUploadPicture, enableComplianceCheck }) => {
5+
const capture = useCallback(async (state, api, event) => {
6+
event.preventDefault();
7+
onStartUploadPicture(state, api);
8+
9+
const {
10+
takePictureAsync,
11+
startUploadAsync,
12+
setPictureAsync,
13+
goNextSight,
14+
checkComplianceAsync,
15+
} = api;
16+
17+
const picture = await takePictureAsync();
18+
setPictureAsync(picture);
19+
20+
const { sights } = state;
21+
const { current, ids } = sights.state;
22+
23+
/**
24+
* Note(Ilyass): We removed the recursive function solution, because it takes too much time,
25+
* instead we re-run the compliance one more time after 1sec of getting the first response
26+
* */
27+
const verifyComplianceStatus = (pictureId, compliances) => {
28+
const hasTodo = Object.values(compliances).some((c) => c.status === 'TODO' || c.is_compliant === null);
29+
30+
if (hasTodo) {
31+
setTimeout(async () => {
32+
await checkComplianceAsync(pictureId, current.metadata.id);
33+
}, 500);
34+
}
35+
};
36+
37+
if (current.index === ids.length - 1) {
38+
const upload = await startUploadAsync(picture);
39+
if (enableComplianceCheck && upload.data?.id) {
40+
const result = await checkComplianceAsync(upload.data.id);
41+
verifyComplianceStatus(upload.data.id, result.data.compliances);
42+
}
43+
44+
onFinishUploadPicture(state, api);
45+
} else {
46+
onFinishUploadPicture(state, api);
47+
goNextSight();
48+
49+
const upload = await startUploadAsync(picture);
50+
if (enableComplianceCheck && upload.data?.id) {
51+
const result = await checkComplianceAsync(upload.data.id);
52+
verifyComplianceStatus(upload.data.id, result.data.compliances);
53+
}
54+
}
55+
}, [enableComplianceCheck, onFinishUploadPicture, onStartUploadPicture]);
56+
57+
const retakeAll = useCallback((sightsIdsToRetake, states, setSightsIds) => {
58+
// adding an initialState that will hold new compliances with `requestCount = 1`
59+
const complianceInitialState = { id: '', status: 'idle', error: null, requestCount: 1, result: null, imageId: null };
60+
const complianceState = {};
61+
sightsIdsToRetake.forEach((id) => { complianceState[id] = { ...complianceInitialState, id }; });
62+
63+
// reset uploads state with the new incoming ones
64+
states.uploads.dispatch({
65+
type: Actions.uploads.RESET_UPLOADS, ids: { sightIds: sightsIdsToRetake } });
66+
67+
// update sightsIds state
68+
setSightsIds({ ids: sightsIdsToRetake, initialState: { compliance: complianceState } });
69+
}, []);
70+
return { capture, retakeAll };
71+
};
72+
export default useHandlers;

0 commit comments

Comments
 (0)