-
Notifications
You must be signed in to change notification settings - Fork 167
LG-11984: document capture selfie hint text #9901
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
c3eb318
c5bb6be
e6859d2
7ab0048
653fc52
d5341ad
4d917be
63afc69
13d9c7c
542fb66
a9d76a5
7e057cd
aeef738
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -337,6 +337,7 @@ function AcuantCapture( | |
| const [attempt, incrementAttempt] = useCounter(1); | ||
| const [acuantFailureCookie, setAcuantFailureCookie, refreshAcuantFailureCookie] = | ||
| useCookie('AcuantCameraHasFailed'); | ||
| const [imageCaptureText, setImageCaptureText] = useState(''); | ||
| // There's some pretty significant changes to this component when it's used for | ||
| // selfie capture vs document image capture. This controls those changes. | ||
| const selfieCapture = name === 'selfie'; | ||
|
|
@@ -653,6 +654,10 @@ function AcuantCapture( | |
| }); | ||
| } | ||
|
|
||
| function onImageCaptureFeedback(text: string) { | ||
|
||
| setImageCaptureText(text); | ||
| } | ||
|
|
||
| return ( | ||
| <div className={[className, 'document-capture-acuant-capture'].filter(Boolean).join(' ')}> | ||
| {isCapturingEnvironment && !selfieCapture && ( | ||
|
|
@@ -678,11 +683,13 @@ function AcuantCapture( | |
| onImageCaptureFailure={onSelfieCaptureFailure} | ||
| onImageCaptureOpen={onSelfieCaptureOpen} | ||
| onImageCaptureClose={onSelfieCaptureClosed} | ||
| onImageCaptureFeedback={onImageCaptureFeedback} | ||
| > | ||
| <AcuantSelfieCaptureCanvas | ||
| fullScreenRef={fullScreenRef} | ||
| fullScreenLabel={t('doc_auth.accessible_labels.document_capture_dialog')} | ||
| onRequestClose={() => setIsCapturingEnvironment(false)} | ||
| imageCaptureText={imageCaptureText} | ||
| /> | ||
| </AcuantSelfieCamera> | ||
| )} | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,5 +1,6 @@ | ||
| import { useContext, useEffect } from 'react'; | ||
| import type { ReactNode } from 'react'; | ||
| import { t } from '@18f/identity-i18n'; | ||
| import AcuantContext from '../context/acuant'; | ||
|
|
||
| declare global { | ||
|
|
@@ -43,6 +44,11 @@ interface AcuantSelfieCameraContextProps { | |
| * when the fullscreen selfie capture page has been closed | ||
| */ | ||
| onImageCaptureClose: () => void; | ||
| /** | ||
| * Capture hint text from onDetection callback, tells the user | ||
| * why the acuant sdk cannot capture a selfie. | ||
| */ | ||
| onImageCaptureFeedback: (text: string) => void; | ||
| /** | ||
| * React children node | ||
| */ | ||
|
|
@@ -63,8 +69,6 @@ interface FaceCaptureCallback { | |
| interface FaceDetectionStates { | ||
| FACE_NOT_FOUND: string; | ||
| TOO_MANY_FACES: string; | ||
| FACE_ANGLE_TOO_LARGE: string; | ||
| PROBABILITY_TOO_SMALL: string; | ||
| FACE_TOO_SMALL: string; | ||
| FACE_CLOSE_TO_BORDER: string; | ||
| } | ||
|
|
@@ -74,6 +78,7 @@ function AcuantSelfieCamera({ | |
| onImageCaptureFailure = () => {}, | ||
| onImageCaptureOpen = () => {}, | ||
| onImageCaptureClose = () => {}, | ||
| onImageCaptureFeedback = () => {}, | ||
| children, | ||
| }: AcuantSelfieCameraContextProps) { | ||
| const { isReady, setIsActive } = useContext(AcuantContext); | ||
|
|
@@ -85,7 +90,8 @@ function AcuantSelfieCamera({ | |
| // Until then, no actions are executed and the user sees only the camera stream. | ||
| // You can opt to display an alert before the callback is triggered. | ||
| }, | ||
| onDetection: () => { | ||
| onDetection: (text) => { | ||
| onImageCaptureFeedback(text); | ||
| // Triggered when the face does not pass the scan. The UI element | ||
| // should be updated here to provide guidence to the user | ||
| }, | ||
|
|
@@ -104,6 +110,7 @@ function AcuantSelfieCamera({ | |
| }, | ||
| onPhotoTaken: () => { | ||
| // The photo has been taken and it's showing a preview with a button to accept or retake the image. | ||
| onImageCaptureFeedback(''); | ||
| }, | ||
| onPhotoRetake: () => { | ||
| // Triggered when retake button is tapped | ||
|
|
@@ -115,12 +122,10 @@ function AcuantSelfieCamera({ | |
| }; | ||
|
|
||
| const faceDetectionStates = { | ||
| FACE_NOT_FOUND: 'FACE NOT FOUND', | ||
| TOO_MANY_FACES: 'TOO MANY FACES', | ||
| FACE_ANGLE_TOO_LARGE: 'FACE ANGLE TOO LARGE', | ||
| PROBABILITY_TOO_SMALL: 'PROBABILITY TOO SMALL', | ||
| FACE_TOO_SMALL: 'FACE TOO SMALL', | ||
| FACE_CLOSE_TO_BORDER: 'TOO CLOSE TO THE FRAME', | ||
| FACE_NOT_FOUND: t('doc_auth.info.selfie_capture_status.face_not_found'), | ||
|
||
| TOO_MANY_FACES: t('doc_auth.info.selfie_capture_status.too_many_faces'), | ||
| FACE_TOO_SMALL: t('doc_auth.info.selfie_capture_status.face_too_small'), | ||
| FACE_CLOSE_TO_BORDER: t('doc_auth.info.selfie_capture_status.face_close_to_border'), | ||
| }; | ||
| const cleanupSelfieCamera = () => { | ||
| window.AcuantPassiveLiveness.end(); | ||
|
|
||
| Original file line number | Diff line number | Diff line change | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -17,13 +17,20 @@ function FullScreenLoadingSpinner({ fullScreenRef, onRequestClose, fullScreenLab | |||||||||
| ); | ||||||||||
| } | ||||||||||
|
|
||||||||||
| function AcuantSelfieCaptureCanvas({ fullScreenRef, onRequestClose, fullScreenLabel }) { | ||||||||||
| function AcuantSelfieCaptureCanvas({ | ||||||||||
| fullScreenRef, | ||||||||||
| onRequestClose, | ||||||||||
| fullScreenLabel, | ||||||||||
| imageCaptureText, | ||||||||||
| }) { | ||||||||||
| const { isReady } = useContext(AcuantContext); | ||||||||||
| // The Acuant SDK script AcuantPassiveLiveness attaches to whatever element has | ||||||||||
| // this id. It then uses that element as the root for the full screen selfie capture | ||||||||||
| const acuantCaptureContainerId = 'acuant-face-capture-container'; | ||||||||||
| return isReady ? ( | ||||||||||
|
||||||||||
| return isReady ? ( | |
| // The <p> to display the text over the selfie capture can be moved to another component | |
| // without too much work, putting it here to keep it conceptually grouped with the selfie capture canvas | |
| return isReady ? ( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Either way, personally think it's too much for an additional component just for 3 lines of static code, actually just one line.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hm, interesting. Seems like that comment might be more confusing than it's worth. Agreed that the <p> should't be it's own component.
What I meant was that it actually doesn't matter where the <p> is (as long as it's in the selfie components) because the CSS removes it from the document flow.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Our use of BEM naming conventions aligns neatly 1-to-1 with React components, where if we had a CSS class name of document-capture-selfie-feedback, I'd expect it to be assigned in a component DocumentCaptureSelfieFeedback. I think that could make sense to split out as a component. As currently implemented, I'd assume it would be called something like acuant-selfie-capture-canvas__selfie-feedback, being an element (BEM) within AcuantSelfieCaptureCanvas.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Following prior comment, an advantage of BEM is it generally avoids issues with needing to qualify (nest) CSS selectors, so I'd expect this to be defined at a top-level.