Преглед изворни кода

feat(admin-ui): Display visual feedback when uploading Assets

Michael Bromley пре 5 година
родитељ
комит
ca6c30fbe4

+ 1 - 0
packages/admin-ui/src/lib/catalog/src/components/asset-list/asset-list.component.html

@@ -12,6 +12,7 @@
         <vdr-action-bar-items locationId="asset-list"></vdr-action-bar-items>
         <vdr-asset-file-input
             (selectFiles)="filesSelected($event)"
+            [uploading]="uploading"
             dropZoneTarget=".content-area"
         ></vdr-asset-file-input>
     </vdr-ab-right>

+ 17 - 12
packages/admin-ui/src/lib/catalog/src/components/asset-list/asset-list.component.ts

@@ -14,7 +14,7 @@ import {
 import { SortOrder } from '@vendure/common/lib/generated-shop-types';
 import { PaginationInstance } from 'ngx-pagination';
 import { combineLatest, EMPTY, Observable } from 'rxjs';
-import { debounceTime, map, switchMap, takeUntil } from 'rxjs/operators';
+import { debounceTime, finalize, map, switchMap, takeUntil } from 'rxjs/operators';
 
 @Component({
     selector: 'vdr-asset-list',
@@ -24,6 +24,7 @@ import { debounceTime, map, switchMap, takeUntil } from 'rxjs/operators';
 export class AssetListComponent extends BaseListComponent<GetAssetList.Query, GetAssetList.Items>
     implements OnInit {
     searchTerm = new FormControl('');
+    uploading = false;
     paginationConfig$: Observable<PaginationInstance>;
 
     constructor(
@@ -36,7 +37,7 @@ export class AssetListComponent extends BaseListComponent<GetAssetList.Query, Ge
         super(router, route);
         super.setQueryFn(
             (...args: any[]) => this.dataService.product.getAssetList(...args),
-            data => data.assets,
+            (data) => data.assets,
             (skip, take) => ({
                 options: {
                     skip,
@@ -66,24 +67,28 @@ export class AssetListComponent extends BaseListComponent<GetAssetList.Query, Ge
 
     filesSelected(files: File[]) {
         if (files.length) {
-            this.dataService.product.createAssets(files).subscribe(res => {
-                super.refresh();
-                this.notificationService.success(_('asset.notify-create-assets-success'), {
-                    count: files.length,
+            this.uploading = true;
+            this.dataService.product
+                .createAssets(files)
+                .pipe(finalize(() => (this.uploading = false)))
+                .subscribe((res) => {
+                    super.refresh();
+                    this.notificationService.success(_('asset.notify-create-assets-success'), {
+                        count: files.length,
+                    });
                 });
-            });
         }
     }
 
     deleteAsset(asset: Asset) {
         this.showModalAndDelete(asset.id)
             .pipe(
-                switchMap(response => {
+                switchMap((response) => {
                     if (response.result === DeletionResult.DELETED) {
                         return [true];
                     } else {
                         return this.showModalAndDelete(asset.id, response.message || '').pipe(
-                            map(r => r.result === DeletionResult.DELETED),
+                            map((r) => r.result === DeletionResult.DELETED),
                         );
                     }
                 }),
@@ -95,7 +100,7 @@ export class AssetListComponent extends BaseListComponent<GetAssetList.Query, Ge
                     });
                     this.refresh();
                 },
-                err => {
+                (err) => {
                     this.notificationService.error(_('common.notify-delete-error'), {
                         entity: 'Asset',
                     });
@@ -114,8 +119,8 @@ export class AssetListComponent extends BaseListComponent<GetAssetList.Query, Ge
                 ],
             })
             .pipe(
-                switchMap(res => (res ? this.dataService.product.deleteAsset(assetId, !!message) : EMPTY)),
-                map(res => res.deleteAsset),
+                switchMap((res) => (res ? this.dataService.product.deleteAsset(assetId, !!message) : EMPTY)),
+                map((res) => res.deleteAsset),
             );
     }
 }

+ 7 - 1
packages/admin-ui/src/lib/core/src/shared/components/asset-file-input/asset-file-input.component.html

@@ -1,7 +1,13 @@
 <input type="file" class="file-input" #fileInput (change)="select($event)" multiple />
-<button class="btn btn-primary" (click)="fileInput.click()">
+<button class="btn btn-primary" (click)="fileInput.click()" [disabled]="uploading">
+    <ng-container *ngIf="uploading; else selectable" >
+        <clr-spinner clrInline></clr-spinner>
+        {{ 'asset.uploading' | translate }}
+    </ng-container>
+    <ng-template #selectable>
     <clr-icon shape="upload-cloud"></clr-icon>
     {{ 'asset.upload-assets' | translate }}
+    </ng-template>
 </button>
 <div
     class="drop-zone"

+ 2 - 1
packages/admin-ui/src/lib/core/src/shared/components/asset-file-input/asset-file-input.component.ts

@@ -24,6 +24,7 @@ export class AssetFileInputComponent implements OnInit {
      * drop zone. Defaults to `body`.
      */
     @Input() dropZoneTarget = 'body';
+    @Input() uploading = false;
     @Output() selectFiles = new EventEmitter<File[]>();
     dragging = false;
     overDropZone = false;
@@ -64,7 +65,7 @@ export class AssetFileInputComponent implements OnInit {
         this.dragging = false;
         this.overDropZone = false;
         const files = Array.from(event.dataTransfer ? event.dataTransfer.items : [])
-            .map(i => i.getAsFile())
+            .map((i) => i.getAsFile())
             .filter(notNullOrUndefined);
         this.selectFiles.emit(files);
     }

+ 1 - 0
packages/admin-ui/src/lib/core/src/shared/components/asset-picker-dialog/asset-picker-dialog.component.html

@@ -3,6 +3,7 @@
         {{ 'asset.select-assets' | translate }}
         <vdr-asset-file-input
             (selectFiles)="createAssets($event)"
+            [uploading]="uploading"
             dropZoneTarget=".modal-content"
         ></vdr-asset-file-input>
     </div>

+ 19 - 17
packages/admin-ui/src/lib/core/src/shared/components/asset-picker-dialog/asset-picker-dialog.component.ts

@@ -3,7 +3,7 @@ import { FormControl } from '@angular/forms';
 import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
 import { PaginationInstance } from 'ngx-pagination';
 import { Observable, Subject } from 'rxjs';
-import { debounceTime, map, takeUntil, tap } from 'rxjs/operators';
+import { debounceTime, finalize, map, takeUntil, tap } from 'rxjs/operators';
 
 import { Asset, GetAssetList, SortOrder } from '../../../common/generated-types';
 import { DataService } from '../../../data/providers/data.service';
@@ -31,6 +31,7 @@ export class AssetPickerDialogComponent implements OnInit, OnDestroy, Dialog<Ass
     resolveWith: (result?: Asset[]) => void;
     selection: Asset[] = [];
     searchTerm = new FormControl('');
+    uploading = false;
     private listQuery: QueryResult<GetAssetList.Query, GetAssetList.Variables>;
     private destroy$ = new Subject<void>();
 
@@ -39,15 +40,12 @@ export class AssetPickerDialogComponent implements OnInit, OnDestroy, Dialog<Ass
     ngOnInit() {
         this.listQuery = this.dataService.product.getAssetList(this.paginationConfig.itemsPerPage, 0);
         this.assets$ = this.listQuery.stream$.pipe(
-            tap(result => (this.paginationConfig.totalItems = result.assets.totalItems)),
-            map(result => result.assets.items),
+            tap((result) => (this.paginationConfig.totalItems = result.assets.totalItems)),
+            map((result) => result.assets.items),
         );
         this.searchTerm.valueChanges
-            .pipe(
-                debounceTime(250),
-                takeUntil(this.destroy$),
-            )
-            .subscribe(searchTerm => {
+            .pipe(debounceTime(250), takeUntil(this.destroy$))
+            .subscribe((searchTerm) => {
                 this.fetchPage(
                     this.paginationConfig.currentPage,
                     this.paginationConfig.itemsPerPage,
@@ -89,16 +87,20 @@ export class AssetPickerDialogComponent implements OnInit, OnDestroy, Dialog<Ass
 
     createAssets(files: File[]) {
         if (files.length) {
-            this.dataService.product.createAssets(files).subscribe(res => {
-                this.fetchPage(
-                    this.paginationConfig.currentPage,
-                    this.paginationConfig.itemsPerPage,
-                    this.searchTerm.value,
-                );
-                this.notificationService.success(_('asset.notify-create-assets-success'), {
-                    count: files.length,
+            this.uploading = true;
+            this.dataService.product
+                .createAssets(files)
+                .pipe(finalize(() => (this.uploading = false)))
+                .subscribe((res) => {
+                    this.fetchPage(
+                        this.paginationConfig.currentPage,
+                        this.paginationConfig.itemsPerPage,
+                        this.searchTerm.value,
+                    );
+                    this.notificationService.success(_('asset.notify-create-assets-success'), {
+                        count: files.length,
+                    });
                 });
-            });
         }
     }
 

+ 3 - 3
packages/admin-ui/src/lib/static/i18n-messages/en.json

@@ -21,7 +21,8 @@
     "update-focal-point": "Update point",
     "update-focal-point-error": "Could not update focal point",
     "update-focal-point-success": "Updated focal point",
-    "upload-assets": "Upload assets"
+    "upload-assets": "Upload assets",
+    "uploading": "Uploading..."
   },
   "breadcrumb": {
     "administrators": "Administrators",
@@ -665,7 +666,6 @@
     "job-error": "Job error",
     "job-queue-name": "Queue name",
     "job-result": "Job result",
-    "job-state": "Job state",
-    "jobs-in-progress": "{ count } {count, plural, one {job} other {jobs}} in progress"
+    "job-state": "Job state"
   }
 }