From b797fb33dae211d713bf4d3cf721bd323f73cae4 Mon Sep 17 00:00:00 2001
From: Quentin Torck <quentin.torck@aiso.com>
Date: Fri, 24 Apr 2020 15:52:38 +0200
Subject: [PATCH] feat-improve-visualization

---
 .../shared-detail.dialog.scss                 | 13 ++++
 .../shared-file-detail.dialog.html            |  2 +
 .../shared-preview/shared-preview.dialog.scss |  6 +-
 .../shared-preview/shared-preview.dialog.ts   |  1 -
 .../components/file-visualizer.component.html |  8 ++-
 .../components/file-visualizer.component.scss | 33 +++++++--
 .../components/file-visualizer.component.ts   | 10 +--
 .../models/file-visualizer.constant.ts        |  2 +-
 .../services/sound-file-visualizer.service.ts | 68 ++++++++++++++++++-
 ...video-classical-file-visualizer.service.ts |  1 +
 .../services/video-file-visualizer.service.ts |  1 +
 .../visualization/visualization.state.ts      |  9 +++
 src/assets/i18n/de.json                       | 10 ++-
 src/assets/i18n/en.json                       | 10 ++-
 src/assets/i18n/fr.json                       | 10 ++-
 15 files changed, 167 insertions(+), 17 deletions(-)

