Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import type { FocusTrap } from 'focus-trap';
import type { FullScreenRefHandle } from '@18f/identity-components';
import { useDidUpdateEffect } from '@18f/identity-react-hooks';
import { useI18n } from '@18f/identity-react-i18n';
import { removeUnloadProtection } from '@18f/identity-url';
import AcuantCamera, { AcuantDocumentType } from './acuant-camera';
import type {
AcuantCaptureFailureError,
Expand Down Expand Up @@ -338,6 +339,8 @@ function AcuantCapture(
const {
failedCaptureAttempts,
onFailedCaptureAttempt,
failedCameraPermissionAttempts,
onFailedCameraPermissionAttempt,
onResetFailedCaptureAttempts,
failedSubmissionAttempts,
forceNativeCamera,
Expand Down Expand Up @@ -561,6 +564,14 @@ function AcuantCapture(
setAcuantFailureCookie(null);

onCameraAccessDeclined();

// Due to a bug with Safari on iOS we force the page to refresh on the third
// time a user denies permissions.
onFailedCameraPermissionAttempt();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I posted this in Slack, but realized I probably should have posted it here instead (with a couple of edits for clarity).

I've tested this branch out locally, and I see something that I'm not sure is intended behavior, or perhaps I'm misinterpreting text. It does look like this behavior is in the video provided, so it's probably me missing something.

With "after permissions being denied three times", I expected the page refresh to happen after I hit cancel on the the third try. But really, after I hit the "front of your license" button area 3x, saw the error message, and then I hit the "front of your license" button area for a 4th time, that is when it refreshes.

It feels a little jarring to me for that to be the timing, but maybe that's just me and this is behavior as intended.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for mentioning this, it is jarring. I think we want to proceed as is.

  • This should not be happening for too many folks.
  • This is a mitigation, we aren't able to truly fix the issue, so we're trying to unblock users which this achieves.
    It would be nice to revisit this at a later date and clean it up, but for now I think "users don't get stuck" is achieved in this PR.

if (failedCameraPermissionAttempts > 2) {
removeUnloadProtection();
window.location.reload();
}
} else if (code === SEQUENCE_BREAK_CODE) {
setOwnErrorMessage(
`${t('doc_auth.errors.upload_error')} ${t('errors.messages.try_again')
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,12 +27,24 @@ interface FailedCaptureAttemptsContextInterface {
*/
failedSubmissionAttempts: number;

/**
* There's a bug with Safari on iOS where if you deny camera permissions
* three times the prompt stops appearing. To avoid this we keep track
* and force a full page reload on the third time.
*/
failedCameraPermissionAttempts: number;

/**
* Callback when submission attempt fails.
* Used to increment the failedSubmissionAttempts
*/
onFailedSubmissionAttempt: (failedImageFingerprints: UploadedImageFingerprints) => void;

/**
* A wrapper around incrementFailedCameraPermissionAttempts
*/
onFailedCameraPermissionAttempt: () => void;

/**
* The maximum number of failed Acuant capture attempts
* before use of the native camera option is triggered
Expand Down Expand Up @@ -79,8 +91,10 @@ const DEFAULT_LAST_ATTEMPT_METADATA: CaptureAttemptMetadata = {
const FailedCaptureAttemptsContext = createContext<FailedCaptureAttemptsContextInterface>({
failedCaptureAttempts: 0,
failedSubmissionAttempts: 0,
failedCameraPermissionAttempts: 0,
onFailedCaptureAttempt: () => {},
onFailedSubmissionAttempt: () => {},
onFailedCameraPermissionAttempt: () => {},
onResetFailedCaptureAttempts: () => {},
maxCaptureAttemptsBeforeNativeCamera: Infinity,
maxSubmissionAttemptsBeforeNativeCamera: Infinity,
Expand Down Expand Up @@ -110,6 +124,7 @@ function FailedCaptureAttemptsContextProvider({
const [failedCaptureAttempts, incrementFailedCaptureAttempts, onResetFailedCaptureAttempts] =
useCounter();
const [failedSubmissionAttempts, incrementFailedSubmissionAttempts] = useCounter();
const [failedCameraPermissionAttempts, incrementFailedCameraPermissionAttempts] = useCounter();

const [failedSubmissionImageFingerprints, setFailedSubmissionImageFingerprints] =
useState<UploadedImageFingerprints>(failedFingerprints);
Expand All @@ -124,6 +139,10 @@ function FailedCaptureAttemptsContextProvider({
setFailedSubmissionImageFingerprints(failedOnes);
}

function onFailedCameraPermissionAttempt() {
incrementFailedCameraPermissionAttempts();
}

const forceNativeCamera =
failedCaptureAttempts >= maxCaptureAttemptsBeforeNativeCamera ||
failedSubmissionAttempts >= maxSubmissionAttemptsBeforeNativeCamera;
Expand All @@ -136,6 +155,8 @@ function FailedCaptureAttemptsContextProvider({
onResetFailedCaptureAttempts,
failedSubmissionAttempts,
onFailedSubmissionAttempt,
failedCameraPermissionAttempts,
onFailedCameraPermissionAttempt,
maxCaptureAttemptsBeforeNativeCamera,
maxSubmissionAttemptsBeforeNativeCamera,
lastAttemptMetadata,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ describe('document-capture/context/failed-capture-attempts', () => {
'onFailedSubmissionAttempt',
'onResetFailedCaptureAttempts',
'maxCaptureAttemptsBeforeNativeCamera',
'onFailedCameraPermissionAttempt',
'failedCameraPermissionAttempts',
'maxSubmissionAttemptsBeforeNativeCamera',
'lastAttemptMetadata',
'failedSubmissionImageFingerprints',
Expand Down