From ea55ef15d07eed3ee22842772205ba925d1b9a42 Mon Sep 17 00:00:00 2001
From: Patrizio Bekerle <patrizio@bekerle.com>
Date: Tue, 30 Jun 2020 08:16:54 +0200
Subject: [PATCH] Implement vpu-file-sink web component (VPU/Apps/Signature#28)

---
 packages/file-handling/README.md            |  49 +++++++-
 packages/file-handling/package.json         |   1 +
 packages/file-handling/src/file-sink.js     | 131 ++++++++++++++++++++
 packages/file-handling/src/vpu-file-sink.js |   4 +
 packages/file-handling/vendor/common        |   2 +-
 5 files changed, 185 insertions(+), 2 deletions(-)
 create mode 100644 packages/file-handling/src/file-sink.js
 create mode 100644 packages/file-handling/src/vpu-file-sink.js

diff --git a/packages/file-handling/README.md b/packages/file-handling/README.md
index c17e4cab..7c2d6658 100644
--- a/packages/file-handling/README.md
+++ b/packages/file-handling/README.md
@@ -4,6 +4,9 @@
 
 ## FileSource
 
+This web component allows the selection of local files via file dialog or drag and drop and to select and download
+files from a [Nextcloud](https://nextcloud.com/) instance.
+
 ### Usage
 
 ```html
@@ -31,7 +34,7 @@
     - example `<vpu-file-source nextcloud-web-dav-url="http://localhost:8081/remote.php/dav/files"></vpu-file-source>`
     - `nextcloud-auth-url` also needs to be set for the Nextcloud file picker to be active
 
-### Events
+### Outgoing Events
 
 #### `vpu-file-source-file-selected`
 
@@ -39,6 +42,46 @@ This event is sent if a file was selected.
 
 **Payload**: `{'file': File}` where `File` is the binary file that was selected
 
+## FileSink
+
+This web component is able to receive files and present as them as ZIP file download. 
+
+### Usage
+
+```html
+<vpu-file-sink></vpu-file-sink>
+```
+
+### Attributes
+
+- `lang` (optional, default: `de`): set to `de` or `en` for German or English
+    - example `<vpu-file-source lang="de"></vpu-file-source>`
+- `nextcloud-auth-url` (optional): Nextcloud Auth Url to use with the Nextcloud file picker
+    - example `<vpu-file-source nextcloud-auth-url="http://localhost:8081/index.php/apps/webapppassword"></vpu-file-source>`
+    - `nextcloud-web-dav-url` also needs to be set for the Nextcloud file picker to be active
+- `nextcloud-web-dav-url` (optional): Nextcloud WebDav Url to use with the Nextcloud file picker
+    - example `<vpu-file-source nextcloud-web-dav-url="http://localhost:8081/remote.php/dav/files"></vpu-file-source>`
+    - `nextcloud-auth-url` also needs to be set for the Nextcloud file picker to be active
+
+### Incoming Events
+
+#### `vpu-file-sink-download-compressed-files`
+
+If this event is received a *save-as* dialog will pop up to store a zip file of the received files.
+
+##### Payload
+
+`{'files': [File], 'filename': 'my-file.zip'}` where `[File]` is an array of binary files which should be
+compressed and `filename` is the name of the zip file that should be suggested in the *save-as* dialog
+
+##### Example
+
+```javascript
+const detail = { "files": files, "filename": "signed-documents.zip" };
+const event = new CustomEvent("vpu-file-sink-download-compressed-files", { "detail": detail });
+this._("#file-sink").dispatchEvent(event);
+```
+
 ## Local development
 
 ```bash
@@ -55,3 +98,7 @@ npm run watch-local
 ```
 
 Jump to <http://localhost:8002> and you should get a demo page.
+
+To use the Nextcloud functionality you need a running Nextcloud server with the
+[webapppassword](https://gitlab.tugraz.at/VPU/Middleware/Nextcloud/webapppassword) Nextcloud app like this
+[Nextcloud Development Environment](https://gitlab.tugraz.at/VPU/Middleware/Nextcloud/webapppassword/-/tree/master/docker). 
diff --git a/packages/file-handling/package.json b/packages/file-handling/package.json
index 97783b5a..4e2a79e9 100644
--- a/packages/file-handling/package.json
+++ b/packages/file-handling/package.json
@@ -26,6 +26,7 @@
   },
   "dependencies": {
     "@open-wc/scoped-elements": "^1.0.8",
+    "file-saver": "^2.0.2",
     "i18next": "^19.4.2",
     "jszip": "^3.5.0",
     "lit-element": "^2.1.0",
diff --git a/packages/file-handling/src/file-sink.js b/packages/file-handling/src/file-sink.js
new file mode 100644
index 00000000..ee76039c
--- /dev/null
+++ b/packages/file-handling/src/file-sink.js
@@ -0,0 +1,131 @@
+import {i18n} from './i18n';
+import {css, html} from 'lit-element';
+import {ScopedElementsMixin} from '@open-wc/scoped-elements';
+import VPULitElement from 'vpu-common/vpu-lit-element';
+import * as commonUtils from "vpu-common/utils";
+import {Icon, MiniSpinner} from 'vpu-common';
+import * as commonStyles from 'vpu-common/styles';
+import {NextcloudFilePicker} from "./vpu-nextcloud-file-picker";
+import {classMap} from 'lit-html/directives/class-map.js';
+import FileSaver from 'file-saver';
+
+
+/**
+ * FileSink web component
+ */
+export class FileSink extends ScopedElementsMixin(VPULitElement) {
+    constructor() {
+        super();
+        this.lang = 'de';
+        this.nextcloudAuthUrl = '';
+        this.nextcloudWebDavUrl = '';
+
+        this._onDownloadCompressedFiles = this.onDownloadCompressedFiles.bind(this);
+    }
+
+    static get scopedElements() {
+        return {
+            'vpu-icon': Icon,
+            'vpu-mini-spinner': MiniSpinner,
+            'vpu-nextcloud-file-picker': NextcloudFilePicker,
+        };
+    }
+
+    /**
+     * See: https://lit-element.polymer-project.org/guide/properties#initialize
+     */
+    static get properties() {
+        return {
+            lang: { type: String },
+            nextcloudAuthUrl: { type: String, attribute: 'nextcloud-auth-url' },
+            nextcloudWebDavUrl: { type: String, attribute: 'nextcloud-web-dav-url' },
+        };
+    }
+
+    async onDownloadCompressedFiles(event) {
+        const detail = event.detail;
+
+        console.log("event", event);
+        console.log("detail", detail);
+
+        // see: https://stuk.github.io/jszip/
+        let JSZip = (await import('jszip/dist/jszip.js')).default;
+        let zip = new JSZip();
+        let fileNames = [];
+
+        // add all signed pdf-files
+        detail.files.forEach((file) => {
+            let fileName = file.name;
+
+            // add pseudo-random string on duplicate file name
+            if (fileNames.indexOf(fileName) !== -1) {
+                fileName = commonUtils.getBaseName(fileName) + "-" + Math.random().toString(36).substring(7) + "." +
+                    commonUtils.getFileExtension(fileName);
+            }
+
+            fileNames.push(fileName);
+            zip.file(fileName, file);
+        });
+
+        let content = await zip.generateAsync({type:"blob"});
+
+        // see: https://github.com/eligrey/FileSaver.js#readme
+        FileSaver.saveAs(content, detail.filename || "files.zip");
+    }
+
+    update(changedProperties) {
+        changedProperties.forEach((oldValue, propName) => {
+            switch (propName) {
+                case "lang":
+                    i18n.changeLanguage(this.lang);
+                    break;
+            }
+        });
+
+        super.update(changedProperties);
+    }
+
+    connectedCallback() {
+        super.connectedCallback();
+
+        this.updateComplete.then(() => {
+            this.addEventListener('vpu-file-sink-download-compressed-files', this._onDownloadCompressedFiles);
+        });
+    }
+
+    disconnectedCallback() {
+        // remove event listeners
+        window.removeEventListener('vpu-file-sink-download-compressed-files', this._onDownloadCompressedFiles);
+
+        super.disconnectedCallback();
+    }
+
+    preventDefaults (e) {
+        e.preventDefault();
+        e.stopPropagation();
+    }
+
+    static get styles() {
+        // language=css
+        return css`
+            ${commonStyles.getGeneralCSS()}
+            ${commonStyles.getButtonCSS()}
+        `;
+    }
+
+    render() {
+        return html`
+<!--
+            <vpu-nextcloud-file-picker id="nextcloud-file-picker"
+                                       class="${classMap({hidden: this.nextcloudWebDavUrl === "" || this.nextcloudAuthUrl === ""})}"
+                                       ?disabled="${this.disabled}"
+                                       lang="${this.lang}"
+                                       auth-url="${this.nextcloudAuthUrl}"
+                                       web-dav-url="${this.nextcloudWebDavUrl}"
+                                       @vpu-nextcloud-file-picker-file-downloaded="${(event) => {
+                                           this.sendFileEvent(event.detail.file);
+                                       }}"></vpu-nextcloud-file-picker>
+-->
+        `;
+    }
+}
\ No newline at end of file
diff --git a/packages/file-handling/src/vpu-file-sink.js b/packages/file-handling/src/vpu-file-sink.js
new file mode 100644
index 00000000..bcc2431e
--- /dev/null
+++ b/packages/file-handling/src/vpu-file-sink.js
@@ -0,0 +1,4 @@
+import * as commonUtils from "vpu-common/utils";
+import {FileSink} from './file-sink';
+
+commonUtils.defineCustomElement('vpu-file-sink', FileSink);
diff --git a/packages/file-handling/vendor/common b/packages/file-handling/vendor/common
index 338c8012..63b49c3a 160000
--- a/packages/file-handling/vendor/common
+++ b/packages/file-handling/vendor/common
@@ -1 +1 @@
-Subproject commit 338c8012f85e7b54a1e32f0f7af603e864573463
+Subproject commit 63b49c3a2eeae312b4a2aeec4cc2f7f0ee12b749
-- 
GitLab