diff --git a/src/app/shared/components/dialogs/shared-detail-dialog/shared-detail.dialog.scss b/src/app/shared/components/dialogs/shared-detail-dialog/shared-detail.dialog.scss
index cc3060671..8d11e5cd7 100644
--- a/src/app/shared/components/dialogs/shared-detail-dialog/shared-detail.dialog.scss
+++ b/src/app/shared/components/dialogs/shared-detail-dialog/shared-detail.dialog.scss
@@ -17,6 +17,19 @@ h2 {
 }
 
 .visualization {
+
+  max-height: 100%;
+  height: 100%;
+  overflow-y: hidden;
+
+  .visualization-mol {
+
+    &.has-error-message {
+      height: 0 ! important;
+      display: none;
+    }
+  }
+
   .warning-area {
     display: flex;
     flex-direction: column;
diff --git a/src/app/shared/components/dialogs/shared-file-detail/shared-file-detail.dialog.html b/src/app/shared/components/dialogs/shared-file-detail/shared-file-detail.dialog.html
index 56a3b6119..82f0d5386 100644
--- a/src/app/shared/components/dialogs/shared-file-detail/shared-file-detail.dialog.html
+++ b/src/app/shared/components/dialogs/shared-file-detail/shared-file-detail.dialog.html
@@ -94,6 +94,8 @@
        *ngIf="showPreviewTab.isActive"
   >
     <dlcm-file-visualizer
+        class="visualization-mol"
+        [class.has-error-message]="visualizationErrorMessage"
         [fileToVisualize]="getFileInput()"
         (errorMessageChange)="visualizationErrorMessage = $event"
     ></dlcm-file-visualizer>
diff --git a/src/app/shared/components/dialogs/shared-preview/shared-preview.dialog.scss b/src/app/shared/components/dialogs/shared-preview/shared-preview.dialog.scss
index b5be60976..d8951dd5e 100644
--- a/src/app/shared/components/dialogs/shared-preview/shared-preview.dialog.scss
+++ b/src/app/shared/components/dialogs/shared-preview/shared-preview.dialog.scss
@@ -1,3 +1,7 @@
 :host {
-  height: 500px;
+
+  min-width: 300px;
+  width: 100%;
+  height: 100%;
+
 }
diff --git a/src/app/shared/components/dialogs/shared-preview/shared-preview.dialog.ts b/src/app/shared/components/dialogs/shared-preview/shared-preview.dialog.ts
index 68a5dba77..3da99160a 100644
--- a/src/app/shared/components/dialogs/shared-preview/shared-preview.dialog.ts
+++ b/src/app/shared/components/dialogs/shared-preview/shared-preview.dialog.ts
@@ -9,7 +9,6 @@ import {
 } from "@angular/material/dialog";
 import {SharedAbstractContainer} from "@shared/components/containers/shared-abstract/shared-abstract.container";
 import {SharedPreviewDialogData} from "@shared/components/dialogs/shared-preview/shared-preview-dialog-data.model";
-import {FileVisualizerPresentational} from "@shared/filevisualizer/components/file-visualizer.component";
 
 @Component({
   selector: "dlcm-preview-dialog",
diff --git a/src/app/shared/filevisualizer/components/file-visualizer.component.html b/src/app/shared/filevisualizer/components/file-visualizer.component.html
index a7eabd7a2..e487bc02e 100644
--- a/src/app/shared/filevisualizer/components/file-visualizer.component.html
+++ b/src/app/shared/filevisualizer/components/file-visualizer.component.html
@@ -1,3 +1,9 @@
-<div #visualizationContainer>
+<div #visualizationContainer
+     class="visualization-container"
+>
   <ng-template dlcmExternalPlugin></ng-template>
+  <div *ngIf="isLoadingVisualizationObs | async"
+       class="spinner-background"
+       [dlcmSpinner]="isLoadingVisualizationObs | async"
+  ></div>
 </div>
diff --git a/src/app/shared/filevisualizer/components/file-visualizer.component.scss b/src/app/shared/filevisualizer/components/file-visualizer.component.scss
index c12ab9d3f..e8868635a 100644
--- a/src/app/shared/filevisualizer/components/file-visualizer.component.scss
+++ b/src/app/shared/filevisualizer/components/file-visualizer.component.scss
@@ -1,8 +1,31 @@
-.zoom-in {
-  cursor: zoom-in;
-}
+:host {
+  min-height: 300px;
+  width: 100%;
+  min-width: 300px;
+  height: 100%;
+
+  .visualization-container {
+    height: 100%;
+  }
+
+  .spinner-background {
+    background-color: rgba(255, 255, 255, 0.66);
+    height: 100%;
+    animation: from-transparent-background-color 0.5s ease-in-out;
+  }
+
+  .zoom-in {
+    cursor: zoom-in;
+    overflow-y: auto;
+    max-height: 100%;
+  }
+
+  .zoom-out {
+    cursor: zoom-out;
+    overflow-y: auto;
+    max-height: 100%;
+  }
 
-.zoom-out {
-  cursor: zoom-out;
 }
 
+
diff --git a/src/app/shared/filevisualizer/components/file-visualizer.component.ts b/src/app/shared/filevisualizer/components/file-visualizer.component.ts
index 62ee7a83a..1d70ce758 100644
--- a/src/app/shared/filevisualizer/components/file-visualizer.component.ts
+++ b/src/app/shared/filevisualizer/components/file-visualizer.component.ts
@@ -44,6 +44,8 @@ import {
 })
 export class FileVisualizerPresentational extends CoreAbstractAngularElement implements AfterContentInit, OnDestroy {
 
+  isLoadingVisualizationObs: Observable<boolean> = MemoizedUtil.isLoading(this._store, VisualizationState);
+
   previewFile: Observable<BlobElementModel> = MemoizedUtil.select(this._store, VisualizationState, state =>
     ({
       blob: state.blob,
@@ -66,10 +68,10 @@ export class FileVisualizerPresentational extends CoreAbstractAngularElement imp
 
   @ViewChild(AddExternalComponentDirective, {static: true}) addExternalPlugin: AddExternalComponentDirective;
 
-  private readonly _closeBS: BehaviorSubject<boolean | undefined> = new BehaviorSubject<boolean | undefined>(undefined);
+  private readonly _closeBS: BehaviorSubject<boolean> = new BehaviorSubject<boolean>(false);
 
   @Output("closeVisualizerChange")
-  readonly closeVisualizerObs: Observable<boolean | undefined> = ObservableUtil.asObservable(this._closeBS);
+  readonly closeVisualizerObs: Observable<boolean> = ObservableUtil.asObservable(this._closeBS);
 
   private readonly _errorMessageBS: BehaviorSubject<string> = new BehaviorSubject<string>("");
 
@@ -116,11 +118,11 @@ export class FileVisualizerPresentational extends CoreAbstractAngularElement imp
       this.closeVisualization();
       if (!this.canHandleBySize(fileInfo)) {
         const sizeNotSupported = "notification.file.visualizer.file.toobig";
-        this._notificationService.showInformation(TRANSLATE(sizeNotSupported), true);
+        this._notificationService.showInformation(TRANSLATE("notification.file.visualizer.file.toobig"), true);
         this._errorMessageBS.next(sizeNotSupported);
       } else {
         const notSupportedMessage = "notification.file.visualizer.file.notsupported";
-        this._notificationService.showInformation(TRANSLATE(notSupportedMessage), true);
+        this._notificationService.showInformation(TRANSLATE("notification.file.visualizer.file.notsupported"), true);
         this._errorMessageBS.next(notSupportedMessage);
       }
       return;
diff --git a/src/app/shared/filevisualizer/models/file-visualizer.constant.ts b/src/app/shared/filevisualizer/models/file-visualizer.constant.ts
index 730d4c97b..dc288524c 100644
--- a/src/app/shared/filevisualizer/models/file-visualizer.constant.ts
+++ b/src/app/shared/filevisualizer/models/file-visualizer.constant.ts
@@ -19,7 +19,7 @@ export class FileVisualizerConstant {
   static readonly classicalMoviePronomId: string[] = ["fmt/199", "fmt/203", "fmt/944", "fmt/945", "fmt/946", "fmt/947", "fmt/948"];
 
   // pdf constant
-  static readonly pdfExtensions: string[] = ["Office Open XML Document"];
+  static readonly pdfExtensions: string[] = ["PDF/A", ".pdf"];
   static readonly pdfContentType: string[] = [];
   static readonly pdfMimeType: string[] = ["application/pdf"];
   static readonly pdfPronomId: string[] = ["fmt/95", "fmt/3", "fmt/476", "fmt/477", "fmt/479", "fmt/480", "fmt/18", "fmt/19", "fmt/20", "fmt/276"];
diff --git a/src/app/shared/filevisualizer/services/sound-file-visualizer.service.ts b/src/app/shared/filevisualizer/services/sound-file-visualizer.service.ts
index 5a47925aa..b8b81b4fe 100644
--- a/src/app/shared/filevisualizer/services/sound-file-visualizer.service.ts
+++ b/src/app/shared/filevisualizer/services/sound-file-visualizer.service.ts
@@ -34,6 +34,7 @@ export class SoundFileVisualizerService implements AbstractFileVisualizer {
   }
 
   isVisualizationOnGoing(fileInfo: FileInput, domElement: Element): boolean {
+    const test = isTruthyObject(this.wave);
     return isTruthyObject(this.wave);
   }
 
@@ -54,22 +55,87 @@ export class SoundFileVisualizerService implements AbstractFileVisualizer {
   }
 
   closeVisualizer(fileInfo: FileInput, domElement: Element): void {
+    this.wave.unAll();
+    this.wave.stop();
     this.wave.destroy();
   }
 
   openVisualizer(fileInfo: FileInput, domElement: Element): void {
     if (this.canHandle(fileInfo)) {
+      const divContainer = document.createElement("div");
+      divContainer.style.display = "grid";
+      divContainer.style.gridTemplateRows = "auto auto auto";
+      const waveContainer = document.createElement("div");
+      waveContainer.id = "wave";
+      waveContainer.style.overflow = "hidden";
+      divContainer.appendChild(waveContainer);
+      const timelineContainer = document.createElement("div");
+      timelineContainer.style.display = "flex";
+      timelineContainer.style.justifyContent = "center";
+      const timeElement = document.createElement("span");
+      timeElement.id = "time";
+      timelineContainer.appendChild(timeElement);
+      const buttonContainer = document.createElement("div");
+      buttonContainer.style.display = "flex";
+      buttonContainer.style.alignItems = "center";
+      buttonContainer.style.justifyContent = "center";
+      const matButton = document.createElement("button");
+      matButton.onclick = (ev => this.wave.playPause());
+      matButton.textContent = "Play | Pause";
+      matButton.classList.add("mat-button");
+      matButton.classList.add("mat-focus-indicator");
+      matButton.classList.add("mat-button-base");
+      matButton.classList.add("mat-primary");
+      buttonContainer.appendChild(matButton);
+      divContainer.appendChild(buttonContainer);
+      divContainer.appendChild(timelineContainer);
+      document.createElement("div");
+      domElement.appendChild(divContainer);
       this.wave = WaveSurfer.create({
-        container: domElement,
+        container: document.querySelector("#wave"),
+        barWidth: 2,
+        barHeight: 1,
+        overflow: "hidden ! important",
         waveColor: "violet",
         progressColor: "purple",
       });
+
       const url = URL.createObjectURL(fileInfo.blob);
       this.wave.load(url);
       this.wave.on("ready", () => {
         this.wave.play();
       });
+
+      this.wave.on("audioprocess", () => {
+        const totalTime = this.wave.getDuration();
+        const currentTime = this.wave.getCurrentTime();
+        document.getElementById("time").innerText = this.getMiliSecondeDisplay(Math.round(currentTime * 1000)) + "/" + this.getMiliSecondeDisplay(Math.round(totalTime * 1000));
+      });
+
+      this.wave.on("pause", () => {
+        const totalTime = this.wave.getDuration();
+        const currentTime = this.wave.getCurrentTime();
+        document.getElementById("time").innerText = this.getMiliSecondeDisplay(Math.round(currentTime * 1000)) + "/" + this.getMiliSecondeDisplay(Math.round(totalTime * 1000));
+      });
+    }
+  }
+
+  private getMiliSecondeDisplay(millisec: number): string {
+    let seconds: any = (millisec / 1000).toFixed(0);
+    let minutes: any = Math.floor(seconds / 60);
+    let hours: any = "";
+    if (minutes > 59) {
+      hours = Math.floor(minutes / 60);
+      hours = (hours >= 10) ? hours : "0" + hours;
+      minutes = minutes - (hours * 60);
+      minutes = (minutes >= 10) ? minutes : "0" + minutes;
+    }
+    seconds = Math.floor(seconds % 60);
+    seconds = (seconds >= 10) ? seconds : "0" + seconds;
+    if (hours !== "") {
+      return hours + ":" + minutes + ":" + seconds;
     }
+    return minutes + ":" + seconds;
   }
 
   doAction(fileInfo: FileInput, domElement: Element): void {
diff --git a/src/app/shared/filevisualizer/services/video-classical-file-visualizer.service.ts b/src/app/shared/filevisualizer/services/video-classical-file-visualizer.service.ts
index edfe853ae..088db2ff4 100644
--- a/src/app/shared/filevisualizer/services/video-classical-file-visualizer.service.ts
+++ b/src/app/shared/filevisualizer/services/video-classical-file-visualizer.service.ts
@@ -60,6 +60,7 @@ export class VideoClassicalFileVisualizerService implements AbstractFileVisualiz
   openVisualizer(fileInfo: FileInput, domElement: Element): void {
     const url = URL.createObjectURL(fileInfo.blob);
     this.video = document.createElement("video");
+    this.video.style.backgroundColor = "black";
     this.video.controls = true;
     this.video.autoplay = true;
 
diff --git a/src/app/shared/filevisualizer/services/video-file-visualizer.service.ts b/src/app/shared/filevisualizer/services/video-file-visualizer.service.ts
index f3bbaa10b..dcfb34b98 100644
--- a/src/app/shared/filevisualizer/services/video-file-visualizer.service.ts
+++ b/src/app/shared/filevisualizer/services/video-file-visualizer.service.ts
@@ -73,6 +73,7 @@ export class VideoFileVisualizerService implements AbstractFileVisualizer {
     this.video.src = url;
     this.video.height = 500;
     this.video.width = 500;
+    this.video.style.backgroundColor = "black";
     domElement.appendChild(this.video);
   }
 
diff --git a/src/app/shared/stores/visualization/visualization.state.ts b/src/app/shared/stores/visualization/visualization.state.ts
index 9190153c5..17b135f62 100644
--- a/src/app/shared/stores/visualization/visualization.state.ts
+++ b/src/app/shared/stores/visualization/visualization.state.ts
@@ -56,6 +56,9 @@ export class VisualizationState extends BasicState<SharedVisualizationStateModel
 
   @Action(SharedVisualizationAction.Download)
   download(ctx: StateContext<SharedVisualizationStateModel>, action: SharedVisualizationAction.Download): Observable<Blob> {
+    ctx.patchState({
+      isLoadingCounter: ctx.getState().isLoadingCounter + 1,
+    });
     const url = `${this._urlResource}/${action.data.dataFile.packageId}/${ApiResourceNameEnum.DATAFILE}/${action.data.dataFile.resId}/${ApiActionEnum.DL}`;
     return this.downloadService.downloadInMemory(url, action.data.dataFile.fileName, false)
       .pipe(
@@ -69,11 +72,17 @@ export class VisualizationState extends BasicState<SharedVisualizationStateModel
 
   @Action(SharedVisualizationAction.DownloadFailed)
   downloadFailed(ctx: StateContext<SharedVisualizationStateModel>, action: SharedVisualizationAction.DownloadFailed): void {
+    ctx.patchState({
+      isLoadingCounter: ctx.getState().isLoadingCounter - 1,
+    });
     this.notificationService.showError(TRANSLATE("deposit.notification.download.fail"));
   }
 
   @Action(SharedVisualizationAction.DownloadSucess)
   downloadSuccess(ctx: StateContext<SharedVisualizationStateModel>, action: SharedVisualizationAction.DownloadSucess): void {
+    ctx.patchState({
+      isLoadingCounter: ctx.getState().isLoadingCounter - 1,
+    });
     ctx.patchState({
       blob: action.blob,
       blobId: action.blobId,
diff --git a/src/assets/i18n/de.json b/src/assets/i18n/de.json
index 79bea18cc..9bbe0d6c4 100644
--- a/src/assets/i18n/de.json
+++ b/src/assets/i18n/de.json
@@ -1869,6 +1869,14 @@
         "joinOrgUnitRequest": "Join organizational unit request"
       }
     },
+    "file": {
+      "visualizer": {
+        "file": {
+          "notsupported": "The file you try to open is not supported",
+          "toobig": "The file you try to open as a size not supported"
+        }
+      }
+    },
     "metadataType": {
       "file": {
         "downloadInProgress": "File download started",
@@ -2625,4 +2633,4 @@
       }
     }
   }
-}
\ No newline at end of file
+}
diff --git a/src/assets/i18n/en.json b/src/assets/i18n/en.json
index 79bea18cc..9bbe0d6c4 100644
--- a/src/assets/i18n/en.json
+++ b/src/assets/i18n/en.json
@@ -1869,6 +1869,14 @@
         "joinOrgUnitRequest": "Join organizational unit request"
       }
     },
+    "file": {
+      "visualizer": {
+        "file": {
+          "notsupported": "The file you try to open is not supported",
+          "toobig": "The file you try to open as a size not supported"
+        }
+      }
+    },
     "metadataType": {
       "file": {
         "downloadInProgress": "File download started",
@@ -2625,4 +2633,4 @@
       }
     }
   }
-}
\ No newline at end of file
+}
diff --git a/src/assets/i18n/fr.json b/src/assets/i18n/fr.json
index 2b8efaa13..a8c070217 100644
--- a/src/assets/i18n/fr.json
+++ b/src/assets/i18n/fr.json
@@ -1869,6 +1869,14 @@
         "joinOrgUnitRequest": "Demande pour rejoindre une unité organisationnelle"
       }
     },
+    "file": {
+      "visualizer": {
+        "file": {
+          "notsupported": "Le fichier que vous essayer d'ouvrir n'est pas supporté pour le moment.",
+          "toobig": "Le fichier que vous essayez d'ouvrir a une taille trop importante."
+        }
+      }
+    },
     "metadataType": {
       "file": {
         "downloadInProgress": "Le téléchargement a démarré",
@@ -2625,4 +2633,4 @@
       }
     }
   }
-}
\ No newline at end of file
+}
-- 
GitLab