Replace tasksubmitter with action-button
I've changed tasksubmitter to accept a "thing" and "action" rather than a single URL. This seems more in keeping with propertyControl, and eliminates the need for loads of computed properties. I've also deleted (rather than just commenting out) chunks of the web app that are no longer in use. These may be reinstated in the future - but we can get them from git history. It's less confusing not to have vestigial code in the repo. This builds OK but is not, as yet, tested with hardware.
This commit is contained in:
parent
612b88d58c
commit
cbd8b090ec
22 changed files with 225 additions and 2325 deletions
|
|
@ -1,392 +0,0 @@
|
|||
<template>
|
||||
<div
|
||||
class="capture-card uk-card uk-card-default uk-padding-remove uk-width-medium"
|
||||
:class="{ 'uk-card-secondary': $store.state.darkMode }"
|
||||
>
|
||||
<div class="uk-card-media-top">
|
||||
<a class="lightbox-link" :href="imgURL" :data-caption="name">
|
||||
<img
|
||||
class="uk-width-1-1"
|
||||
:data-src="thumbURL"
|
||||
:alt="id"
|
||||
width="300"
|
||||
height="225"
|
||||
uk-img
|
||||
/>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="uk-card-body uk-padding-small">
|
||||
<div
|
||||
class="uk-width-1-1 uk-margin-small uk-margin-remove-left uk-margin-remove-right"
|
||||
uk-grid
|
||||
>
|
||||
<div class="uk-margin-remove-top uk-padding-remove uk-width-expand">
|
||||
{{ name }}
|
||||
</div>
|
||||
<div class="uk-margin-remove-top uk-padding-remove uk-width-auto">
|
||||
<a href="#" class="uk-icon" @click="delCaptureConfirm()">
|
||||
<span class="material-symbols-outlined">delete</span>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="uk-text-meta uk-margin-remove-top uk-padding-remove uk-width-expand"
|
||||
>
|
||||
<time>{{ time }}</time>
|
||||
</div>
|
||||
<div
|
||||
class="uk-text-meta uk-margin-remove-top uk-padding-remove uk-width-auto"
|
||||
>
|
||||
<a :href="metadataModalTarget" uk-toggle>More...</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="uk-card-footer uk-padding-small">
|
||||
<button
|
||||
v-if="openInImjoyMenuItems.length != 0"
|
||||
class="uk-icon"
|
||||
type="button"
|
||||
>
|
||||
<img
|
||||
style="width:25px;"
|
||||
src="https://imjoy.io/static/img/imjoy-icon.svg"
|
||||
/>
|
||||
</button>
|
||||
<div uk-dropdown="pos: top-center">
|
||||
<ul class="uk-nav uk-dropdown-nav">
|
||||
<li v-for="item in openInImjoyMenuItems" :key="item.name">
|
||||
<a href="#" @click="item.callback(name, imgURL)"
|
||||
><span class="material-symbols-outlined">launch</span
|
||||
>{{ item.title }}</a
|
||||
>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div v-for="tag in tags" :key="tag" class="uk-display-inline">
|
||||
<span
|
||||
v-if="tag === 'temporary'"
|
||||
class="uk-label uk-label-danger uk-margin-small-right"
|
||||
uk-tooltip="title: Capture will be removed automatically; delay: 500"
|
||||
>Temporary</span
|
||||
>
|
||||
<span
|
||||
v-else
|
||||
class="uk-label uk-margin-small-right deletable-label"
|
||||
@click="delTagConfirm(tag)"
|
||||
>
|
||||
{{ tag }}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<a :href="tagModalTarget" uk-toggle>
|
||||
<span class="uk-label uk-label-success uk-margin-small-right">Add</span>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<!-- Metadata modal -->
|
||||
<div :id="metadataModalID" uk-modal>
|
||||
<div
|
||||
class="uk-modal-dialog uk-modal-body"
|
||||
:class="{
|
||||
'uk-light uk-background-secondary': $store.state.darkMode
|
||||
}"
|
||||
>
|
||||
<button class="uk-modal-close-default" type="button" uk-close></button>
|
||||
<h2 class="uk-modal-title">{{ name }}</h2>
|
||||
<p><b>Path: </b>{{ path }}</p>
|
||||
<p><b>Time: </b>{{ time }}</p>
|
||||
<p><b>ID: </b>{{ id }}</p>
|
||||
<p><b>Format: </b>{{ format }}</p>
|
||||
|
||||
<hr />
|
||||
|
||||
<div v-for="(value, key) in annotations" :key="key">
|
||||
<p>
|
||||
<b>{{ key }}: </b>{{ value }}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<hr />
|
||||
|
||||
<div class="uk-flex-bottom" uk-grid>
|
||||
<div class="uk-width-2-3">
|
||||
<keyvalList v-model="newAnnotations" />
|
||||
</div>
|
||||
<div class="uk-width-1-3">
|
||||
<button
|
||||
class="uk-button uk-button-primary uk-form-small uk-width-1-1"
|
||||
@click="handleAnnotationsSubmit()"
|
||||
>
|
||||
Add annotation
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- New tag modal -->
|
||||
<div :id="tagModalID" uk-modal>
|
||||
<form
|
||||
class="uk-modal-dialog uk-modal-body uk-margin-auto-vertical"
|
||||
:class="{
|
||||
'uk-light uk-background-secondary': $store.state.darkMode
|
||||
}"
|
||||
@submit.prevent="handleTagSubmit"
|
||||
>
|
||||
<div class="uk-inline">
|
||||
<span class="uk-form-icon"
|
||||
><span class="material-symbols-outlined">label</span></span
|
||||
>
|
||||
<input
|
||||
v-model="newTag"
|
||||
autofocus
|
||||
class="uk-input uk-form-width-medium uk-form-small"
|
||||
type="text"
|
||||
name="tagname"
|
||||
placeholder="tag"
|
||||
/>
|
||||
|
||||
<button
|
||||
class="uk-button uk-button-default uk-margin-left uk-form-small uk-modal-close"
|
||||
type="button"
|
||||
>
|
||||
Cancel
|
||||
</button>
|
||||
<button
|
||||
type="submit"
|
||||
class="uk-button uk-button-primary uk-margin-left uk-form-small"
|
||||
>
|
||||
Save
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import UIkit from "uikit";
|
||||
import axios from "axios";
|
||||
import { mapState } from "vuex";
|
||||
|
||||
import keyvalList from "../../fieldComponents/keyvalList";
|
||||
|
||||
// Export main app
|
||||
export default {
|
||||
name: "CaptureCard",
|
||||
|
||||
components: {
|
||||
keyvalList
|
||||
},
|
||||
|
||||
props: {
|
||||
id: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
name: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
time: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
path: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
format: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
links: {
|
||||
type: Object,
|
||||
required: true
|
||||
},
|
||||
tags: {
|
||||
type: Array,
|
||||
required: false,
|
||||
default: function() {
|
||||
return [];
|
||||
}
|
||||
},
|
||||
annotations: {
|
||||
type: Object,
|
||||
required: false,
|
||||
default: function() {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
data: function() {
|
||||
return {
|
||||
newTag: "",
|
||||
newAnnotations: {}
|
||||
};
|
||||
},
|
||||
|
||||
computed: {
|
||||
tagModalID: function() {
|
||||
return this.makeModalName("tag-modal-");
|
||||
},
|
||||
tagModalTarget: function() {
|
||||
return "#" + this.tagModalID;
|
||||
},
|
||||
metadataModalID: function() {
|
||||
return this.makeModalName("metadata-modal-");
|
||||
},
|
||||
metadataModalTarget: function() {
|
||||
return "#" + this.metadataModalID;
|
||||
},
|
||||
thumbURL: function() {
|
||||
return `${this.links.download.href}?thumbnail=true`;
|
||||
},
|
||||
imgURL: function() {
|
||||
return this.links.download.href;
|
||||
},
|
||||
tagsURL: function() {
|
||||
return this.links.tags.href;
|
||||
},
|
||||
annotationsURL: function() {
|
||||
return this.links.annotations.href;
|
||||
},
|
||||
captureURL: function() {
|
||||
return this.links.self.href;
|
||||
},
|
||||
...mapState("imjoy", { openInImjoyMenuItems: "openImageMenu" })
|
||||
},
|
||||
|
||||
created: function() {},
|
||||
|
||||
methods: {
|
||||
getMetadata: function() {
|
||||
// Send metadata request
|
||||
axios
|
||||
.get(this.captureURL)
|
||||
.then(response => {
|
||||
this.$emit("update:tags", response.data.metadata.image.tags);
|
||||
this.$emit(
|
||||
"update:annotations",
|
||||
response.data.metadata.image.annotations
|
||||
);
|
||||
})
|
||||
.catch(error => {
|
||||
this.modalError(error); // Let mixin handle error
|
||||
});
|
||||
},
|
||||
handleTagSubmit: function(event) {
|
||||
if (this.newTag !== "") {
|
||||
this.newTagRequest(this.newTag);
|
||||
this.newTag = "";
|
||||
}
|
||||
UIkit.modal(event.target.parentNode).hide();
|
||||
},
|
||||
|
||||
handleAnnotationsSubmit: function() {
|
||||
this.putAnnotationsRequest(this.newAnnotations);
|
||||
this.newAnnotations = {};
|
||||
},
|
||||
|
||||
delCaptureConfirm: function() {
|
||||
var context = this;
|
||||
this.modalConfirm("Permanantly delete capture?").then(function() {
|
||||
context.delCaptureRequest();
|
||||
});
|
||||
},
|
||||
|
||||
delCaptureRequest: function() {
|
||||
// Send tag DELETE request
|
||||
axios
|
||||
.delete(this.captureURL)
|
||||
.then(() => {
|
||||
// Emit signal to update capture list
|
||||
this.$root.$emit("globalUpdateCaptures");
|
||||
})
|
||||
.catch(error => {
|
||||
this.modalError(error); // Let mixin handle error
|
||||
});
|
||||
},
|
||||
|
||||
newTagRequest: function(tagString) {
|
||||
// Send tag PUT request
|
||||
axios
|
||||
.put(this.tagsURL, [tagString])
|
||||
.then(() => {
|
||||
// Update tag array
|
||||
this.getTagRequest();
|
||||
})
|
||||
.catch(error => {
|
||||
this.modalError(error); // Let mixin handle error
|
||||
});
|
||||
},
|
||||
|
||||
putAnnotationsRequest: function(annotationsObject) {
|
||||
// Send metadata PUT request
|
||||
axios
|
||||
.put(this.annotationsURL, annotationsObject)
|
||||
.then(() => {
|
||||
// Update metadata object
|
||||
this.getAnnotationsRequest();
|
||||
})
|
||||
.catch(error => {
|
||||
this.modalError(error); // Let mixin handle error
|
||||
});
|
||||
},
|
||||
|
||||
delTagConfirm: function(tagString) {
|
||||
var context = this;
|
||||
this.modalConfirm(`Remove tag '${tagString}'?`).then(function() {
|
||||
context.delTagRequest(tagString);
|
||||
});
|
||||
},
|
||||
|
||||
delTagRequest: function(tagString) {
|
||||
// Send tag DELETE request
|
||||
axios
|
||||
.delete(this.tagsURL, { data: [tagString] })
|
||||
.then(() => {
|
||||
// Update tag array
|
||||
this.getTagRequest();
|
||||
})
|
||||
.catch(error => {
|
||||
this.modalError(error); // Let mixin handle error
|
||||
});
|
||||
},
|
||||
|
||||
getTagRequest: function() {
|
||||
// Send tag request
|
||||
axios
|
||||
.get(this.tagsURL)
|
||||
.then(response => {
|
||||
// Pass the update tag array to the parent
|
||||
this.$emit("update:tags", response.data);
|
||||
})
|
||||
.catch(error => {
|
||||
this.modalError(error); // Let mixin handle error
|
||||
});
|
||||
},
|
||||
|
||||
getAnnotationsRequest: function() {
|
||||
// Send tag request
|
||||
axios
|
||||
.get(this.annotationsURL)
|
||||
.then(response => {
|
||||
// Pass the update annotation array to the parent
|
||||
this.$emit("update:annotations", response.data);
|
||||
})
|
||||
.catch(error => {
|
||||
this.modalError(error); // Let mixin handle error
|
||||
});
|
||||
},
|
||||
|
||||
makeModalName: function(prefix) {
|
||||
return prefix + this.id;
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
|
@ -1,154 +0,0 @@
|
|||
<template>
|
||||
<div
|
||||
class="capture-card uk-card uk-card-primary uk-padding-remove uk-width-medium"
|
||||
>
|
||||
<div class="uk-card-media-top">
|
||||
<a href="#">
|
||||
<img
|
||||
class="uk-width-1-1"
|
||||
:data-src="thumbnail"
|
||||
:alt="id"
|
||||
width="300"
|
||||
height="225"
|
||||
uk-img
|
||||
@click="onClick"
|
||||
/>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="uk-card-body uk-padding-small">
|
||||
<div
|
||||
class="uk-width-1-1 uk-margin-small uk-margin-remove-left uk-margin-remove-right"
|
||||
uk-grid
|
||||
>
|
||||
<div class="uk-margin-remove-top uk-padding-remove uk-width-expand">
|
||||
<b>{{ type }}: </b>
|
||||
{{ name }}
|
||||
</div>
|
||||
<div class="uk-margin-remove-top uk-padding-remove uk-width-auto">
|
||||
<a href="#" class="uk-icon" @click="delAllConfirm()">
|
||||
<span class="material-symbols-outlined">delete</span>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="uk-text-meta uk-margin-remove-top uk-padding-remove uk-width-expand"
|
||||
>
|
||||
<time>{{ time }}</time>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="uk-card-footer uk-padding-small">
|
||||
<button
|
||||
v-if="openInImjoyMenuItems.length != 0"
|
||||
class="uk-icon"
|
||||
type="button"
|
||||
>
|
||||
<img
|
||||
style="width:25px;"
|
||||
src="https://imjoy.io/static/img/imjoy-icon.svg"
|
||||
/>
|
||||
</button>
|
||||
<div uk-dropdown="pos: top-center">
|
||||
<ul class="uk-nav uk-dropdown-nav">
|
||||
<li v-for="item in openInImjoyMenuItems" :key="item.name">
|
||||
<a
|
||||
href="#"
|
||||
style="color:black"
|
||||
@click="item.callback(name, allURLs)"
|
||||
><span class="material-symbols-outlined">launch</span
|
||||
>{{ item.title }}</a
|
||||
>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<span
|
||||
v-for="tag in tags"
|
||||
:key="tag"
|
||||
class="uk-label uk-margin-small-right deletable-label"
|
||||
>
|
||||
{{ tag }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import axios from "axios";
|
||||
import { mapState } from "vuex";
|
||||
|
||||
// Export main app
|
||||
export default {
|
||||
name: "ScanCard",
|
||||
|
||||
props: {
|
||||
id: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
name: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
time: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
type: {
|
||||
type: String,
|
||||
required: false,
|
||||
default: "Dataset"
|
||||
},
|
||||
thumbnail: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
captures: {
|
||||
type: Array,
|
||||
required: true
|
||||
},
|
||||
tags: {
|
||||
type: Array,
|
||||
required: false,
|
||||
default: function() {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
computed: {
|
||||
allURLs: function() {
|
||||
var urls = [];
|
||||
for (var capture of this.captures) {
|
||||
urls.push(capture.links.self.href);
|
||||
}
|
||||
return urls;
|
||||
},
|
||||
...mapState("imjoy", { openInImjoyMenuItems: "openScanMenu" })
|
||||
},
|
||||
|
||||
methods: {
|
||||
onClick: function() {
|
||||
this.$emit("selectFolder", this.id);
|
||||
},
|
||||
|
||||
delAllConfirm: function() {
|
||||
var context = this;
|
||||
this.modalConfirm(
|
||||
"Permanantly delete all captures in this dataset?"
|
||||
).then(function() {
|
||||
context.deleteAll();
|
||||
});
|
||||
},
|
||||
|
||||
deleteAll: function() {
|
||||
axios.all(this.allURLs.map(l => axios.delete(l))).then(() => {
|
||||
// Emit signal to update capture list
|
||||
this.$root.$emit("globalUpdateCaptures");
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
|
@ -1,163 +0,0 @@
|
|||
<template>
|
||||
<div v-if="zipBuilderUri" class="uk-width-small">
|
||||
<div v-show="downloadReady">
|
||||
<button
|
||||
:disabled="isDownloading"
|
||||
type="button"
|
||||
class="uk-button uk-button-default uk-width-1-1"
|
||||
@click="downloadWithAxios()"
|
||||
>
|
||||
{{ isDownloading ? `${downloadProgress}%` : "Download" }}
|
||||
</button>
|
||||
</div>
|
||||
<div v-show="!downloadReady">
|
||||
<taskSubmitter
|
||||
v-if="zipBuilderUri"
|
||||
:can-terminate="false"
|
||||
:submit-url="zipBuilderUri"
|
||||
:submit-label="'Create ZIP'"
|
||||
:submit-data="captureIds"
|
||||
@response="onResponse"
|
||||
@error="modalError"
|
||||
>
|
||||
</taskSubmitter>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import axios from "axios";
|
||||
import taskSubmitter from "../../genericComponents/taskSubmitter";
|
||||
|
||||
// Export main app
|
||||
export default {
|
||||
name: "ZipDownloader",
|
||||
|
||||
components: {
|
||||
taskSubmitter
|
||||
},
|
||||
|
||||
props: {
|
||||
captureIds: {
|
||||
type: Array,
|
||||
required: true
|
||||
}
|
||||
},
|
||||
|
||||
data: function() {
|
||||
return {
|
||||
downloadProgress: 0,
|
||||
isDownloading: false,
|
||||
downloadReady: false,
|
||||
downloadUrl: null,
|
||||
zipBuilderUri: null,
|
||||
zipGetterUri: null,
|
||||
lastSessionId: null
|
||||
};
|
||||
},
|
||||
|
||||
mounted: function() {
|
||||
this.updateZipperUri();
|
||||
},
|
||||
|
||||
created: function() {
|
||||
// Watch for host 'ready', then update status
|
||||
this.unwatchStoreFunction = this.$store.watch(
|
||||
(state, getters) => {
|
||||
return getters.ready;
|
||||
},
|
||||
ready => {
|
||||
if (ready) {
|
||||
// If the connection is now ready, update zipper URL
|
||||
this.updateZipperUri();
|
||||
}
|
||||
}
|
||||
);
|
||||
},
|
||||
|
||||
methods: {
|
||||
updateZipperUri: function() {
|
||||
if (this.$store.state.available) {
|
||||
/*axios
|
||||
.get(this.pluginsUri) // Get a list of plugins
|
||||
.then(response => {
|
||||
var plugins = response.data;
|
||||
var foundExtension = plugins.find(
|
||||
e => e.title === "org.openflexure.zipbuilder"
|
||||
);
|
||||
// if ZipBuilderPlugin is enabled
|
||||
if (foundExtension) {
|
||||
// Get plugin action links
|
||||
this.zipBuilderUri = foundExtension.links.build.href;
|
||||
this.zipGetterUri = foundExtension.links.get.href;
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
this.modalError(error); // Let mixin handle error
|
||||
});*/
|
||||
} else {
|
||||
this.zipBuilderUri = null;
|
||||
this.zipGetterUri = null;
|
||||
}
|
||||
},
|
||||
|
||||
resetZipper: function() {
|
||||
this.downloadReady = false;
|
||||
},
|
||||
|
||||
forceFileDownload(response) {
|
||||
// Start file download by creating and clicking an invisible link
|
||||
// One day I hope for a better solution to exist for this...
|
||||
// A man can dream.....
|
||||
const url = window.URL.createObjectURL(new Blob([response.data]));
|
||||
const link = document.createElement("a");
|
||||
link.href = url;
|
||||
link.setAttribute("download", `${this.lastSessionId}.zip`); //or any other extension
|
||||
document.body.appendChild(link);
|
||||
link.click();
|
||||
},
|
||||
|
||||
downloadWithAxios() {
|
||||
// Configure progress indicator and response type
|
||||
let config = {
|
||||
responseType: "blob",
|
||||
onDownloadProgress: progressEvent => {
|
||||
this.downloadProgress = Math.floor(
|
||||
(progressEvent.loaded * 100) / progressEvent.total
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
// Set downloading flag
|
||||
this.isDownloading = true;
|
||||
|
||||
// Start download
|
||||
axios
|
||||
.get(this.downloadUrl, config)
|
||||
.then(response => {
|
||||
this.forceFileDownload(response);
|
||||
this.deleteLastZip();
|
||||
this.resetZipper();
|
||||
})
|
||||
.catch(error => {
|
||||
this.modalError(error); // Let mixin handle error
|
||||
})
|
||||
.finally(() => {
|
||||
this.isDownloading = false;
|
||||
});
|
||||
},
|
||||
|
||||
deleteLastZip() {
|
||||
axios.delete(this.downloadUrl).catch(error => {
|
||||
this.modalError(error); // Let mixin handle error
|
||||
});
|
||||
},
|
||||
|
||||
onResponse: function(response) {
|
||||
this.lastSessionId = response.output.id;
|
||||
this.downloadUrl = `${this.zipGetterUri}/${this.lastSessionId}`;
|
||||
this.downloadReady = true;
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
Loading…
Add table
Add a link
Reference in a new issue