Commit fcab0502 by Jonathan Thomas

Added file upload progress to the UI. Multiple upload progress bars are…

Added file upload progress to the UI. Multiple upload progress bars are supported. Also, clicking on a file now previews the selected file. Video and images are supported.
parent 73135ab9
...@@ -7,15 +7,24 @@ ...@@ -7,15 +7,24 @@
<label for="floatingInput">Search</label> <label for="floatingInput">Search</label>
</div> </div>
</div> </div>
<div v-for="file in files" :key="file.id" class="col-4" style="overflow: scroll;"> <div v-for="file in files" :key="file.id" class="col-4">
<img @click="toggleSelection(file)" :class="getSelectedClass(file)" class="file-thumbnail img-fluid img-thumbnail" :title="file.name" src="../assets/logo.png"/> <img @click="toggleSelection(file)" :class="getSelectedClass(file)" class="file-thumbnail img-fluid img-thumbnail" :title="file.name" src="../assets/logo.png"/>
</div> </div>
<div v-for="upload in uploads" :key="upload.id" class="col-4">
<div class="row h-100">
<div class="col-sm-12 my-auto p-3">
<div class="progress align-middle">
<div class="progress-bar" role="progressbar" v-bind:style="{ width: upload.progress + '%'}" :aria-valuenow="upload.progress" aria-valuemin="0" aria-valuemax="100"></div>
</div>
</div>
</div>
</div>
<input ref="fileUpload" type="file" @change="fileChanged" hidden> <input ref="fileUpload" type="file" @change="fileChanged" hidden>
</div> </div>
</template> </template>
<script> <script>
import { mapActions, mapState } from "vuex"; import { mapActions, mapState, mapMutations } from "vuex";
export default { export default {
name: "Files.vue", name: "Files.vue",
...@@ -27,7 +36,7 @@ export default { ...@@ -27,7 +36,7 @@ export default {
chooseFiles() { chooseFiles() {
this.$refs.fileUpload.click() this.$refs.fileUpload.click()
}, },
fileChanged(event) { async fileChanged(event) {
let data = new FormData(); let data = new FormData();
data.append('media', event.target.files[0]); data.append('media', event.target.files[0]);
data.append('project', this.project.url); data.append('project', this.project.url);
...@@ -35,23 +44,25 @@ export default { ...@@ -35,23 +44,25 @@ export default {
let payload = { project_id: this.project.id, let payload = { project_id: this.project.id,
project_url: this.project.url, project_url: this.project.url,
data} data}
this.createFile(payload) await this.createFile(payload)
this.$refs.fileUpload.value = null
}, },
toggleSelection(fileObject) { toggleSelection(fileObject) {
fileObject.selected = !fileObject.selected this.setPreviewFile(fileObject)
}, },
getSelectedClass(fileObject) { getSelectedClass(fileObject) {
if (fileObject.selected) { if (this.previewFile && fileObject.id == this.previewFile.id) {
return 'selected' return 'selected'
} else { } else {
return '' return ''
} }
}, },
...mapActions(['loadFiles', 'createFile']) ...mapActions(['loadFiles', 'createFile']),
...mapMutations((['setPreviewFile']))
}, },
computed: { computed: {
...mapState(['files']) ...mapState(['files', 'previewFile', 'uploads'])
}, },
mounted() { mounted() {
this.loadFiles(this.project.id) this.loadFiles(this.project.id)
......
<template> <template>
<h3 class="p-2">Preview <button type="button" class="btn btn-danger export-btn">Export</button></h3> <h3 class="p-2">Preview <button type="button" class="btn btn-danger export-btn">Export</button></h3>
<video controls="" preload="" poster="http://sandbox.thewikies.com/vfe-generator/images/big-buck-bunny_poster.jpg" class="video-responsive" id="video1" width="100%" height="100%"> <video v-if="!isImage && hasPreviewFile" controls="" preload="" poster="" class="video-responsive">
<source type="video/mp4" src="http://clips.vorwaerts-gmbh.de/big_buck_bunny.mp4"> <source :type="previewFormat" :src="previewFile.media">
<source type="video/webm" src="http://clips.vorwaerts-gmbh.de/big_buck_bunny.webm">
<source type="video/ogg" src="http://clips.vorwaerts-gmbh.de/big_buck_bunny.ogv">
<track src="Content/audio/captions.vtt" kind="subtitles" srclang="en" label="English">
<img alt="Big Buck Bunny" src="http://sandbox.thewikies.com/vfe-generator/images/big-buck-bunny_poster.jpg" width="640" height="360" title="No video playback capabilities, please download the video below">
</video> </video>
<img v-if="isImage && hasPreviewFile" :src="previewFile.media" class="img-fluid img-thumbnail" />
</template> </template>
<script> <script>
import {mapState} from "vuex";
export default { export default {
name: "Preview.vue", name: "Preview.vue",
props: ['project'] props: ['project'],
methods: {
},
computed: {
...mapState(['previewFile']),
hasPreviewFile() {
if (this.previewFile) {
return true
} else {
return false
}
},
isImage() {
return (this.previewFile && this.previewFile.json.vcodec == "QImage")
},
previewFormat() {
if (this.previewFile) {
return `video/${this.previewFile.media.split('.').pop()}`
}
return ''
}
}
} }
</script> </script>
......
...@@ -5,11 +5,12 @@ import { instance } from "./axios" ...@@ -5,11 +5,12 @@ import { instance } from "./axios"
export default createStore({ export default createStore({
state: { state: {
counter: 1,
projects: [], projects: [],
files: [], files: [],
user: null, user: null,
errors: [] errors: [],
previewFile: null,
uploads: []
}, },
mutations: { mutations: {
...@@ -31,7 +32,24 @@ export default createStore({ ...@@ -31,7 +32,24 @@ export default createStore({
}, },
setFiles(state, files) { setFiles(state, files) {
state.files = files state.files = files
} },
setPreviewFile(state, file) {
state.previewFile = file
},
addUpload(state, upload) {
state.uploads.push(upload)
},
updateUpload(state, upload) {
for (const up of state.uploads) {
if (up.id == upload.id) {
up.progress = upload.progress
break
}
}
},
removeUpload(state, uploadID) {
state.uploads = state.uploads.filter(up => up.id != uploadID )
},
}, },
getters: { getters: {
isAuthenticated: state => !!state.user isAuthenticated: state => !!state.user
...@@ -88,10 +106,24 @@ export default createStore({ ...@@ -88,10 +106,24 @@ export default createStore({
} }
}, },
async createFile({dispatch, commit}, payload) { async createFile({dispatch, commit}, payload) {
let uploadID = uuidv4()
let uploadObject = { 'id': uploadID, progress: 0 }
commit('addUpload', uploadObject)
const config = {
onUploadProgress: progressEvent => {
let progress = (progressEvent.loaded / progressEvent.total) * 100;
let uploadObject = { 'id': uploadID, progress }
commit('updateUpload', uploadObject)
}
}
try { try {
await instance.post(`${payload.project_url}files/`, payload.data) await instance.post(`${payload.project_url}files/`, payload.data, config)
commit('removeUpload', uploadID)
dispatch('loadFiles', payload.project_id) dispatch('loadFiles', payload.project_id)
} catch(err) { } catch(err) {
commit('removeUpload', uploadID)
commit('addError', JSON.stringify(err.response.data)) commit('addError', JSON.stringify(err.response.data))
} }
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment