ui_migration refactor(store) use pinia stores in components
This commit is contained in:
parent
694a5a690f
commit
3a5a7488dd
25 changed files with 284 additions and 273 deletions
|
|
@ -2,8 +2,8 @@
|
|||
<div id="app" class="uk-height-1-1 uk-margin-remove uk-padding-remove" :class="handleTheme">
|
||||
<!-- this stops the app loading until setConnected is committed in the store, this means
|
||||
other components will not load until we have Thing Descriptions. -->
|
||||
<loadingContent v-if="!$store.getters.ready" />
|
||||
<appContent v-if="$store.getters.ready" />
|
||||
<loadingContent v-if="!ready" />
|
||||
<appContent v-else />
|
||||
<!-- Runtime modals -->
|
||||
<div id="modal-center" ref="keyboardManualModal" class="uk-flex-top" uk-modal>
|
||||
<div class="uk-modal-dialog uk-modal-body uk-margin-auto-vertical">
|
||||
|
|
@ -28,6 +28,9 @@ import appContent from "./components/appContent.vue";
|
|||
import loadingContent from "./components/loadingContent.vue";
|
||||
import Mousetrap from "mousetrap";
|
||||
import { eventBus } from "./eventBus.js";
|
||||
import { mapWritableState, mapState, mapActions } from "pinia";
|
||||
import { useSettingsStore } from "@/stores/settings.js";
|
||||
import { useWotStore } from "@/stores/wot.js";
|
||||
|
||||
const move_keys = ["up", "down", "left", "right", "pageup", "pagedown"];
|
||||
|
||||
|
|
@ -75,6 +78,16 @@ export default {
|
|||
},
|
||||
|
||||
computed: {
|
||||
...mapWritableState(useSettingsStore, [
|
||||
"appTheme",
|
||||
"navigationStepSize",
|
||||
"navigationInvert",
|
||||
"microscopeHostname",
|
||||
"error",
|
||||
"waiting",
|
||||
]),
|
||||
...mapState(useSettingsStore, ["ready", "baseUri"]),
|
||||
|
||||
isSystemDark: function () {
|
||||
if (window.matchMedia && window.matchMedia("(prefers-color-scheme: dark)").matches) {
|
||||
return true;
|
||||
|
|
@ -84,9 +97,9 @@ export default {
|
|||
},
|
||||
handleTheme: function () {
|
||||
var isDark = false;
|
||||
if (this.$store.state.appTheme == "dark") {
|
||||
if (this.appTheme == "dark") {
|
||||
isDark = true;
|
||||
} else if (this.$store.state.appTheme == "system") {
|
||||
} else if (this.appTheme == "system") {
|
||||
if (this.systemDark) {
|
||||
isDark = true;
|
||||
}
|
||||
|
|
@ -98,126 +111,130 @@ export default {
|
|||
},
|
||||
},
|
||||
|
||||
// watch origin function refactored
|
||||
watch: {
|
||||
baseUri() {
|
||||
this.checkConnection();
|
||||
},
|
||||
// The "Whole App" Wrapper Logic
|
||||
ready(isReady) {
|
||||
if (isReady) {
|
||||
this.bindHardwareInputs(); // Turn keys ON when connected
|
||||
} else {
|
||||
this.unbindHardwareInputs(); // Turn keys OFF if disconnected
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
mounted() {
|
||||
// Query CSS dark theme preference
|
||||
var mql = window.matchMedia("(prefers-color-scheme: dark)");
|
||||
this.mql = window.matchMedia("(prefers-color-scheme: dark)");
|
||||
this.systemDark = this.mql.matches;
|
||||
// Check for system dark theme when mounted
|
||||
if (mql.matches) {
|
||||
if (this.mql.matches) {
|
||||
this.systemDark = true;
|
||||
}
|
||||
// Create a theme observer to watch for changes
|
||||
this.themeObserver = mql.addListener((e) => {
|
||||
if (e.matches) {
|
||||
this.systemDark = true;
|
||||
} else {
|
||||
this.systemDark = false;
|
||||
}
|
||||
});
|
||||
this.themeWatchdog = (e) => {
|
||||
this.systemDark = e.matches;
|
||||
};
|
||||
this.mql.addEventListener("change", this.themeWatchdog);
|
||||
// Check connection to API
|
||||
this.checkConnection();
|
||||
},
|
||||
|
||||
created: function () {
|
||||
window.addEventListener("beforeunload", this.handleExit);
|
||||
// Scrollwheel listener
|
||||
window.addEventListener("wheel", this.wheelMonitor);
|
||||
// Watch for origin changes
|
||||
this.unwatchOriginFunction = this.$store.watch(
|
||||
(state, getters) => {
|
||||
return getters.baseUri;
|
||||
},
|
||||
() => {
|
||||
this.checkConnection();
|
||||
},
|
||||
);
|
||||
|
||||
// Keyboard shortcuts
|
||||
Mousetrap.bind("?", () => {
|
||||
this.toggleModalElement(this.$refs["keyboardManualModal"]); // Calls the mixin
|
||||
});
|
||||
|
||||
Mousetrap.bind(
|
||||
move_keys,
|
||||
(event, key) => {
|
||||
event.preventDefault();
|
||||
this.keysDown.add(key);
|
||||
this.updateJogFromKeys();
|
||||
},
|
||||
"keydown",
|
||||
);
|
||||
|
||||
Mousetrap.bind(
|
||||
move_keys,
|
||||
(event, key) => {
|
||||
event.preventDefault();
|
||||
this.keysDown.delete(key);
|
||||
this.updateJogFromKeys();
|
||||
},
|
||||
"keyup",
|
||||
);
|
||||
|
||||
this.keyboardManual.push({
|
||||
shortcut: "←↑→↓",
|
||||
description: "Move the microscope stage",
|
||||
});
|
||||
|
||||
this.keyboardManual.push({
|
||||
shortcut: "pgup / pgdn",
|
||||
description: "Move the microscope focus",
|
||||
});
|
||||
|
||||
// Capture
|
||||
Mousetrap.bind("c", () => {
|
||||
eventBus.emit("globalCaptureEvent", {});
|
||||
});
|
||||
this.keyboardManual.push({
|
||||
shortcut: "c",
|
||||
description: "Take a capture",
|
||||
});
|
||||
|
||||
// Autofocus
|
||||
Mousetrap.bind("a", () => {
|
||||
eventBus.emit("globalFastAutofocusEvent", {});
|
||||
});
|
||||
this.keyboardManual.push({
|
||||
shortcut: "a",
|
||||
description: "Fast autofocus",
|
||||
});
|
||||
|
||||
// Increment/decrement tab
|
||||
Mousetrap.bind("shift+down", () => {
|
||||
eventBus.emit("globalIncrementTab", {});
|
||||
});
|
||||
Mousetrap.bind("shift+up", () => {
|
||||
eventBus.emit("globalDecrementTab", {});
|
||||
});
|
||||
this.keyboardManual.push({
|
||||
shortcut: "shift+↑ / shift+↓",
|
||||
description: "Switch tab",
|
||||
});
|
||||
},
|
||||
|
||||
beforeUnmount: function () {
|
||||
// Disconnect the theme observer
|
||||
if (this.themeObserver) {
|
||||
this.themeObserver.disconnect();
|
||||
}
|
||||
this.mql.removeEventListener("change", this.themeWatchdog);
|
||||
// Remove scrollwheel listener
|
||||
window.removeEventListener("wheel", this.wheelMonitor);
|
||||
// Remove origin watcher
|
||||
this.unwatchOriginFunction();
|
||||
// Remove key listeners
|
||||
Mousetrap.reset();
|
||||
},
|
||||
|
||||
methods: {
|
||||
...mapActions(useSettingsStore, ["setConnected"]),
|
||||
|
||||
bindHardwareInputs() {
|
||||
window.addEventListener("wheel", this.wheelMonitor);
|
||||
|
||||
Mousetrap.bind("?", () => {
|
||||
this.toggleModalElement(this.$refs["keyboardManualModal"]);
|
||||
});
|
||||
|
||||
Mousetrap.bind(
|
||||
move_keys,
|
||||
(event, key) => {
|
||||
event.preventDefault();
|
||||
this.keysDown.add(key);
|
||||
this.updateJogFromKeys();
|
||||
},
|
||||
"keydown",
|
||||
);
|
||||
|
||||
Mousetrap.bind(
|
||||
move_keys,
|
||||
(event, key) => {
|
||||
event.preventDefault();
|
||||
this.keysDown.delete(key);
|
||||
this.updateJogFromKeys();
|
||||
},
|
||||
"keyup",
|
||||
);
|
||||
|
||||
this.keyboardManual.push({
|
||||
shortcut: "←↑→↓",
|
||||
description: "Move the microscope stage",
|
||||
});
|
||||
|
||||
this.keyboardManual.push({
|
||||
shortcut: "pgup / pgdn",
|
||||
description: "Move the microscope focus",
|
||||
});
|
||||
|
||||
Mousetrap.bind("c", () => {
|
||||
eventBus.emit("globalCaptureEvent", {});
|
||||
});
|
||||
this.keyboardManual.push({
|
||||
shortcut: "c",
|
||||
description: "Take a capture",
|
||||
});
|
||||
|
||||
Mousetrap.bind("a", () => {
|
||||
eventBus.emit("globalFastAutofocusEvent", {});
|
||||
});
|
||||
this.keyboardManual.push({
|
||||
shortcut: "a",
|
||||
description: "Fast autofocus",
|
||||
});
|
||||
|
||||
Mousetrap.bind("shift+down", () => {
|
||||
eventBus.emit("globalIncrementTab", {});
|
||||
});
|
||||
Mousetrap.bind("shift+up", () => {
|
||||
eventBus.emit("globalDecrementTab", {});
|
||||
});
|
||||
this.keyboardManual.push({
|
||||
shortcut: "shift+↑ / shift+↓",
|
||||
description: "Switch tab",
|
||||
});
|
||||
},
|
||||
|
||||
unbindHardwareInputs() {
|
||||
window.removeEventListener("wheel", this.wheelMonitor);
|
||||
Mousetrap.reset();
|
||||
this.keyboardManual = [];
|
||||
},
|
||||
|
||||
async checkConnection() {
|
||||
var baseUri = this.$store.getters.baseUri;
|
||||
this.$store.commit("changeWaiting", true);
|
||||
// TODO: more robust check - e.g. use a microscope Thing
|
||||
// TODO: should we purge existing consumedThings?
|
||||
this.waiting = true;
|
||||
const wotStore = useWotStore();
|
||||
try {
|
||||
await this.$store.dispatch("wot/fetchThingDescriptions", `${baseUri}/thing_descriptions/`);
|
||||
await wotStore.fetchThingDescriptions(`${this.baseUri}/thing_descriptions/`);
|
||||
for (let requiredThing of ["camera", "system"]) {
|
||||
if (!this.thingAvailable(requiredThing)) {
|
||||
throw new Error(`No ${requiredThing} found, the GUI won't work without one.`);
|
||||
|
|
@ -225,74 +242,48 @@ export default {
|
|||
}
|
||||
try {
|
||||
let hostname = await this.readThingProperty("system", "hostname");
|
||||
this.$store.commit("changeMicroscopeHostname", hostname);
|
||||
this.microscopeHostname = hostname;
|
||||
document.title = `OpenFlexure Microscope: ${hostname}`;
|
||||
} catch {
|
||||
this.$store.commit("changeMicroscopeHostname", null);
|
||||
this.microscopeHostname = null;
|
||||
}
|
||||
// start rendering components
|
||||
this.$store.commit("setConnected");
|
||||
this.$store.commit("setErrorMessage", null);
|
||||
this.setConnected();
|
||||
this.error = null;
|
||||
} catch (error) {
|
||||
this.$store.commit("setErrorMessage", error);
|
||||
this.error = error;
|
||||
} finally {
|
||||
this.$store.commit("changeWaiting", false);
|
||||
this.waiting = false;
|
||||
}
|
||||
},
|
||||
handleExit: function () {},
|
||||
|
||||
/**
|
||||
* Handle global mouse wheel events to be associated with navigation
|
||||
*/
|
||||
wheelMonitor: function (event) {
|
||||
// Only capture scroll if the event target's parent contains the "scrollTarget" class
|
||||
handleExit() {},
|
||||
|
||||
wheelMonitor(event) {
|
||||
if (
|
||||
event.target.parentNode.classList.contains("scrollTarget") ||
|
||||
event.target.classList.contains("scrollTarget")
|
||||
) {
|
||||
const z_rel = event.deltaY / 100;
|
||||
// Emit a signal to move, acted on by panelControl.vue
|
||||
const navigationStepSize = this.$store.state.navigationStepSize;
|
||||
const z = z_rel * navigationStepSize.z;
|
||||
// Don't use `jog() due to variable size of jogs here and the rate limiting in
|
||||
// `jog()`. No need to invert on z, as navigationInvert.z isn't exposed.
|
||||
const z = z_rel * this.navigationStepSize.z;
|
||||
this.invokeAction("stage", "jog", { x: 0, y: 0, z: z });
|
||||
eventBus.emit("globalUpdatePositionEvent");
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Jog for key-presses.
|
||||
*
|
||||
* This is a similar to the function in stageControlButtons.vue however it uses
|
||||
* uses the key repeat to fire in case a key up is missed. It debounces any
|
||||
* request to jog that is too recent after the last jog.
|
||||
*/
|
||||
jog(x, y, z) {
|
||||
// Manually debounce extra requests from keyboard repeat rate.
|
||||
// This is used rather than an interval in case of missing a repeat.
|
||||
const now = Date.now();
|
||||
const navigationInvert = this.$store.state.navigationInvert;
|
||||
if (now - this.lastJogTime < this.jogTime) {
|
||||
return;
|
||||
}
|
||||
this.lastJogTime = now;
|
||||
|
||||
this.invokeAction("stage", "jog", {
|
||||
x: x * this.jogDistance * (navigationInvert.x ? -1 : 1),
|
||||
y: y * this.jogDistance * (navigationInvert.y ? -1 : 1),
|
||||
x: x * this.jogDistance * (this.navigationInvert.x ? -1 : 1),
|
||||
y: y * this.jogDistance * (this.navigationInvert.y ? -1 : 1),
|
||||
z: z * this.jogDistance,
|
||||
});
|
||||
eventBus.emit("globalUpdatePositionEvent");
|
||||
},
|
||||
|
||||
/**
|
||||
* Stop jogging on key-up
|
||||
*
|
||||
* This is also similar to the function in stageControlButtons.vue. It handles
|
||||
* stopping jogging and resetting the `lastJogTime` so there is no delay when
|
||||
* starting a new jog after an old jog finished.
|
||||
*/
|
||||
jogStop() {
|
||||
this.invokeAction("stage", "jog", { stop: true });
|
||||
this.lastJogTime = 0;
|
||||
|
|
@ -301,9 +292,6 @@ export default {
|
|||
}, 100);
|
||||
},
|
||||
|
||||
/**
|
||||
* Track which keys are still down on keypress (or key repeat).
|
||||
*/
|
||||
updateJogFromKeys() {
|
||||
let x = 0,
|
||||
y = 0,
|
||||
|
|
|
|||
|
|
@ -91,6 +91,7 @@ import slideScanContent from "./tabContentComponents/slideScanContent.vue";
|
|||
import viewContent from "./tabContentComponents/viewContent.vue";
|
||||
import { markRaw } from "vue";
|
||||
import { eventBus } from "../eventBus.js";
|
||||
import { useSettingsStore } from "@/stores/settings.js";
|
||||
|
||||
// Import modal components for device initialisation
|
||||
import calibrationWizard from "./modalComponents/calibrationWizard.vue";
|
||||
|
|
@ -196,6 +197,7 @@ export default {
|
|||
},
|
||||
|
||||
mounted() {
|
||||
const store = useSettingsStore();
|
||||
// A global signal listener to switch tab
|
||||
eventBus.on("globalSwitchTab", (tabID) => {
|
||||
this.currentTab = tabID;
|
||||
|
|
@ -208,7 +210,7 @@ export default {
|
|||
eventBus.on("globalDecrementTab", () => {
|
||||
this.incrementTabBy(-1);
|
||||
});
|
||||
if (this.$store.getters.ready) {
|
||||
if (store.ready) {
|
||||
this.startModals();
|
||||
}
|
||||
},
|
||||
|
|
|
|||
|
|
@ -16,6 +16,8 @@
|
|||
|
||||
<script>
|
||||
import { useIntersectionObserver } from "@vueuse/core";
|
||||
import { useSettingsStore } from "@/stores/settings.js";
|
||||
import { mapState } from "pinia";
|
||||
|
||||
// Export main app
|
||||
export default {
|
||||
|
|
@ -28,8 +30,9 @@ export default {
|
|||
},
|
||||
|
||||
computed: {
|
||||
streamImgUri: function () {
|
||||
return `${this.$store.getters.baseUri}/camera/mjpeg_stream`;
|
||||
...mapState(useSettingsStore, ["baseUri"]),
|
||||
streamImgUri() {
|
||||
return `${this.baseUri}/camera/mjpeg_stream`;
|
||||
},
|
||||
},
|
||||
|
||||
|
|
|
|||
|
|
@ -5,6 +5,9 @@
|
|||
</template>
|
||||
|
||||
<script>
|
||||
import { mapState } from "pinia";
|
||||
import { useSettingsStore } from "@/stores/settings";
|
||||
|
||||
export default {
|
||||
name: "ProgressBar",
|
||||
|
||||
|
|
@ -13,6 +16,7 @@ export default {
|
|||
emits: ["set-tab"],
|
||||
|
||||
computed: {
|
||||
...mapState(useSettingsStore, ["ready"]),
|
||||
tooltipOptions: function () {
|
||||
var title = this.id.charAt(0).toUpperCase() + this.id.slice(1);
|
||||
return `pos: right; title: ${title}; delay: 500`;
|
||||
|
|
@ -21,7 +25,7 @@ export default {
|
|||
classObject: function () {
|
||||
return {
|
||||
"tabicon-active": this.currentTab == this.id,
|
||||
"uk-disabled": this.requireConnection && !this.$store.getters.ready,
|
||||
"uk-disabled": this.requireConnection && !this.ready,
|
||||
};
|
||||
},
|
||||
},
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
<template>
|
||||
<div
|
||||
v-if="!(requireConnection && !$store.getters.ready)"
|
||||
:hidden="currentTab != tabID"
|
||||
v-if="!(requireConnection && !ready) && currentTab === tabID"
|
||||
class="uk-width-expand uk-height-1-1"
|
||||
>
|
||||
<slot></slot>
|
||||
|
|
@ -9,6 +8,9 @@
|
|||
</template>
|
||||
|
||||
<script>
|
||||
import { mapState } from "pinia";
|
||||
import { useSettingsStore } from "@/stores/settings";
|
||||
|
||||
export default {
|
||||
name: "TabContent",
|
||||
|
||||
|
|
@ -23,7 +25,9 @@ export default {
|
|||
},
|
||||
requireConnection: Boolean,
|
||||
},
|
||||
computed: {},
|
||||
computed: {
|
||||
...mapState(useSettingsStore, ["ready"]),
|
||||
},
|
||||
|
||||
methods: {},
|
||||
};
|
||||
|
|
|
|||
|
|
@ -14,6 +14,9 @@
|
|||
</template>
|
||||
|
||||
<script>
|
||||
import { mapState } from "pinia";
|
||||
import { useSettingsStore } from "@/stores/settings.js";
|
||||
|
||||
export default {
|
||||
name: "TabIcon",
|
||||
|
||||
|
|
@ -52,6 +55,7 @@ export default {
|
|||
emits: ["set-tab"],
|
||||
|
||||
computed: {
|
||||
...mapState(useSettingsStore, ["ready"]),
|
||||
computedTitle: function () {
|
||||
if (this.title !== undefined) {
|
||||
return this.title;
|
||||
|
|
@ -74,7 +78,7 @@ export default {
|
|||
classObject: function () {
|
||||
return {
|
||||
"tabicon-active": this.currentTab == this.tabID,
|
||||
"uk-disabled": this.requireConnection && !this.$store.getters.ready,
|
||||
"uk-disabled": this.requireConnection && !this.ready,
|
||||
};
|
||||
},
|
||||
},
|
||||
|
|
|
|||
|
|
@ -257,7 +257,7 @@ export default {
|
|||
*
|
||||
*/
|
||||
async checkExistingTasks() {
|
||||
const ongoingTask = await this.getOngingAction(this.thing, this.action);
|
||||
const ongoingTask = await this.getOngoingAction(this.thing, this.action);
|
||||
if (ongoingTask) {
|
||||
// There is a started task
|
||||
this.taskStarted = true;
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@ export default {
|
|||
computed: {
|
||||
barWidthFromProgress: function () {
|
||||
var progress = this.progress <= 100 ? this.progress : 100;
|
||||
var styleString = `width: ${progress}%`;
|
||||
var styleString = progress != null ? `width: ${progress}%` : `width: 0%`;
|
||||
return styleString;
|
||||
},
|
||||
indeterminateProgressBar: function () {
|
||||
|
|
|
|||
|
|
@ -67,11 +67,9 @@ export default {
|
|||
|
||||
computed: {
|
||||
propertyDescription: function () {
|
||||
try {
|
||||
return this.thingDescription(this.thingName).properties[this.propertyName];
|
||||
} catch {
|
||||
return undefined;
|
||||
}
|
||||
const td = this.wotStore.thingDescriptions[this.thingName];
|
||||
if (!td || !td.properties) return undefined;
|
||||
return td.properties[this.propertyName];
|
||||
},
|
||||
},
|
||||
|
||||
|
|
|
|||
|
|
@ -1,10 +1,12 @@
|
|||
<template>
|
||||
<div class="uk-flex uk-flex-column uk-flex-center uk-height-1-1">
|
||||
<div v-if="$store.state.waiting" class="uk-align-center" uk-spinner="ratio: 3"></div>
|
||||
<div v-if="$store.state.waiting" class="uk-align-center">Loading...</div>
|
||||
<div v-if="waiting" class="uk-align-center uk-text-center">
|
||||
<div uk-spinner="ratio: 3"></div>
|
||||
<p>Loading...</p>
|
||||
</div>
|
||||
<span class="material-symbols-outlined uk-align-center error-icon">error_outline</span>
|
||||
<div v-if="$store.state.error" class="uk-align-center">
|
||||
{{ $store.state.error }}
|
||||
<div v-if="error" class="uk-align-center">
|
||||
{{ error }}
|
||||
</div>
|
||||
<div class="uk-align-center">
|
||||
<devTools class="uk-width-medium"></devTools>
|
||||
|
|
@ -14,6 +16,8 @@
|
|||
|
||||
<script>
|
||||
import devTools from "./tabContentComponents/aboutComponents/devTools.vue";
|
||||
import { mapWritableState } from "pinia";
|
||||
import { useSettingsStore } from "@/stores/settings.js";
|
||||
|
||||
// Export main app
|
||||
export default {
|
||||
|
|
@ -23,8 +27,8 @@ export default {
|
|||
devTools,
|
||||
},
|
||||
|
||||
data: function () {
|
||||
return {};
|
||||
computed: {
|
||||
...mapWritableState(useSettingsStore, ["error", "waiting"]),
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
|
|
|||
|
|
@ -22,6 +22,8 @@
|
|||
<script>
|
||||
import stepTemplateWithStream from "../stepTemplateWithStream.vue";
|
||||
import cameraCalibrationSettings from "../../../tabContentComponents/settingsComponents/cameraSettingsComponents/cameraCalibrationSettings.vue";
|
||||
import { useSettingsStore } from "@/stores/settings.js";
|
||||
import { mapState } from "pinia";
|
||||
|
||||
export default {
|
||||
name: "CameraMainCalibrationStep",
|
||||
|
|
@ -34,8 +36,9 @@ export default {
|
|||
emits: ["awaiting-user"],
|
||||
|
||||
computed: {
|
||||
...mapState(useSettingsStore, ["baseUri"]),
|
||||
cameraUri: function () {
|
||||
return `${this.$store.getters.baseUri}/camera/`;
|
||||
return `${this.baseUri}/camera/`;
|
||||
},
|
||||
},
|
||||
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
<div>
|
||||
<form class="uk-form-stacked" action="" method="GET" @submit="overrideAPIHost">
|
||||
<label class="uk-form-label">Override API origin</label>
|
||||
<input v-model="newOrigin" name="overrideOrigin" class="uk-input" type="text" />
|
||||
<input v-model="overrideOrigin" name="overrideOrigin" class="uk-input" type="text" />
|
||||
<label class="uk-form-label">
|
||||
<input v-model="reloadWhenOverridingOrigin" class="uk-checkbox" type="checkbox" />
|
||||
Reload web app with new origin
|
||||
|
|
@ -13,31 +13,30 @@
|
|||
</template>
|
||||
|
||||
<script>
|
||||
// Export main app
|
||||
import { mapWritableState } from "pinia";
|
||||
import { useSettingsStore } from "@/stores/settings.js";
|
||||
|
||||
export default {
|
||||
name: "DevTools",
|
||||
|
||||
components: {},
|
||||
|
||||
data: function () {
|
||||
data() {
|
||||
return {
|
||||
newOrigin: this.$store.state.overrideOrigin,
|
||||
reloadWhenOverridingOrigin: true,
|
||||
};
|
||||
},
|
||||
|
||||
methods: {
|
||||
overrideAPIHost: function (event) {
|
||||
// Save the origin override, so that if we reload the web app, you can easily
|
||||
this.$store.commit("changeOverrideOrigin", this.newOrigin);
|
||||
computed: {
|
||||
...mapWritableState(useSettingsStore, ["overrideOrigin", "origin"]),
|
||||
},
|
||||
|
||||
// If we have elected not to reload the interface, just update the origin
|
||||
// in the store. Otherwise, the form's default action will do the job for us.
|
||||
// TODO: preserve other query parameters when reloading
|
||||
methods: {
|
||||
overrideAPIHost(event) {
|
||||
if (!this.reloadWhenOverridingOrigin) {
|
||||
this.$store.commit("changeOrigin", this.newOrigin);
|
||||
this.origin = this.overrideOrigin;
|
||||
event.preventDefault();
|
||||
}
|
||||
// if reloadWhenOverridingOrigin is true, form submits normally
|
||||
// passing overrideOrigin as a query param in the URL
|
||||
},
|
||||
},
|
||||
};
|
||||
|
|
|
|||
|
|
@ -202,7 +202,7 @@ export default {
|
|||
},
|
||||
async checkIfStartedExternally() {
|
||||
if (!this.taskStarted | !this.taskRunning) {
|
||||
const ongoingTask = await this.getOngingAction(this.thing, this.action);
|
||||
const ongoingTask = await this.getOngoingAction(this.thing, this.action);
|
||||
if (ongoingTask) {
|
||||
this.resetData();
|
||||
const taskUrl = ongoingTask.links.find((t) => t.rel == "self").href;
|
||||
|
|
|
|||
|
|
@ -79,6 +79,8 @@
|
|||
|
||||
<script>
|
||||
import { eventBus } from "@/eventBus.js";
|
||||
import { mapWritableState } from "pinia";
|
||||
import { useSettingsStore } from "@/stores/settings.js";
|
||||
|
||||
export default {
|
||||
name: "StageControlButtons",
|
||||
|
|
@ -97,6 +99,13 @@ export default {
|
|||
jogDistance: 600,
|
||||
jogTime: 300,
|
||||
}),
|
||||
|
||||
computed: {
|
||||
...mapWritableState(useSettingsStore, ["navigationInvert"]),
|
||||
},
|
||||
|
||||
// TODO: jogInterval may need to be cleared on "beforeUnmount()"
|
||||
|
||||
methods: {
|
||||
/**
|
||||
* Jog d-pad and focus buttons.
|
||||
|
|
@ -116,11 +125,10 @@ export default {
|
|||
// pointer is.
|
||||
pointerEvent.target.setPointerCapture(pointerEvent.pointerId);
|
||||
|
||||
const navigationInvert = this.$store.state.navigationInvert;
|
||||
let invokeJog = () =>
|
||||
this.invokeAction("stage", "jog", {
|
||||
x: x * this.jogDistance * (navigationInvert.x ? -1 : 1),
|
||||
y: y * this.jogDistance * (navigationInvert.y ? -1 : 1),
|
||||
x: x * this.jogDistance * (this.navigationInvert.x ? -1 : 1),
|
||||
y: y * this.jogDistance * (this.navigationInvert.y ? -1 : 1),
|
||||
z: z * this.jogDistance,
|
||||
});
|
||||
invokeJog();
|
||||
|
|
|
|||
|
|
@ -92,6 +92,8 @@ import axios from "axios";
|
|||
import PaginateLinks from "@/components/genericComponents/paginateLinks.vue";
|
||||
import EndpointButton from "../labThingsComponents/endpointButton.vue";
|
||||
import { useIntersectionObserver } from "@vueuse/core";
|
||||
import { useSettingsStore } from "@/stores/settings.js";
|
||||
import { mapState } from "pinia";
|
||||
|
||||
export default {
|
||||
name: "LoggingContent",
|
||||
|
|
@ -114,6 +116,7 @@ export default {
|
|||
},
|
||||
|
||||
computed: {
|
||||
...mapState(useSettingsStore, ["baseUri"]),
|
||||
filteredLevels: function () {
|
||||
let cutoffIndex = this.allLevels.indexOf(this.filterLevel);
|
||||
return this.allLevels.slice(cutoffIndex, -1);
|
||||
|
|
@ -130,10 +133,10 @@ export default {
|
|||
return items;
|
||||
},
|
||||
logURI: function () {
|
||||
return `${this.$store.getters.baseUri}/log/`;
|
||||
return `${this.baseUri}/log/`;
|
||||
},
|
||||
logFileURI: function () {
|
||||
return `${this.$store.getters.baseUri}/logfile/`;
|
||||
return `${this.baseUri}/logfile/`;
|
||||
},
|
||||
pagedItems: function () {
|
||||
let startIndex = (this.currentPage - 1) * this.maxitems;
|
||||
|
|
|
|||
|
|
@ -29,6 +29,9 @@
|
|||
|
||||
<script>
|
||||
import axios from "axios";
|
||||
import { useWotStore } from "@/stores/wot.js";
|
||||
import { mapActions } from "pinia";
|
||||
import { useSettingsStore } from "@/stores/settings.js";
|
||||
|
||||
export default {
|
||||
name: "PowerContent",
|
||||
|
|
@ -52,6 +55,8 @@ export default {
|
|||
},
|
||||
|
||||
methods: {
|
||||
...mapActions(useSettingsStore, ["resetState"]),
|
||||
...mapActions(useWotStore, ["deleteAllThingDescriptions"]),
|
||||
systemRequest: function (action) {
|
||||
let message = "";
|
||||
if (action == "reboot") {
|
||||
|
|
@ -61,8 +66,8 @@ export default {
|
|||
}
|
||||
this.modalConfirm(message).then(
|
||||
() => {
|
||||
this.$store.commit("resetState");
|
||||
this.$store.commit("wot/deleteAllThingDescriptions");
|
||||
this.resetState();
|
||||
this.deleteAllThingDescriptions();
|
||||
// Post and silence errors
|
||||
axios.post(this.thingActionUrl("system", action)).catch(() => {});
|
||||
},
|
||||
|
|
|
|||
|
|
@ -82,6 +82,8 @@
|
|||
import axios from "axios";
|
||||
import actionButton from "../../labThingsComponents/actionButton.vue";
|
||||
import EndpointButton from "../../labThingsComponents/endpointButton.vue";
|
||||
import { mapState } from "pinia";
|
||||
import { useSettingsStore } from "@/stores/settings.js";
|
||||
|
||||
// Export main app
|
||||
export default {
|
||||
|
|
@ -109,11 +111,12 @@ export default {
|
|||
emits: ["viewer-requested", "update-requested"],
|
||||
|
||||
computed: {
|
||||
...mapState(useSettingsStore, ["baseUri"]),
|
||||
downloadStitchFile() {
|
||||
return `${this.$store.getters.baseUri}/smart_scan/get_stitch/${this.scanData.name}`;
|
||||
return `${this.baseUri}/smart_scan/get_stitch/${this.scanData.name}`;
|
||||
},
|
||||
thumbnailPath() {
|
||||
return `${this.$store.getters.baseUri}/smart_scan/scans/stitched_thumbnail.jpg?scan_name=${this.scanData.name}&modified=${this.scanData.modified}`;
|
||||
return `${this.baseUri}/smart_scan/scans/stitched_thumbnail.jpg?scan_name=${this.scanData.name}&modified=${this.scanData.modified}`;
|
||||
},
|
||||
},
|
||||
|
||||
|
|
|
|||
|
|
@ -41,18 +41,10 @@
|
|||
</div>
|
||||
</nav>
|
||||
|
||||
<ScanViewerModal
|
||||
ref="scanViewer"
|
||||
:selected-scan="selectedScan"
|
||||
:base-uri="$store.getters.baseUri"
|
||||
/>
|
||||
<ScanViewerModal ref="scanViewer" :selected-scan="selectedScan" :base-uri="baseUri" />
|
||||
|
||||
<!-- Gallery -->
|
||||
<div
|
||||
v-if="$store.getters.ready"
|
||||
class="uk-padding-remove-top"
|
||||
uk-lightbox="toggle: .lightbox-link"
|
||||
>
|
||||
<div v-if="ready" class="uk-padding-remove-top" uk-lightbox="toggle: .lightbox-link">
|
||||
<!-- Gallery capture cards -->
|
||||
<div class="gallery-grid uk-grid-match" uk-grid>
|
||||
<div v-if="scansEmpty">
|
||||
|
|
@ -86,6 +78,9 @@ import scanCard from "./scanListComponents/scanCard.vue";
|
|||
import ScanViewerModal from "./scanListComponents/scanViewer.vue";
|
||||
import { eventBus } from "../../eventBus.js";
|
||||
import { useIntersectionObserver } from "@vueuse/core";
|
||||
import { useSettingsStore } from "@/stores/settings.js";
|
||||
import { mapState, storeToRefs } from "pinia";
|
||||
import { watch } from "vue";
|
||||
|
||||
// Export main app
|
||||
export default {
|
||||
|
|
@ -111,6 +106,7 @@ export default {
|
|||
},
|
||||
|
||||
computed: {
|
||||
...mapState(useSettingsStore, ["baseUri", "ready"]),
|
||||
scansUri() {
|
||||
return this.thingPropertyUrl("smart_scan", "scans");
|
||||
},
|
||||
|
|
@ -119,7 +115,7 @@ export default {
|
|||
},
|
||||
selectedScanDZI() {
|
||||
if (this.selectedScan && this.selectedScan.dzi != "") {
|
||||
return `${this.$store.getters.baseUri}/data/smart_scan/${this.selectedScan.name}/images/${this.selectedScan.dzi}`;
|
||||
return `${this.baseUri}/data/smart_scan/${this.selectedScan.name}/images/${this.selectedScan.dzi}`;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
|
|
@ -134,6 +130,15 @@ export default {
|
|||
},
|
||||
|
||||
async mounted() {
|
||||
const store = useSettingsStore();
|
||||
const { ready } = storeToRefs(store);
|
||||
watch(ready, (isReady) => {
|
||||
if (isReady) {
|
||||
this.updateScans();
|
||||
} else {
|
||||
this.scans = [];
|
||||
}
|
||||
});
|
||||
useIntersectionObserver(
|
||||
this.$refs.galleryDisplay,
|
||||
([{ isIntersecting }]) => {
|
||||
|
|
@ -155,24 +160,6 @@ export default {
|
|||
});
|
||||
},
|
||||
|
||||
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 capture list
|
||||
this.updateScans();
|
||||
} else {
|
||||
// If the connection is now disconnected, empty capture list
|
||||
this.captures = {};
|
||||
}
|
||||
},
|
||||
);
|
||||
},
|
||||
|
||||
beforeUnmount() {
|
||||
// Remove global signal listener to perform a gallery refresh
|
||||
eventBus.off("globalUpdateScans", this.updateScans);
|
||||
|
|
|
|||
|
|
@ -25,6 +25,8 @@
|
|||
import cameraCalibrationSettings from "./cameraSettingsComponents/cameraCalibrationSettings.vue";
|
||||
import miniStreamDisplay from "../../genericComponents/miniStreamDisplay.vue";
|
||||
import ServerSpecifiedPropertyControl from "../../labThingsComponents/serverSpecifiedPropertyControl.vue";
|
||||
import { mapState } from "pinia";
|
||||
import { useSettingsStore } from "@/stores/settings.js";
|
||||
|
||||
// Export main app
|
||||
export default {
|
||||
|
|
@ -43,8 +45,9 @@ export default {
|
|||
},
|
||||
|
||||
computed: {
|
||||
cameraUri: function () {
|
||||
return `${this.$store.getters.baseUri}/camera/`;
|
||||
...mapState(useSettingsStore, ["baseUri"]),
|
||||
cameraUri() {
|
||||
return `${this.baseUri}/camera/`;
|
||||
},
|
||||
},
|
||||
|
||||
|
|
|
|||
|
|
@ -19,6 +19,9 @@
|
|||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import { useSettingsStore } from "@/stores/settings.js";
|
||||
import { mapWritableState } from "pinia";
|
||||
|
||||
// Export main app
|
||||
export default {
|
||||
name: "AppSettings",
|
||||
|
|
@ -28,14 +31,7 @@ export default {
|
|||
},
|
||||
|
||||
computed: {
|
||||
appTheme: {
|
||||
get() {
|
||||
return this.$store.state.appTheme;
|
||||
},
|
||||
set(value) {
|
||||
this.$store.commit("changeAppTheme", value);
|
||||
},
|
||||
},
|
||||
...mapWritableState(useSettingsStore, ["appTheme"]),
|
||||
},
|
||||
|
||||
methods: {
|
||||
|
|
|
|||
|
|
@ -12,6 +12,9 @@
|
|||
</template>
|
||||
|
||||
<script>
|
||||
import { mapState } from "pinia";
|
||||
import { useSettingsStore } from "@/stores/settings.js";
|
||||
|
||||
// Export main app
|
||||
export default {
|
||||
name: "StreamSettings",
|
||||
|
|
@ -21,12 +24,13 @@ export default {
|
|||
},
|
||||
|
||||
computed: {
|
||||
...mapState(useSettingsStore, ["disableStream"]),
|
||||
disableStream: {
|
||||
get() {
|
||||
return this.$store.state.disableStream;
|
||||
return this.disableStream;
|
||||
},
|
||||
set(value) {
|
||||
this.$store.commit("changeDisableStream", value);
|
||||
this.disableStream = value;
|
||||
},
|
||||
},
|
||||
},
|
||||
|
|
|
|||
|
|
@ -10,29 +10,27 @@
|
|||
<div>
|
||||
<label class="uk-form-label" for="form-stacked-text">x</label>
|
||||
<div class="uk-form-controls">
|
||||
<input v-model="stepSize.x" class="uk-input uk-form-small" type="number" />
|
||||
<input v-model="navigationStepSize.x" class="uk-input uk-form-small" type="number" />
|
||||
</div>
|
||||
<label class="uk-margin-small-right">
|
||||
<input v-model="invert.x" class="uk-checkbox" type="checkbox" />
|
||||
<input v-model="navigationInvert.x" class="uk-checkbox" type="checkbox" />
|
||||
Invert x
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label class="uk-form-label" for="form-stacked-text">y</label>
|
||||
<div class="uk-form-controls">
|
||||
<input v-model="stepSize.y" class="uk-input uk-form-small" type="number" />
|
||||
<input v-model="navigationStepSize.y" class="uk-input uk-form-small" type="number" />
|
||||
</div>
|
||||
<label class="uk-margin-small-right">
|
||||
<input v-model="invert.y" class="uk-checkbox" type="checkbox" />
|
||||
<input v-model="navigationInvert.y" class="uk-checkbox" type="checkbox" />
|
||||
Invert y
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label class="uk-form-label" for="form-stacked-text">z</label>
|
||||
<div class="uk-form-controls">
|
||||
<input v-model="stepSize.z" class="uk-input uk-form-small" type="number" />
|
||||
<input v-model="navigationStepSize.z" class="uk-input uk-form-small" type="number" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -40,35 +38,16 @@
|
|||
</template>
|
||||
|
||||
<script>
|
||||
import { storeToRefs } from "pinia";
|
||||
import { useSettingsStore } from "@/stores/settings.js";
|
||||
|
||||
export default {
|
||||
name: "StageControlSettings",
|
||||
|
||||
computed: {
|
||||
// Note that as stepSize and invert are mutated (i.e. we change stepSize.x not stepSize)
|
||||
// rather than directly set we cannot use get() and set() computed to interact with the
|
||||
// store as changed won't be detected by set(). Instead use a deep watcher to
|
||||
// update the store (see ``watch:`` below)
|
||||
stepSize() {
|
||||
return this.$store.state.navigationStepSize;
|
||||
},
|
||||
invert() {
|
||||
return this.$store.state.navigationInvert;
|
||||
},
|
||||
},
|
||||
|
||||
watch: {
|
||||
stepSize: {
|
||||
deep: true,
|
||||
handler(newVal) {
|
||||
this.$store.commit("changeNavigationStepSize", newVal);
|
||||
},
|
||||
},
|
||||
invert: {
|
||||
deep: true,
|
||||
handler(newVal) {
|
||||
this.$store.commit("changeNavigationInvert", newVal);
|
||||
},
|
||||
},
|
||||
setup() {
|
||||
const store = useSettingsStore();
|
||||
const { navigationStepSize, navigationInvert } = storeToRefs(store);
|
||||
return { navigationStepSize, navigationInvert };
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
|
|
|||
|
|
@ -27,6 +27,7 @@
|
|||
</div>
|
||||
<div class="uk-margin">
|
||||
<propertyControl
|
||||
v-if="thingAvailable('smart_scan')"
|
||||
thing-name="smart_scan"
|
||||
property-name="stitch_tiff"
|
||||
label="When Stitching, Produce a Pyramidal TIFF"
|
||||
|
|
@ -41,6 +42,8 @@
|
|||
import propertyControl from "@/components/labThingsComponents/propertyControl.vue";
|
||||
import ServerSpecifiedInterface from "@/components/labThingsComponents/serverSpecifiedInterface.vue";
|
||||
import { useIntersectionObserver } from "@vueuse/core";
|
||||
import { mapWritableState } from "pinia";
|
||||
import { useSettingsStore } from "@/stores/settings.js";
|
||||
|
||||
export default {
|
||||
name: "SlideScanControls",
|
||||
|
|
@ -81,6 +84,7 @@ export default {
|
|||
},
|
||||
|
||||
methods: {
|
||||
...mapWritableState(useSettingsStore, ["ready"]),
|
||||
visibilityChanged(isVisible) {
|
||||
if (isVisible) {
|
||||
this.readSettings();
|
||||
|
|
|
|||
|
|
@ -61,7 +61,8 @@ import actionTab from "./actionTab.vue";
|
|||
import slideScanControls from "./slideScanComponents/slideScanControls.vue";
|
||||
import streamDisplay from "./streamContent.vue";
|
||||
import ActionButton from "../labThingsComponents/actionButton.vue";
|
||||
|
||||
import { mapState } from "pinia";
|
||||
import { useSettingsStore } from "@/stores/settings.js";
|
||||
export default {
|
||||
name: "SlideScanContent",
|
||||
|
||||
|
|
@ -84,6 +85,7 @@ export default {
|
|||
},
|
||||
|
||||
computed: {
|
||||
...mapState(useSettingsStore, ["baseUri"]),
|
||||
scanning() {
|
||||
return this.taskId && this.taskUrl;
|
||||
},
|
||||
|
|
@ -138,7 +140,7 @@ export default {
|
|||
// while the scan is running
|
||||
let mtime = await this.readThingProperty("smart_scan", "latest_preview_stitch_time", true);
|
||||
if (mtime !== null) {
|
||||
this.lastStitchedImage = `${this.$store.getters.baseUri}/smart_scan/latest_preview_stitch.jpg?t=${mtime}`;
|
||||
this.lastStitchedImage = `${this.baseUri}/smart_scan/latest_preview_stitch.jpg?t=${mtime}`;
|
||||
}
|
||||
|
||||
this.lastScanName = await this.readThingProperty("smart_scan", "latest_scan_name", true);
|
||||
|
|
|
|||
|
|
@ -15,14 +15,11 @@
|
|||
/>
|
||||
|
||||
<div v-if="!streamEnabled" class="uk-height-1-1">
|
||||
<div v-if="$store.state.waiting" class="uk-position-center">
|
||||
<div v-if="waiting" class="uk-position-center">
|
||||
<div uk-spinner="ratio: 4.5"></div>
|
||||
</div>
|
||||
|
||||
<div
|
||||
v-else-if="$store.state.disableStream"
|
||||
class="uk-position-center position-relative text-center"
|
||||
>
|
||||
<div v-else-if="disableStream" class="uk-position-center position-relative text-center">
|
||||
Stream preview disabled
|
||||
</div>
|
||||
|
||||
|
|
@ -36,11 +33,20 @@
|
|||
<script>
|
||||
import { eventBus } from "../../eventBus.js";
|
||||
import { useIntersectionObserver } from "@vueuse/core";
|
||||
import { useSettingsStore } from "@/stores/settings.js";
|
||||
import { mapState, mapWritableState } from "pinia";
|
||||
|
||||
// Export main app
|
||||
export default {
|
||||
name: "StreamDisplay",
|
||||
|
||||
setup() {
|
||||
const store = useSettingsStore();
|
||||
return {
|
||||
store,
|
||||
};
|
||||
},
|
||||
|
||||
data: function () {
|
||||
return {
|
||||
isVisible: false,
|
||||
|
|
@ -51,15 +57,17 @@ export default {
|
|||
},
|
||||
|
||||
computed: {
|
||||
...mapState(useSettingsStore, ["ready", "baseUri"]),
|
||||
...mapWritableState(useSettingsStore, ["disableStream", "waiting"]),
|
||||
streamEnabled: function () {
|
||||
return this.$store.getters.ready && !this.$store.state.disableStream;
|
||||
return this.ready && !this.disableStream;
|
||||
},
|
||||
thisStreamOpen: function () {
|
||||
// Only a single MJPEG connection should be open at a time
|
||||
return !(this.displaySize[0] == 0) && !(this.displaySize[1] == 0);
|
||||
},
|
||||
streamImgUri: function () {
|
||||
return `${this.$store.getters.baseUri}/camera/mjpeg_stream`;
|
||||
return `${this.baseUri}/camera/mjpeg_stream`;
|
||||
},
|
||||
},
|
||||
|
||||
|
|
@ -101,7 +109,7 @@ export default {
|
|||
// Disconnect the size observer
|
||||
this.sizeObserver.disconnect();
|
||||
// Remove from the array of active streams
|
||||
this.$store.commit("removeStream", this._uid);
|
||||
this.store.removeStream(this.$.uid);
|
||||
},
|
||||
|
||||
methods: {
|
||||
|
|
@ -154,12 +162,12 @@ export default {
|
|||
// Handle closed stream
|
||||
if (this.displaySize[0] == 0 && this.displaySize[1] == 0) {
|
||||
// If this stream was previously active
|
||||
if (this.$store.state.activeStreams[this._uid] == true) {
|
||||
this.$store.commit("removeStream", this._uid);
|
||||
if (this.store.activeStreams[this.$.uid] == true) {
|
||||
this.store.removeStream(this.$.uid);
|
||||
}
|
||||
// If resized to anything other than zero
|
||||
} else {
|
||||
this.$store.commit("addStream", this._uid);
|
||||
this.store.addStream(this.$.uid);
|
||||
}
|
||||
},
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue