demarches-normaliennes/app/javascript/new_design/dossiers/auto-save.js
Pierre de La Morinerie 7380d27d78 js: fix null xhr in autosave error handling
Sentry reports many cases of the xhr object being missing in the
error handler.

Ensure the error handling code doesn't crash because of the missing xhr.
2021-08-31 14:54:02 -05: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 && 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);
}
}