import { Injectable } from '@angular/core' ; import { ApiResponse , ApiService } from './api.service' ; import { catchError , concatAll , filter , map , mapTo , mergeAll , } from 'rxjs/operators' ; import { Observable , of , OperatorFunction , throwError } from 'rxjs' ; import { HttpClient , HttpEvent , HttpEventType , HttpHeaders , } from '@angular/common/http' ; import getFileType from '../../helpers/get-file-type' ; /**
 * Upload event type
 */ export enum UploadEventType { Progress = 1 , Success , Fail } /**
 * Upload event payload
 */ export interface UploadEventPayload { progress ? : number ; response ? : any ; request ? : { type : string ; } ; } /**
 * Normalized upload event
 */ export interface UploadEvent { type : UploadEventType ; payload : UploadEventPayload ; } /**
 * Angular's HTTP event to our Upload Event.
 * Disabling `includeResponse` will also disable Minds API status success check!
 */ export const httpEventToUploadEvent = ( type , includeResponse = true ) : OperatorFunction < HttpEvent < ApiResponse > , UploadEvent > => input$ => input$ . pipe ( filter ( ( event ) => [ HttpEventType . Sent , HttpEventType . UploadProgress , HttpEventType . ResponseHeader , includeResponse && HttpEventType . Response , ] . filter ( _ => typeof _ === 'number' ) . includes ( event . type ) ) , map ( ( event ) : UploadEvent => { switch ( event . type ) { case HttpEventType . Sent : return { type : UploadEventType . Progress , payload : { progress : 0 } , } ; case HttpEventType . UploadProgress : return { type : UploadEventType . Progress , payload : { progress : event . loaded / ( event . total || + Infinity ) } , } ; case HttpEventType . ResponseHeader : return { type : UploadEventType . Progress , payload : { progress : 1 } , } ; case HttpEventType . Response : return { type : UploadEventType . Success , payload : { request : { type } , response : event . body , } , } ; } } ) , catchError ( e => { let errorMessage = 'E_CLIENT_ERROR' ; if ( e . error && e . error . message ) { errorMessage = e . error . message ; } else if ( e . message ) { errorMessage = e . message ; } return of ( { type : UploadEventType . Fail , payload : { request : { type } , response : errorMessage , } , } ) ; } ) ) ; /**
 * Service that handle video and image uploads as attachments
 */ @ Injectable ( ) export class AttachmentApiService { param api param http constructor ( protected api , protected http ) { } param file param metadata upload ( file , metadata ) { if ( ! file ) { return of ( null ) ; } if ( /image\/.+/ . test ( file . type ) ) { return this . uploadToApi ( file , metadata ) ; } else if ( /video\/.+/ . test ( file . type ) ) { return this . uploadToS3 ( file , metadata ) ; } return throwError ( new Error ( `You cannot attach a ${ file . type } file` ) ) ; } param file param metadata protected uploadToS3 ( file , metadata ) { const init = of ( { type : UploadEventType . Progress , payload : { progress : 100 } , } ) ; const fileType = getFileType ( file ) ; const upload = this . api . put ( `api/v2/media/upload/prepare/${ file . type . split ( '/' ) [ 0 ] }` ) . pipe ( map ( ( response ) : Observable < UploadEvent > => { const { lease } = response ; const uploadToPresignedUrl = this . http . put ( lease . presigned_url , file , { headers : new HttpHeaders ( { 'Content-Type' : file . type , } ) , reportProgress : true , observe : 'events' , } ) . pipe ( httpEventToUploadEvent ( fileType , false ) ) ; const complete = this . api . put ( `api/v2/media/upload/complete/${ lease . media_type }/${ lease . guid }` ) . pipe ( map ( ( ) : UploadEvent => ( { type : UploadEventType . Success , payload : { request : { type : fileType } , response : lease , } , } ) ) ) ; return of ( uploadToPresignedUrl , complete ) . pipe ( concatAll ( ) ) ; } ) , mergeAll ( ) ) ; return of ( init , upload ) . pipe ( concatAll ( ) ) ; } param file param metadata protected uploadToApi ( file , metadata ) { return this . api . upload ( `api/v1/media` , { file , ... ( metadata || { } ) , } , { upload : true } ) . pipe ( httpEventToUploadEvent ( getFileType ( file ) ) ) ; } param guid remove ( guid ) { return this . api . delete ( `api/v1/media/${ guid }` ) . pipe ( mapTo ( true ) ) ; } }