2022-05-06 19:42:01 +02:00
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 ) {
2022-06-16 15:51:45 +02:00
const { directUploadUrl , autoAttachUrl , maxFileSize } = input . dataset ;
2022-05-06 19:42:01 +02:00
invariant ( directUploadUrl , 'Could not find the direct upload URL.' ) ;
this . # input = input ;
2022-06-16 15:51:45 +02:00
this . # uploader = new Uploader (
input ,
file ,
directUploadUrl ,
autoAttachUrl ,
maxFileSize
) ;
2022-05-06 19:42:01 +02:00
}
// 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 ) ;
2022-11-09 12:33:20 +01:00
this . # input . classList . toggle ( 'fr-text-default--error' , true ) ;
2022-05-06 19:42:01 +02:00
}
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 {
2022-11-09 12:33:20 +01:00
title :
2023-01-31 16:10:17 +01:00
'Le fichier n’ a pas pu être envoyé. Vérifiez votre connexion à Internet, puis ré-essayez. Vérifiez aussi que le pare-feu de votre appareil ou votre réseau autorise l’ envoi de fichier vers ' +
window . location . host ,
2022-05-06 19:42:01 +02:00
retry : true
} ;
} else if ( error . code == ERROR_CODE_READ ) {
return {
2022-11-09 12:33:20 +01:00
title :
'Nous n’ arrivons pas à lire ce fichier sur votre appareil. Essayez à nouveau, ou sélectionnez un autre fichier.' ,
2022-05-06 19:42:01 +02:00
retry : false
} ;
} else {
return {
2022-11-09 12:33:20 +01:00
title : message ,
2022-05-06 19:42:01 +02:00
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() {
2022-10-29 16:08:59 +02:00
return this . # input
. closest ( '.attachment' )
2022-11-24 14:05:53 +01:00
? . querySelector < HTMLElement > ( '.attachment-upload-error' ) ;
2022-05-06 19:42:01 +02:00
}
get errorTitleElement() {
2022-11-09 12:33:20 +01:00
const element =
this . errorElement ? . querySelector < HTMLElement > ( '.fr-error-text' ) ;
2022-05-06 19:42:01 +02:00
invariant ( element , 'Could not find the error title element.' ) ;
return element ;
}
get errorRetryButton() {
const element = this . errorElement ? . querySelector < HTMLButtonElement > (
2022-11-24 14:05:53 +01:00
'.attachment-upload-error-retry'
2022-05-06 19:42:01 +02:00
) ;
invariant ( element , 'Could not find the error retry button element.' ) ;
return element ;
}
}