diff options
Diffstat (limited to 'src/site/components/uploader/Uploader.vue')
| -rw-r--r-- | src/site/components/uploader/Uploader.vue | 258 |
1 files changed, 258 insertions, 0 deletions
diff --git a/src/site/components/uploader/Uploader.vue b/src/site/components/uploader/Uploader.vue new file mode 100644 index 0000000..f180546 --- /dev/null +++ b/src/site/components/uploader/Uploader.vue @@ -0,0 +1,258 @@ +<template> + <div + :class="{ 'has-files': alreadyAddedFiles }" + class="uploader-wrapper"> + <b-select + v-if="loggedIn" + v-model="selectedAlbum" + placeholder="Upload to album" + size="is-medium" + expanded> + <option + v-for="album in albums" + :key="album.id" + :value="album.id"> + {{ album.name }} + </option> + </b-select> + <dropzone + v-if="showDropzone" + id="dropzone" + ref="el" + :options="dropzoneOptions" + :include-styling="false" + @vdropzone-success="dropzoneSuccess" + @vdropzone-error="dropzoneError" + @vdropzone-files-added="dropzoneFilesAdded" /> + <label class="add-more"> + Add or drop more files + </label> + + <div + id="template" + ref="template"> + <div class="dz-preview dz-file-preview"> + <div class="dz-details"> + <div class="dz-filename"> + <span data-dz-name /> + </div> + <div class="dz-size"> + <span data-dz-size /> + </div> + </div> + <div class="result"> + <div class="openLink"> + <a + class="link" + target="_blank"> + Link + </a> + </div> + </div> + <div class="error"> + <div> + <span> + <span + class="error-message" + data-dz-errormessage /> + <i class="icon-web-warning" /> + </span> + </div> + </div> + <div class="dz-progress"> + <span + class="dz-upload" + data-dz-uploadprogress /> + </div> + <!-- + <div class="dz-error-message"><span data-dz-errormessage/></div> + <div class="dz-success-mark"><i class="fa fa-check"/></div> + <div class="dz-error-mark"><i class="fa fa-close"/></div> + --> + </div> + </div> + </div> +</template> + +<script> +import { mapState, mapGetters } from 'vuex'; + +import Dropzone from 'nuxt-dropzone'; +import '~/assets/styles/dropzone.scss'; + +export default { + components: { Dropzone }, + data() { + return { + alreadyAddedFiles: false, + files: [], + dropzoneOptions: {}, + showDropzone: false, + selectedAlbum: null + }; + }, + computed: { + ...mapState({ + config: state => state.config, + albums: state => state.albums.tinyDetails + }), + ...mapGetters({ loggedIn: 'auth/isLoggedIn', token: 'auth/getToken' }) + }, + watch: { + loggedIn() { + this.getAlbums(); + }, + selectedAlbum() { + this.updateDropzoneConfig(); + } + }, + mounted() { + this.dropzoneOptions = { + url: `${this.config.baseURL}/upload`, + timeout: 600000, // 10 minutes + autoProcessQueue: true, + addRemoveLinks: false, + parallelUploads: 5, + uploadMultiple: false, + maxFiles: 1000, + createImageThumbnails: false, + paramName: 'files[]', + forceChunking: false, + chunking: true, + retryChunks: true, + retryChunksLimit: 3, + parallelChunkUploads: true, + chunkSize: this.config.chunkSize * 1000000, + chunksUploaded: this.dropzoneChunksUploaded, + maxFilesize: this.config.maxFileSize, + previewTemplate: this.$refs.template.innerHTML, + dictDefaultMessage: 'Drag & Drop your files or click to browse', + headers: { Accept: 'application/vnd.chibisafe.json' } + }; + this.showDropzone = true; + if (this.loggedIn) this.getAlbums(); + }, + methods: { + /* + Get all available albums so the user can upload directly to one (or several soon™) of them. + */ + async getAlbums() { + try { + await this.$store.dispatch('albums/getTinyDetails'); + } catch (e) { + this.$store.dispatch('alert/set', { text: e.message, error: true }, { root: true }); + } + this.updateDropzoneConfig(); + }, + + /* + This method needs to be called after the token or selectedAlbum changes + since dropzone doesn't seem to update the config values unless you force it. + Tch. + */ + updateDropzoneConfig() { + this.$refs.el.setOption('headers', { + Accept: 'application/vnd.chibisafe.json', + Authorization: this.token ? `Bearer ${this.token}` : '', + albumId: this.selectedAlbum ? this.selectedAlbum : null + }); + }, + + /* + Dropzone stuff + */ + dropzoneFilesAdded() { + this.alreadyAddedFiles = true; + }, + dropzoneSuccess(file, response) { + this.processResult(file, response); + }, + dropzoneError(file, message, xhr) { + this.$store.dispatch('alert', { + text: 'There was an error uploading this file. Check the console.', + error: true + }); + // eslint-disable-next-line no-console + console.error(file, message, xhr); + }, + async dropzoneChunksUploaded(file, done) { + const { data } = await this.$axios.post(`${this.config.baseURL}/upload/chunks`, { + files: [{ + uuid: file.upload.uuid, + original: file.name, + size: file.size, + type: file.type, + count: file.upload.totalChunkCount + }] + }, { + headers: { + albumId: this.selectedAlbum ? this.selectedAlbum : null + } + }); + + this.processResult(file, data); + return done(); + }, + + /* + If upload/s was/were successfull we modify the template so that the buttons for + copying the returned url or opening it in a new window appear. + */ + processResult(file, response) { + if (!response.url) return; + file.previewTemplate.querySelector('.link').setAttribute('href', response.url); + /* + file.previewTemplate.querySelector('.copyLink').addEventListener('click', () => { + this.$store.dispatch('alert', { + text: 'Link copied!' + }); + this.$clipboard(response.url); + }); + */ + } + } +}; +</script> +<style lang="scss" scoped> + #template { display: none; } + .uploader-wrapper { + display: block; + width: 400px; + margin: 0 auto; + max-width: 100%; + position: relative; + } +</style> +<style lang="scss"> + @import '~/assets/styles/_colors.scss'; + + div.uploader-wrapper { + &.has-files { + #dropzone { + padding-bottom: 50px; + } + label.add-more { + position: absolute; + bottom: 23px; + width: 100%; + text-align: center; + color: #797979; + display: block; + pointer-events: none; + } + } + div.control { + margin-bottom: 5px; + span.select { + select { + border: 1px solid #00000061; + background: rgba(0, 0, 0, 0.15); + border-radius: .3em; + color: $uploaderDropdownColor; + padding: 0 0 0 1rem; + } + } + } + label.add-more { display: none; } + } +</style> |