diff --git a/webapp/src/components/genericComponents/miniStreamDisplay.vue b/webapp/src/components/genericComponents/miniStreamDisplay.vue index b699ab97..8b2a1b18 100644 --- a/webapp/src/components/genericComponents/miniStreamDisplay.vue +++ b/webapp/src/components/genericComponents/miniStreamDisplay.vue @@ -5,7 +5,7 @@ class="stream-display uk-width-1-1 uk-height-1-1 scrollTarget" > { + // Always update local visibility instantly this.isVisible = isIntersecting; + + // Clear any previous pending timers to prevent ghost fires + clearTimeout(this.observerTimeout); + + if (isIntersecting) { + // If it's TRUE, we don't want to wait! Turn the stream on immediately. + this.store.addStream(this.streamId); + } else { + // If it's FALSE, wait 50ms before telling the store. + // If a TRUE event fires in the next 50ms (like during a tab switch), + // this timeout will be cancelled and the stream will never flicker off! + this.observerTimeout = setTimeout(() => { + this.store.removeStream(this.streamId); + }, 50); + } }, - { - threshold: 0.0, - }, + { threshold: 0.0 }, ); + this.stopIntersectionObserver = stop; + }, + + beforeUnmount() { + clearTimeout(this.observerTimeout); // Clean up the timer! + if (this.stopIntersectionObserver) { + this.stopIntersectionObserver(); + } + this.store.removeStream(this.streamId); + // Override img source to single pixel and drop MJPEG stream + const imgElement = this.$refs["click-frame"]; + if (imgElement) { + imgElement.src = ONE_PIXEL_FALLBACK; + } }, }; diff --git a/webapp/src/components/labThingsComponents/actionStatusModal.vue b/webapp/src/components/labThingsComponents/actionStatusModal.vue index b73f93de..88ea7f42 100644 --- a/webapp/src/components/labThingsComponents/actionStatusModal.vue +++ b/webapp/src/components/labThingsComponents/actionStatusModal.vue @@ -2,7 +2,11 @@

{{ title }}

- +
@@ -79,6 +83,13 @@ export default { emits: ["terminateTask"], + data() { + return { + // This adds the parent name as value for prop streamId + setStreamId: this.$options.name, + }; + }, + methods: { show() { UIkit.modal(this.$refs.modal).show(); diff --git a/webapp/src/components/modalComponents/calibrationWizard.vue b/webapp/src/components/modalComponents/calibrationWizard.vue index 0dd1dcc2..1e54ec6b 100644 --- a/webapp/src/components/modalComponents/calibrationWizard.vue +++ b/webapp/src/components/modalComponents/calibrationWizard.vue @@ -68,8 +68,8 @@ export default { this.$refs["calibrationModalEl"].addEventListener("hidden", this.onHide); // Check which Things are available on mount. const allCalibrationTasks = { - camera: cameraCalibrationTask, - camera_stage_mapping: cameraStageMappingTask, + camera: markRaw(cameraCalibrationTask), + camera_stage_mapping: markRaw(cameraStageMappingTask), }; this.availableCalibrationTasks = Object.fromEntries( Object.entries(allCalibrationTasks).filter(([thing]) => this.thingAvailable(thing)), diff --git a/webapp/src/components/modalComponents/calibrationWizardComponents/cameraStageMappingTask.vue b/webapp/src/components/modalComponents/calibrationWizardComponents/cameraStageMappingTask.vue index 48f25e9b..c2a5c8c0 100644 --- a/webapp/src/components/modalComponents/calibrationWizardComponents/cameraStageMappingTask.vue +++ b/webapp/src/components/modalComponents/calibrationWizardComponents/cameraStageMappingTask.vue @@ -18,7 +18,7 @@ import runCsmStep from "./csmSteps/runCsmStep.vue"; import { markRaw } from "vue"; export default { - name: "CameraCalibrationTask", + name: "CameraStageMappingTask", components: { calibrationWizardTask, }, diff --git a/webapp/src/components/modalComponents/calibrationWizardComponents/stepTemplateWithStream.vue b/webapp/src/components/modalComponents/calibrationWizardComponents/stepTemplateWithStream.vue index 4859e838..64344cda 100644 --- a/webapp/src/components/modalComponents/calibrationWizardComponents/stepTemplateWithStream.vue +++ b/webapp/src/components/modalComponents/calibrationWizardComponents/stepTemplateWithStream.vue @@ -1,7 +1,7 @@ @@ -15,6 +15,13 @@ export default { components: { miniStreamDisplay, }, + + data() { + return { + // This adds the parent name as value for prop streamId + setStreamId: this.$options.name, + }; + }, }; diff --git a/webapp/src/components/tabContentComponents/actionTab.vue b/webapp/src/components/tabContentComponents/actionTab.vue index 7ebcf511..4555c1ee 100644 --- a/webapp/src/components/tabContentComponents/actionTab.vue +++ b/webapp/src/components/tabContentComponents/actionTab.vue @@ -53,7 +53,7 @@ mode.

{{ taskInfoTitle }}

- +
@@ -141,6 +141,7 @@ export default { progress: null, log: [], pollTimer: null, + setStreamId: this.$options.name, }; }, diff --git a/webapp/src/components/tabContentComponents/controlComponents/paneControl.vue b/webapp/src/components/tabContentComponents/controlComponents/paneControl.vue index 15c2c84b..b9f0a000 100644 --- a/webapp/src/components/tabContentComponents/controlComponents/paneControl.vue +++ b/webapp/src/components/tabContentComponents/controlComponents/paneControl.vue @@ -34,6 +34,7 @@ import axios from "axios"; import ActionButton from "../../labThingsComponents/actionButton.vue"; import positionControl from "./positionControl.vue"; import autofocusControl from "./autofocusControl.vue"; +import { eventBus } from "../../../eventBus.js"; export default { name: "PaneControl", @@ -61,8 +62,12 @@ export default { this.modalError("No image URI returned from capture task."); return; } + // Emit flash capture stream animation + eventBus.emit("globalFlashStream"); + // To save the returned data, we make a virtual link and click it let imageResponse = await axios.get(imageUri, { responseType: "blob" }); + const url = window.URL.createObjectURL(new Blob([imageResponse.data])); const link = document.createElement("a"); link.href = url; diff --git a/webapp/src/components/tabContentComponents/controlContent.vue b/webapp/src/components/tabContentComponents/controlContent.vue index 4a75dcd6..b69872b8 100644 --- a/webapp/src/components/tabContentComponents/controlContent.vue +++ b/webapp/src/components/tabContentComponents/controlContent.vue @@ -5,7 +5,7 @@
- +
@@ -21,6 +21,13 @@ export default { paneControl, streamDisplay, }, + + data() { + return { + // This adds the parent name as value for prop streamId + setStreamId: this.$options.name, + }; + }, }; diff --git a/webapp/src/components/tabContentComponents/settingsComponents/CSMSettings.vue b/webapp/src/components/tabContentComponents/settingsComponents/CSMSettings.vue index 7167d68e..099cd248 100644 --- a/webapp/src/components/tabContentComponents/settingsComponents/CSMSettings.vue +++ b/webapp/src/components/tabContentComponents/settingsComponents/CSMSettings.vue @@ -10,7 +10,7 @@
- +
@@ -28,6 +28,13 @@ export default { CSMCalibrationSettings, miniStreamDisplay, }, + + data() { + return { + // This adds the parent name as value for prop streamId + setStreamId: this.$options.name, + }; + }, }; diff --git a/webapp/src/components/tabContentComponents/settingsComponents/cameraSettings.vue b/webapp/src/components/tabContentComponents/settingsComponents/cameraSettings.vue index 3cd28432..80ead801 100644 --- a/webapp/src/components/tabContentComponents/settingsComponents/cameraSettings.vue +++ b/webapp/src/components/tabContentComponents/settingsComponents/cameraSettings.vue @@ -15,7 +15,7 @@
- +
@@ -41,6 +41,8 @@ export default { data() { return { manualCameraSettings: [], + // This adds the parent name as value for prop streamId + setStreamId: this.$options.name, }; }, diff --git a/webapp/src/components/tabContentComponents/slideScanContent.vue b/webapp/src/components/tabContentComponents/slideScanContent.vue index 6f8255ba..6ab945f9 100644 --- a/webapp/src/components/tabContentComponents/slideScanContent.vue +++ b/webapp/src/components/tabContentComponents/slideScanContent.vue @@ -17,7 +17,7 @@ class="image-fit" :src="lastStitchedImage" /> - + @@ -16,5 +16,12 @@ export default { components: { streamDisplay, }, + + data() { + return { + // This adds the parent name as value for prop streamId + setStreamId: this.$options.name, + }; + }, };