openflexure-microscope-server/src/components/viewComponents/galleryDisplay.vue
2019-11-20 14:10:20 +00:00

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>