294 lines
8 KiB
Vue
294 lines
8 KiB
Vue
<template>
|
|
<div class="galleryDisplay uk-padding uk-padding-remove-top">
|
|
<nav
|
|
class="uk-navbar-container uk-navbar-transparent navbar"
|
|
uk-navbar="mode: click"
|
|
>
|
|
<div
|
|
class="uk-navbar-left uk-padding-remove-top uk-padding-remove-bottom"
|
|
>
|
|
<ul class="uk-navbar-nav">
|
|
<li :class="[sortDescending ? 'uk-active' : '']">
|
|
<a class="uk-icon" href="#" @click="sortDescending = true"
|
|
><i class="material-icons">keyboard_arrow_down</i></a
|
|
>
|
|
</li>
|
|
<li :class="[!sortDescending ? 'uk-active' : '']">
|
|
<a class="uk-icon" href="#" @click="sortDescending = false"
|
|
><i class="material-icons">keyboard_arrow_up</i></a
|
|
>
|
|
</li>
|
|
<li>
|
|
<a href="#">Filter</a>
|
|
<div
|
|
class="uk-navbar-dropdown"
|
|
:class="{
|
|
'uk-light uk-background-secondary':
|
|
$store.state.globalSettings.darkMode
|
|
}"
|
|
>
|
|
<ul class="uk-nav uk-navbar-dropdown-nav">
|
|
<form class="uk-form-stacked">
|
|
<div
|
|
v-for="tag in allTags"
|
|
:key="tag"
|
|
class="uk-margin-small"
|
|
>
|
|
<label
|
|
><input
|
|
:id="tag"
|
|
v-model="checkedTags"
|
|
class="uk-checkbox"
|
|
type="checkbox"
|
|
:value="tag"
|
|
checked
|
|
/>
|
|
{{ tag }}</label
|
|
>
|
|
</div>
|
|
</form>
|
|
</ul>
|
|
</div>
|
|
</li>
|
|
</ul>
|
|
</div>
|
|
</nav>
|
|
|
|
<div
|
|
v-if="$store.getters.ready"
|
|
class="uk-padding-remove-top"
|
|
uk-lightbox="toggle: .lightbox-link"
|
|
>
|
|
<div
|
|
v-if="galleryFolder"
|
|
class="uk-flex uk-flex-middle uk-padding uk-padding-remove-horizontal uk-padding-remove-bottom"
|
|
>
|
|
<a href="#" class="uk-icon uk-margin-remove" @click="galleryFolder = ''"
|
|
><i class="material-icons">arrow_back</i></a
|
|
>
|
|
<div class="uk-margin-left">
|
|
<h3 class="uk-margin-remove uk-margin-left">
|
|
<b>SCAN</b> {{ allScans[galleryFolder].metadata.filename }}
|
|
</h3>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="uk-grid-medium uk-grid-match uk-margin-top" uk-grid>
|
|
<div v-for="item in sortedItems" :key="item.metadata.id">
|
|
<scanCard
|
|
v-if="'isScan' in item"
|
|
:metadata="item.metadata"
|
|
:thumbnail="item.thumbnail"
|
|
/>
|
|
<captureCard v-else :capture-state="item" />
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<script>
|
|
import axios from "axios";
|
|
import captureCard from "./galleryComponents/captureCard.vue";
|
|
import scanCard from "./galleryComponents/scanCard.vue";
|
|
|
|
// Export main app
|
|
export default {
|
|
name: "GalleryDisplay",
|
|
|
|
components: {
|
|
captureCard,
|
|
scanCard
|
|
},
|
|
|
|
data: function() {
|
|
return {
|
|
captureList: [],
|
|
checkedTags: [],
|
|
sortDescending: true,
|
|
galleryFolder: "",
|
|
scanTag: "scan"
|
|
};
|
|
},
|
|
|
|
computed: {
|
|
captureApiUri: function() {
|
|
return this.$store.getters.uri + "/camera/capture";
|
|
},
|
|
|
|
allTags: function() {
|
|
// Return an array of unique tags across all captures
|
|
var tags = [];
|
|
for (var capture of this.captureList) {
|
|
for (var tag of capture.metadata.tags) {
|
|
if (!tags.includes(tag)) {
|
|
tags.push(tag);
|
|
}
|
|
}
|
|
}
|
|
return tags.sort();
|
|
},
|
|
|
|
noScanCaptureList: function() {
|
|
var captures = [];
|
|
for (var capture of this.captureList) {
|
|
// Filter by selected tags
|
|
var tags = capture.metadata.tags;
|
|
|
|
// Add to capture list if matched
|
|
if (!tags.includes(this.scanTag)) {
|
|
captures.push(capture);
|
|
}
|
|
}
|
|
|
|
return captures;
|
|
},
|
|
|
|
allScans: function() {
|
|
// Return an array of unique tags across all captures
|
|
var scans = {};
|
|
|
|
for (var capture of this.captureList) {
|
|
var custom = capture.metadata.custom;
|
|
var tags = capture.metadata.tags;
|
|
|
|
if ("scan_id" in custom) {
|
|
var id = custom["scan_id"];
|
|
|
|
// If this scan ID hasn't been seen before
|
|
if (!(id in scans)) {
|
|
scans[id] = {};
|
|
scans[id].isScan = true;
|
|
scans[id].captureList = [];
|
|
scans[id].metadata = {
|
|
filename: custom.basename,
|
|
time: custom.time,
|
|
id: custom.scan_id
|
|
};
|
|
scans[id].metadata.tags = [];
|
|
scans[id].metadata.custom = {};
|
|
}
|
|
|
|
// Add the capture object to the scan
|
|
scans[id].captureList.push(capture);
|
|
|
|
// Add missing scan metadata, prioritising first capture
|
|
for (var key of Object.keys(custom)) {
|
|
if (!(key in scans[id].metadata.custom)) {
|
|
scans[id].metadata.custom[key] = custom[key];
|
|
}
|
|
}
|
|
|
|
// Append missing tags
|
|
for (var tag of tags) {
|
|
if (!scans[id].metadata.tags.includes(tag)) {
|
|
scans[id].metadata.tags.push(tag);
|
|
}
|
|
}
|
|
|
|
// Create a preview thumbnail
|
|
if (!("thumbnail" in scans[id])) {
|
|
scans[id].thumbnail =
|
|
this.$store.getters.uri +
|
|
"/camera/capture/" +
|
|
capture.metadata.id +
|
|
"/download?thumbnail=true";
|
|
}
|
|
}
|
|
}
|
|
return scans;
|
|
},
|
|
|
|
scanList: function() {
|
|
return Object.values(this.allScans);
|
|
},
|
|
|
|
itemList: function() {
|
|
if (this.galleryFolder) {
|
|
console.log(this.allScans[this.galleryFolder].captureList);
|
|
return this.allScans[this.galleryFolder].captureList;
|
|
} else {
|
|
return this.noScanCaptureList.concat(this.scanList);
|
|
}
|
|
},
|
|
|
|
filteredItems: function() {
|
|
return this.filterCaptureList(this.itemList, this.checkedTags);
|
|
},
|
|
|
|
sortedItems: function() {
|
|
return this.sortCaptureList(this.filteredItems);
|
|
}
|
|
},
|
|
|
|
mounted() {
|
|
// A global signal listener to perform a gallery refresh
|
|
this.$root.$on("globalUpdateCaptureList", () => {
|
|
this.updateCaptureList();
|
|
});
|
|
// A global signal listener to set the gallery folder
|
|
this.$root.$on("globalUpdateCaptureFolder", folder => {
|
|
this.galleryFolder = folder;
|
|
});
|
|
},
|
|
|
|
methods: {
|
|
updateCaptureList: function() {
|
|
console.log("Updating capture list...");
|
|
// Send move request
|
|
axios
|
|
.get(this.captureApiUri)
|
|
.then(response => {
|
|
this.$store.dispatch("updateState"); // Update store state for good measure
|
|
this.captureList = response.data; // Update boxes from response
|
|
})
|
|
.catch(error => {
|
|
this.modalError(error); // Let mixin handle error
|
|
});
|
|
},
|
|
|
|
filterCaptureList: function(list, filterTags) {
|
|
var result = [];
|
|
for (var capture of list) {
|
|
// Assume exclusion
|
|
var includeCapture = false;
|
|
|
|
// Filter by selected tags
|
|
var tags = capture.metadata.tags;
|
|
let checker = (arr, target) => target.every(v => arr.includes(v));
|
|
// True if all tags match
|
|
includeCapture = checker(tags, filterTags);
|
|
|
|
// Add to capture list if matched
|
|
if (includeCapture == true) {
|
|
result.push(capture);
|
|
}
|
|
}
|
|
|
|
return result;
|
|
},
|
|
|
|
sortCaptureList: function(list) {
|
|
function compare(a, b) {
|
|
if (a.metadata.time < b.metadata.time) return -1;
|
|
if (a.metadata.time > b.metadata.time) return 1;
|
|
return 0;
|
|
}
|
|
|
|
if (this.sortDescending == true) {
|
|
return list.sort(compare).reverse();
|
|
} else {
|
|
return list.sort(compare);
|
|
}
|
|
}
|
|
}
|
|
};
|
|
</script>
|
|
|
|
<style scoped lang="less">
|
|
.navbar {
|
|
border-width: 0 0 1px 0;
|
|
border-style: solid;
|
|
border-color: rgba(180, 180, 180, 0.25);
|
|
}
|
|
</style>
|