Commit 3d1229fb by Jonathan Thomas

Updated clips to have context menu, larger. Thumbnails only appear once loaded.…

Updated clips to have context menu, larger. Thumbnails only appear once loaded. Fixed some buggy code when switching between projects (showing the wrong clips). Added time calculation to timeline and clips.
parent bf3292b6
...@@ -3,24 +3,26 @@ ...@@ -3,24 +3,26 @@
<h3>Clips</h3> <h3>Clips</h3>
<div class="col-12"> <div class="col-12">
<div class="row gy-2 gx-2 mb-2" v-for="clip in clips" :key="clip.id"> <div class="row gy-2 gx-2 mb-2" v-for="clip in thumbnailedClips" :key="clip.id">
<div class="col-7 text-center img-parent"> <div class="col-12 text-center img-parent">
<img @click="toggleSelection(clip)" :class="getSelectedClass(clip)" class="img-fluid img-thumbnail clip-thumbnail" :title="clip.name" :src="clip.thumbnail"/>
</div>
<div class="col-5">
<div class="row">
<div class="col-12"> <div class="btn-group dropstart context-menu">
<div class="d-grid gap-1 p-2"> <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="dropdown-toggle bi bi-three-dots-vertical" data-bs-toggle="dropdown" aria-expanded="false" viewBox="0 0 16 16">
<button type="button" class="btn btn-outline-primary">Move Up</button> <path d="M9.5 13a1.5 1.5 0 1 1-3 0 1.5 1.5 0 0 1 3 0zm0-5a1.5 1.5 0 1 1-3 0 1.5 1.5 0 0 1 3 0zm0-5a1.5 1.5 0 1 1-3 0 1.5 1.5 0 0 1 3 0z"/>
<button type="button" class="btn btn-outline-primary">Move Down</button> </svg>
<button type="button" class="btn btn-outline-primary" @click="deleteClipBtn(clip.id)">Delete</button> <ul class="dropdown-menu small-dropdown">
</div> <li><a class="dropdown-item" href="#">Move Up</a></li>
</div> <li><a class="dropdown-item" href="#">Move Down</a></li>
<li><hr class="dropdown-divider"></li>
<li><a class="dropdown-item" href="#" @click="deleteClipBtn(clip.id)">Delete</a></li>
</ul>
</div>
<div class="clip-label">
{{ (clip.end - clip.start).toFixed(1) }} Seconds
</div> </div>
<img @click="toggleSelection(clip)" :class="getSelectedClass(clip)" class="img-fluid img-thumbnail clip-thumbnail" :title="clip.name" :src="clip.thumbnail"/>
</div> </div>
</div> </div>
...@@ -57,20 +59,27 @@ export default { ...@@ -57,20 +59,27 @@ export default {
} }
}, },
...mapActions(['loadClips', 'createClip', 'deleteClip']), ...mapActions(['loadClips', 'createClip', 'deleteClip']),
...mapMutations((['setPreviewFile'])) ...mapMutations((['setPreviewFile', 'setClips']))
}, },
computed: { computed: {
thumbnailedClips() {
return this.clips.filter(clip => clip.thumbnail)
},
...mapState(['clips', 'previewFile', 'latestClip']) ...mapState(['clips', 'previewFile', 'latestClip'])
}, },
watch: { watch: {
latestClip() { latestClip() {
this.$nextTick(() => { this.$nextTick(() => {
this.$refs.bottom.scrollIntoView({behavior: 'smooth', block: 'center'}) this.$refs.bottom.scrollIntoView({behavior: 'smooth', block: 'end'})
}) })
} }
}, },
async mounted() { async mounted() {
this.setClips([])
await this.loadClips(this.project.id) await this.loadClips(this.project.id)
},
unmounted() {
this.setClips([])
} }
} }
</script> </script>
...@@ -92,4 +101,24 @@ export default { ...@@ -92,4 +101,24 @@ export default {
.selected { .selected {
border: #0d6efd 4px solid; border: #0d6efd 4px solid;
} }
.context-menu {
color: #ffffff;
position: absolute;
top: 0%;
right: 0%;
padding: 10px;
cursor: pointer;
}
.small-dropdown {
min-width: 5em;
opacity: 80%;
}
.clip-label {
color: #ffffff;
position: absolute;
top: 88%;
z-index: 999;
right: 5%;
font-size: .8em;
}
</style> </style>
\ No newline at end of file
...@@ -97,18 +97,20 @@ export default { ...@@ -97,18 +97,20 @@ export default {
} }
}, },
...mapActions(['loadFiles', 'createFile', 'deleteFile']), ...mapActions(['loadFiles', 'createFile', 'deleteFile']),
...mapMutations((['setPreviewFile'])) ...mapMutations((['setPreviewFile', 'setFiles']))
}, },
computed: { computed: {
searchedFiles() { searchedFiles() {
return this.files.filter(file => path.basename(file.media).toLowerCase().includes(this.search.toLowerCase())) return this.files.filter(file => path.basename(file.media).toLowerCase().includes(this.search.toLowerCase()) && file.thumbnail)
}, },
...mapState(['files', 'previewFile', 'uploads']) ...mapState(['files', 'previewFile', 'uploads'])
}, },
mounted() { mounted() {
this.setFiles([])
this.loadFiles(this.project.id) this.loadFiles(this.project.id)
}, },
unmounted() { unmounted() {
this.setFiles([])
this.setPreviewFile(null) this.setPreviewFile(null)
} }
} }
......
...@@ -138,10 +138,12 @@ export default { ...@@ -138,10 +138,12 @@ export default {
} }
}, },
seekToMarker(marker) { seekToMarker(marker) {
if (marker == 'start') { if (this.$refs.video) {
this.$refs.video.currentTime = this.markers.start * this.$refs.video.duration if (marker == 'start') {
} else if (marker == 'end') { this.$refs.video.currentTime = this.markers.start * this.$refs.video.duration
this.$refs.video.currentTime = this.markers.end * this.$refs.video.duration } else if (marker == 'end') {
this.$refs.video.currentTime = this.markers.end * this.$refs.video.duration
}
} }
}, },
drag(e) { drag(e) {
......
...@@ -82,7 +82,7 @@ ...@@ -82,7 +82,7 @@
</template> </template>
<script> <script>
import { mapState, mapActions } from 'vuex' import { mapState, mapActions, mapMutations } from 'vuex'
import { Modal } from "bootstrap" import { Modal } from "bootstrap"
export default { export default {
...@@ -133,7 +133,8 @@ export default { ...@@ -133,7 +133,8 @@ export default {
this.$refs.Close.click(); this.$refs.Close.click();
this.deletedProject = null this.deletedProject = null
}, },
...mapActions(['loadProjects', 'createProject', 'deleteProject']) ...mapActions(['loadProjects', 'createProject', 'deleteProject']),
...mapMutations(['setProject'])
}, },
computed: { computed: {
...mapState(['projects']) ...mapState(['projects'])
......
...@@ -8,11 +8,12 @@ export default createStore({ ...@@ -8,11 +8,12 @@ export default createStore({
projects: [], projects: [],
files: [], files: [],
clips: [], clips: [],
user: null,
errors: [], errors: [],
uploads: [],
project: null,
user: null,
previewFile: null, previewFile: null,
latestClip: null, latestClip: null,
uploads: []
}, },
mutations: { mutations: {
...@@ -41,6 +42,9 @@ export default createStore({ ...@@ -41,6 +42,9 @@ export default createStore({
setProjects(state, projects) { setProjects(state, projects) {
state.projects = projects state.projects = projects
}, },
setProject(state, project) {
state.project = project
},
setFiles(state, files) { setFiles(state, files) {
state.files = files state.files = files
}, },
...@@ -48,10 +52,12 @@ export default createStore({ ...@@ -48,10 +52,12 @@ export default createStore({
state.files.push(fileObj) state.files.push(fileObj)
}, },
updateFileThumbnail(state, payload) { updateFileThumbnail(state, payload) {
state.files = [ if (payload.obj.projects.includes(state.project.url)) {
state.files = [
...state.files = state.files.filter(file => file.id != payload.obj.id), ...state.files = state.files.filter(file => file.id != payload.obj.id),
payload.obj payload.obj
] ]
}
}, },
deleteFile(state, file_id) { deleteFile(state, file_id) {
state.files = state.files.filter(file => file.id != file_id) state.files = state.files.filter(file => file.id != file_id)
...@@ -61,18 +67,23 @@ export default createStore({ ...@@ -61,18 +67,23 @@ export default createStore({
}, },
setClips(state, clips) { setClips(state, clips) {
state.clips = clips state.clips = clips
if (!clips) {
// clear latest clip if no clips
state.latestClip = null
}
}, },
deleteClip(state, clip_id) { deleteClip(state, clip_id) {
state.clips = state.clips.filter(clip => clip.id != clip_id) state.clips = state.clips.filter(clip => clip.id != clip_id)
}, },
updateClipThumbnail(state, payload) { updateClipThumbnail(state, payload) {
state.clips = [ if (state.project.url == payload.obj.project) {
...state.clips = state.clips.filter(clip => clip.id != payload.obj.id), state.clips = [
payload.obj ...state.clips = state.clips.filter(clip => clip.id != payload.obj.id),
] payload.obj
if (payload.latest) ]
{ if (payload.latest) {
state.latestClip = payload.obj state.latestClip = payload.obj
}
} }
}, },
setPreviewFile(state, file) { setPreviewFile(state, file) {
...@@ -148,7 +159,7 @@ export default createStore({ ...@@ -148,7 +159,7 @@ export default createStore({
for (let fileObj of files) { for (let fileObj of files) {
// fix long duration on images // fix long duration on images
if (fileObj.json.duration == 3600) { if (fileObj.json.duration == 3600) {
fileObj.json.duration = 60 * 3 fileObj.json.duration = 60
} }
let payload = { obj: fileObj, frame: 1 } let payload = { obj: fileObj, frame: 1 }
dispatch('attachThumbnail', payload) dispatch('attachThumbnail', payload)
...@@ -226,7 +237,7 @@ export default createStore({ ...@@ -226,7 +237,7 @@ export default createStore({
let fileObj = response.data let fileObj = response.data
// fix long duration on images // fix long duration on images
if (fileObj.json.duration == 3600) { if (fileObj.json.duration == 3600) {
fileObj.json.duration = 60 * 3 fileObj.json.duration = 60
} }
commit('removeUpload', uploadID) commit('removeUpload', uploadID)
......
...@@ -11,7 +11,7 @@ ...@@ -11,7 +11,7 @@
</template> </template>
<script> <script>
import { mapActions, mapMutations } from "vuex"; import { mapActions, mapMutations, mapState } from "vuex";
import Files from "../components/Files"; import Files from "../components/Files";
import Clips from "../components/Clips"; import Clips from "../components/Clips";
import Preview from "../components/Preview"; import Preview from "../components/Preview";
...@@ -21,17 +21,17 @@ export default { ...@@ -21,17 +21,17 @@ export default {
data() { data() {
return { return {
hasData: false, hasData: false,
project: {}
} }
}, },
methods: { methods: {
...mapActions(['getProject']), ...mapActions(['getProject']),
...mapMutations(['addError']) ...mapMutations(['addError', 'setProject'])
}, },
computed: { computed: {
id() { id() {
return this.$route.params.id return this.$route.params.id
} },
...mapState(['project'])
}, },
components: { components: {
Files, Clips, Preview Files, Clips, Preview
...@@ -39,14 +39,16 @@ export default { ...@@ -39,14 +39,16 @@ export default {
async mounted() { async mounted() {
try { try {
let results = await this.getProject(this.$route.params.id) let results = await this.getProject(this.$route.params.id)
this.project = results.data this.setProject(results.data)
this.hasData = true this.hasData = true
} catch(err) { } catch(err) {
// Handle 404 // Handle 404
this.addError(err.response.data) this.addError(err.response.data)
await this.$router.push(`/`) await this.$router.push(`/`)
} }
},
unmounted() {
this.setProject(null)
} }
} }
</script> </script>
......
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