demarches-normaliennes/app/javascript/shared/activestorage/auto-upload.ts

139 lines
3.5 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import invariant from 'tiny-invariant';
import { show, hide, toggle } from '@utils';
import Uploader from './uploader';
import {
FileUploadError,
ERROR_CODE_READ,
FAILURE_CONNECTIVITY
} from './file-upload-error';
type ErrorMessage = {
title: string;
retry: boolean;
};
// Given a file input in a champ with a selected file, upload a file,
// then attach it to the dossier.
//
// On success, the champ is replaced by an HTML fragment describing the attachment.
// On error, a error message is displayed above the input.
export class AutoUpload {
#input: HTMLInputElement;
#uploader: Uploader;
constructor(input: HTMLInputElement, file: File) {
const { directUploadUrl, autoAttachUrl, maxFileSize } = input.dataset;
invariant(directUploadUrl, 'Could not find the direct upload URL.');
this.#input = input;
this.#uploader = new Uploader(
input,
file,
directUploadUrl,
autoAttachUrl,
maxFileSize
);
}
// Create, upload and attach the file.
// On failure, display an error message and throw a FileUploadError.
async start() {
try {
this.begin();
await this.#uploader.start();
this.succeeded();
} catch (error) {
this.failed(error as FileUploadError);
throw error;
} finally {
this.done();
}
}
private begin() {
this.#input.disabled = true;
this.hideErrorMessage();
}
private succeeded() {
this.#input.value = '';
}
private failed(error: FileUploadError) {
if (!document.body.contains(this.#input)) {
return;
}
this.#uploader.progressBar.destroy();
const message = this.messageFromError(error);
this.displayErrorMessage(message);
this.#input.classList.toggle('fr-text-default--error', true);
}
private done() {
this.#input.disabled = false;
}
private messageFromError(error: FileUploadError): ErrorMessage {
const message = error.message || error.toString();
const canRetry = error.status && error.status != 422;
if (error.failureReason == FAILURE_CONNECTIVITY) {
return {
title:
'Le fichier na pas pu être envoyé. Vérifiez votre connexion à Internet, puis ré-essayez.',
retry: true
};
} else if (error.code == ERROR_CODE_READ) {
return {
title:
'Nous narrivons pas à lire ce fichier sur votre appareil. Essayez à nouveau, ou sélectionnez un autre fichier.',
retry: false
};
} else {
return {
title: message,
retry: !!canRetry
};
}
}
private displayErrorMessage(message: ErrorMessage) {
const errorElement = this.errorElement;
if (errorElement) {
show(errorElement);
this.errorTitleElement.textContent = message.title || '';
toggle(this.errorRetryButton, message.retry);
}
}
private hideErrorMessage() {
const errorElement = this.errorElement;
if (errorElement) {
hide(errorElement);
}
}
get errorElement() {
return this.#input
.closest('.attachment')
?.querySelector<HTMLElement>('.attachment-error');
}
get errorTitleElement() {
const element =
this.errorElement?.querySelector<HTMLElement>('.fr-error-text');
invariant(element, 'Could not find the error title element.');
return element;
}
get errorRetryButton() {
const element = this.errorElement?.querySelector<HTMLButtonElement>(
'.attachment-error-retry'
);
invariant(element, 'Could not find the error retry button element.');
return element;
}
}