From 05aea07c0eb66662c7d35fdcfd66a581002223b2 Mon Sep 17 00:00:00 2001 From: ushahidlee Date: Mon, 21 Oct 2024 22:52:12 +0200 Subject: [PATCH] error handling and icons --- .../src/app/core/interfaces/media.ts | 9 ++- .../media-uploader.component.html | 15 +++-- .../media-uploader.component.scss | 57 +++++++++++++----- .../media-uploader.component.ts | 58 +++++++++++++++---- 4 files changed, 107 insertions(+), 32 deletions(-) diff --git a/apps/web-mzima-client/src/app/core/interfaces/media.ts b/apps/web-mzima-client/src/app/core/interfaces/media.ts index 4a97d1aee5..e82156b695 100644 --- a/apps/web-mzima-client/src/app/core/interfaces/media.ts +++ b/apps/web-mzima-client/src/app/core/interfaces/media.ts @@ -13,7 +13,14 @@ type MediaType = { fileTypes: string; }; -type MediaFileStatus = 'ready' | 'upload' | 'uploading' | 'uploaded' | 'error' | 'delete'; +type MediaFileStatus = + | 'ready' + | 'upload' + | 'uploading' + | 'uploaded' + | 'error' + | 'too_big' + | 'delete'; const mediaTypes = new Map([ [ diff --git a/apps/web-mzima-client/src/app/post/media-uploader/media-uploader.component.html b/apps/web-mzima-client/src/app/post/media-uploader/media-uploader.component.html index f8cb3f99d0..b3f53f8a5e 100644 --- a/apps/web-mzima-client/src/app/post/media-uploader/media-uploader.component.html +++ b/apps/web-mzima-client/src/app/post/media-uploader/media-uploader.component.html @@ -32,28 +32,33 @@
+
+ {{ ErrorEnum.MAX_SIZE | translate }} +
+
Uploading...
-
- - + + + + - +
diff --git a/apps/web-mzima-client/src/app/post/media-uploader/media-uploader.component.scss b/apps/web-mzima-client/src/app/post/media-uploader/media-uploader.component.scss index 7d57b1ef9f..ead9e8c888 100644 --- a/apps/web-mzima-client/src/app/post/media-uploader/media-uploader.component.scss +++ b/apps/web-mzima-client/src/app/post/media-uploader/media-uploader.component.scss @@ -7,20 +7,41 @@ flex-direction: row; padding: 4px; border: 1px solid #f5f5f5; + .thumbnail { + max-width: 100px; + max-height: 100px; + } &.ready { border: 1px solid #f5f5f5; } &.uploaded { border: 1px solid #17710f; + .status-button { + background-color: #17710f; + } } - &.error { + &.error, + &.too_big { border: 1px solid #962923; } border-radius: 4px; - .thumbnail, .media-blank { - max-width: 64px; - max-height: 64px; + height: 100%; + flex-grow: 1; + display: flex; + flex-direction: row; + align-items: center; + .xicon { + padding: 4px; + display: block; + font-size: 3rem; + width: 64px; + height: auto; + } + .media-error { + width: 100%; + text-align: center; + } .loading-spinner { width: 100%; height: 100%; @@ -34,8 +55,8 @@ justify-content: center; .filename { font-size: 14px; - text-overflow: ellipsis; - white-space: nowrap; + text-overflow: wrap; + white-space: wrap; overflow: hidden; } .filesize { @@ -45,19 +66,18 @@ } .status-button { align-self: flex-start; - flex: 0 0 auto; + flex: 0 0; position: relative; - height: 24px; - width: 24px; + min-height: 24px; + min-width: 24px; border-radius: 50%; background-color: #919397; .xicon { position: absolute; - top: 7px; - left: 7px; + top: 2px; + left: 2px; color: #fff; - transform: scale(1.5); - font-weight: bold; + font-size: 20px; } } } @@ -66,11 +86,17 @@ grid-template-columns: 1fr 1fr; column-gap: 4px; row-gap: 4px; + .media-preview { + height: 100px; + } } &-audio { width: 100%; display: inline-flex; flex-direction: column; + .media-preview { + height: auto; + } audio { width: 50%; } @@ -81,9 +107,12 @@ } &-document { display: grid; - grid-template-columns: auto auto; + grid-template-columns: 1fr 1fr; column-gap: 4px; row-gap: 4px; + .media-preview { + height: 64px; + } } img { diff --git a/apps/web-mzima-client/src/app/post/media-uploader/media-uploader.component.ts b/apps/web-mzima-client/src/app/post/media-uploader/media-uploader.component.ts index 633832cea4..1807272b74 100644 --- a/apps/web-mzima-client/src/app/post/media-uploader/media-uploader.component.ts +++ b/apps/web-mzima-client/src/app/post/media-uploader/media-uploader.component.ts @@ -1,6 +1,12 @@ import { HttpErrorResponse, HttpEventType /*, HttpProgressEvent */ } from '@angular/common/http'; import { Component, forwardRef, Input, OnInit } from '@angular/core'; -import { ControlValueAccessor, FormControl, NG_VALUE_ACCESSOR } from '@angular/forms'; +import { + ControlValueAccessor, + FormControl, + NG_VALIDATORS, + NG_VALUE_ACCESSOR, + ValidationErrors, +} from '@angular/forms'; import { DomSanitizer } from '@angular/platform-browser'; import { formHelper } from '@helpers'; import { MediaService } from '@mzima-client/sdk'; @@ -24,6 +30,11 @@ import { useExisting: forwardRef(() => MediaUploaderComponent), multi: true, }, + { + provide: NG_VALIDATORS, + useExisting: MediaUploaderComponent, + multi: true, + }, ], }) export class MediaUploaderComponent implements ControlValueAccessor, OnInit { @@ -55,9 +66,10 @@ export class MediaUploaderComponent implements ControlValueAccessor, OnInit { this.mediaType = mediaTypes.get(this.media)!; } - // Import Helper Methods for the template + // helper imports for the template getDocumentThumbnail = getDocumentThumbnail; getFileSize = getFileSize; + ErrorEnum = ErrorEnum; writeValue(obj: MediaFile[]): void { if (Array.isArray(obj)) { @@ -77,6 +89,17 @@ export class MediaUploaderComponent implements ControlValueAccessor, OnInit { this.isDisabled = isDisabled; } + validate(): ValidationErrors | null { + for (const upload of this.mediaFiles) { + if (upload.status === 'error') return { uploadError: true }; + + if (upload.status === 'too_big') return { uploadsInvalid: true }; + + if (upload.status === 'uploading') return { uploadInProgress: true }; + } + return null; + } + onFileSelected(event: Event) { const inputElement = event.target as HTMLInputElement; @@ -101,6 +124,9 @@ export class MediaUploaderComponent implements ControlValueAccessor, OnInit { mimeType: aFile.type, url: this.sanitizer.bypassSecurityTrustUrl(URL.createObjectURL(photoUrl)), }; + if (mediaFile.size! > this.maxUploadSize * 1000000) { + mediaFile.status = 'too_big'; + } this.mediaFiles.push(mediaFile); } } @@ -108,9 +134,6 @@ export class MediaUploaderComponent implements ControlValueAccessor, OnInit { this.mediaFiles .filter((mediaFile) => mediaFile.status === 'uploading') .forEach((aMediaFile) => { - // }) - // for (let i = 0; i < this.mediaFiles.length; i++) { - // const aMediaFile = this.mediaFiles[i]; const uploadObservable: Observable = this.mediaService .uploadFileProgress(aMediaFile.file!, '') .pipe( @@ -143,7 +166,7 @@ export class MediaUploaderComponent implements ControlValueAccessor, OnInit { mediaFile.generatedId, uploadEvent.body, (theMediaFile) => { - theMediaFile.status = 'ready'; + // theMediaFile.status = 'ready'; return theMediaFile; }, ); @@ -181,6 +204,7 @@ export class MediaUploaderComponent implements ControlValueAccessor, OnInit { console.log(results); }); } + this.onChange(this.mediaFiles); } } @@ -198,16 +222,26 @@ export class MediaUploaderComponent implements ControlValueAccessor, OnInit { } } - if (mediaFile && (mediaFile.status === 'upload' || mediaFile.status === 'ready')) { - const confirmed = await this.confirm.open({ - title: this.translate.instant('notify.default.are_you_sure_you_want_to_delete_this'), - description: this.translate.instant('notify.default.proceed_warning'), - }); + if (mediaFile) { + if (mediaFile.status === 'upload' || mediaFile.status === 'ready') { + const confirmed = await this.confirm.open({ + title: this.translate.instant('notify.default.are_you_sure_you_want_to_delete_this'), + description: this.translate.instant('notify.default.proceed_warning'), + }); - if (!confirmed) return; + if (!confirmed) return; + } else if (mediaFile.status === 'error' || mediaFile.status === 'too_big') { + this.error = ErrorEnum.NONE; + } else { + return; + } // this.mediaFiles[index].status = 'delete'; this.mediaFiles.splice(index, 1); + const filteredItems = this.mediaFiles.filter( + (item) => item.status === 'error' || item.status === 'too_big', + ); + if (filteredItems.length === 0) this.error = ErrorEnum.NONE; } this.onChange(this.mediaFiles); }