demarches-normaliennes/app/javascript/new_design/dossiers/auto-save.js
Pierre de La Morinerie e1909ed29f brouillon: redirect to sign-in when disconnected
There are two cases where the draft auto-save might fail because the
user is no longer authenticated:

- The user signed-out in another tab,
- The brower quit and re-opened, so the Session cookie expired.

In both cases, the auto-save will never succeed until the user
authenticates again, so displaying a "Retry" button is cruel.

Moreover, in plus of all auto-save requests failing with a small error,
the actual hard failure only occurs after filling all the form and
trying to submit it. Then the user is redirected to the sign-in page –
but all their changes are lost.

Instead, we now redirect to the sign-in page on the first 401 error
during the auto-save, let the user sign-in, and then redirect back to
the form.
2021-07-22 11:58:02 +02:00

108 lines
3.1 KiB
JavaScript

import AutoSaveController from './auto-save-controller.js';
import {
debounce,
delegate,
fire,
enable,
disable,
hasClass,
addClass,
removeClass
} from '@utils';
const AUTOSAVE_DEBOUNCE_DELAY = gon.autosave.debounce_delay;
const AUTOSAVE_STATUS_VISIBLE_DURATION = gon.autosave.status_visible_duration;
// Create a controller responsible for queuing autosave operations.
const autoSaveController = new AutoSaveController();
function enqueueAutosaveRequest() {
const form = document.querySelector(FORM_SELECTOR);
autoSaveController.enqueueAutosaveRequest(form);
}
//
// Whenever a 'change' event is triggered on one of the form inputs, try to autosave.
//
const FORM_SELECTOR = 'form#dossier-edit-form.autosave-enabled';
const INPUTS_SELECTOR = `${FORM_SELECTOR} input:not([type=file]), ${FORM_SELECTOR} select, ${FORM_SELECTOR} textarea`;
const RETRY_BUTTON_SELECTOR = '.autosave-retry';
// When an autosave is requested programatically, auto-save the form immediately
addEventListener('autosave:trigger', (event) => {
const form = event.target.closest('form');
if (form && form.classList.contains('autosave-enabled')) {
enqueueAutosaveRequest();
}
});
// When the "Retry" button is clicked, auto-save the form immediately
delegate('click', RETRY_BUTTON_SELECTOR, enqueueAutosaveRequest);
// When an input changes, batches changes for N seconds, then auto-save the form
delegate(
'change',
INPUTS_SELECTOR,
debounce(enqueueAutosaveRequest, AUTOSAVE_DEBOUNCE_DELAY)
);
//
// Display some UI during the autosave
//
addEventListener('autosave:enqueue', () => {
disable(document.querySelector('button.autosave-retry'));
});
addEventListener('autosave:end', () => {
enable(document.querySelector('button.autosave-retry'));
setState('succeeded');
hideSucceededStatusAfterDelay();
});
addEventListener('autosave:error', (event) => {
let error = event.detail;
if (error.xhr.status == 401) {
// If we are unauthenticated, reload the page using a GET request.
// This will allow Devise to properly redirect us to sign-in, and then back to this page.
document.location.reload();
return;
}
enable(document.querySelector('button.autosave-retry'));
setState('failed');
logError(event.detail);
});
function setState(state) {
const autosave = document.querySelector('.autosave');
if (autosave) {
// Re-apply the state even if already present, to get a nice animation
removeClass(autosave, 'autosave-state-idle');
removeClass(autosave, 'autosave-state-succeeded');
removeClass(autosave, 'autosave-state-failed');
autosave.offsetHeight; // flush animations
addClass(autosave, `autosave-state-${state}`);
}
}
function hideSucceededStatus() {
const autosave = document.querySelector('.autosave');
if (hasClass(autosave, 'autosave-state-succeeded')) {
setState('idle');
}
}
const hideSucceededStatusAfterDelay = debounce(
hideSucceededStatus,
AUTOSAVE_STATUS_VISIBLE_DURATION
);
function logError(error) {
if (error && error.message) {
error.message = `[Autosave] ${error.message}`;
console.error(error);
fire(document, 'sentry:capture-exception', error);
}
}