From f19604c3013aae7cdec485da5ad6e7893974fc6b Mon Sep 17 00:00:00 2001 From: Rafael Araujo Lehmkuhl Date: Fri, 13 Dec 2024 16:09:09 -0300 Subject: [PATCH] data-lake: Fix unlistening of variables The old implementation was removing all listeners when one opted to unlisten. --- .../custom-widget-elements/Checkbox.vue | 7 +++-- .../custom-widget-elements/Dial.vue | 7 +++-- .../custom-widget-elements/Dropdown.vue | 7 +++-- .../custom-widget-elements/Label.vue | 7 +++-- .../custom-widget-elements/Slider.vue | 7 +++-- .../custom-widget-elements/Switch.vue | 7 +++-- src/components/widgets/Plotter.vue | 7 +++-- src/libs/actions/data-lake.ts | 31 ++++++++++++++----- 8 files changed, 57 insertions(+), 23 deletions(-) diff --git a/src/components/custom-widget-elements/Checkbox.vue b/src/components/custom-widget-elements/Checkbox.vue index 2e326ea4f..c5f74d739 100644 --- a/src/components/custom-widget-elements/Checkbox.vue +++ b/src/components/custom-widget-elements/Checkbox.vue @@ -47,6 +47,7 @@ const props = defineProps<{ const miniWidget = toRefs(props).miniWidget const isChecked = ref(false) +let listenerId: string | undefined const handleToggleAction = (): void => { if (widgetStore.editingMode) return @@ -83,7 +84,7 @@ onMounted(() => { }) } if (miniWidget.value.options.dataLakeVariable) { - listenDataLakeVariable(miniWidget.value.options.dataLakeVariable.name, (value) => { + listenerId = listenDataLakeVariable(miniWidget.value.options.dataLakeVariable.name, (value) => { isChecked.value = value as boolean }) isChecked.value = widgetStore.getMiniWidgetLastValue(miniWidget.value.hash) as boolean @@ -92,8 +93,10 @@ onMounted(() => { onUnmounted(() => { if (miniWidget.value.options.dataLakeVariable) { - unlistenDataLakeVariable(miniWidget.value.options.dataLakeVariable.name) deleteDataLakeVariable(miniWidget.value.options.dataLakeVariable.id) + if (listenerId) { + unlistenDataLakeVariable(miniWidget.value.options.dataLakeVariable.name, listenerId) + } } }) diff --git a/src/components/custom-widget-elements/Dial.vue b/src/components/custom-widget-elements/Dial.vue index a1c454611..915da7ea0 100644 --- a/src/components/custom-widget-elements/Dial.vue +++ b/src/components/custom-widget-elements/Dial.vue @@ -59,6 +59,7 @@ const miniWidget = toRefs(props).miniWidget const potentiometerValue = ref(0) const rotationAngle = ref(-150) +let listenerId: string | undefined watch( () => widgetStore.miniWidgetManagerVars(miniWidget.value.hash).configMenuOpen, @@ -106,7 +107,7 @@ onMounted(() => { }) } if (miniWidget.value.options.dataLakeVariable) { - listenDataLakeVariable(miniWidget.value.options.dataLakeVariable?.name, (value) => { + listenerId = listenDataLakeVariable(miniWidget.value.options.dataLakeVariable?.name, (value) => { setDialValue(value as number) }) const initialValue = widgetStore.getMiniWidgetLastValue(miniWidget.value.hash) @@ -183,8 +184,10 @@ const startDrag = (event: MouseEvent): void => { onUnmounted(() => { if (miniWidget.value.options.dataLakeVariable) { - unlistenDataLakeVariable(miniWidget.value.options.dataLakeVariable.name) deleteDataLakeVariable(miniWidget.value.options.dataLakeVariable.id) + if (listenerId) { + unlistenDataLakeVariable(miniWidget.value.options.dataLakeVariable.name, listenerId) + } } }) diff --git a/src/components/custom-widget-elements/Dropdown.vue b/src/components/custom-widget-elements/Dropdown.vue index 20220a7ca..bff3fff8e 100644 --- a/src/components/custom-widget-elements/Dropdown.vue +++ b/src/components/custom-widget-elements/Dropdown.vue @@ -43,6 +43,7 @@ import { useWidgetManagerStore } from '@/stores/widgetManager' import { CustomWidgetElementOptions, CustomWidgetElementType, SelectorOption } from '@/types/widgets' const widgetStore = useWidgetManagerStore() +let listenerId: string | undefined const props = defineProps<{ /** @@ -112,7 +113,7 @@ onMounted(() => { }) } if (miniWidget.value.options.dataLakeVariable) { - listenDataLakeVariable(miniWidget.value.options.dataLakeVariable.name, (value) => { + listenerId = listenDataLakeVariable(miniWidget.value.options.dataLakeVariable.name, (value) => { selectedOption.value = options.value.find((option) => option.value === value) }) const storedValue = widgetStore.getMiniWidgetLastValue(miniWidget.value.hash) @@ -125,8 +126,10 @@ onMounted(() => { onUnmounted(() => { if (miniWidget.value.options.dataLakeVariable) { - unlistenDataLakeVariable(miniWidget.value.options.dataLakeVariable.name) deleteDataLakeVariable(miniWidget.value.options.dataLakeVariable.id) + if (listenerId) { + unlistenDataLakeVariable(miniWidget.value.options.dataLakeVariable.name, listenerId) + } } }) diff --git a/src/components/custom-widget-elements/Label.vue b/src/components/custom-widget-elements/Label.vue index 514c9491e..3befcc4e0 100644 --- a/src/components/custom-widget-elements/Label.vue +++ b/src/components/custom-widget-elements/Label.vue @@ -41,6 +41,7 @@ const props = defineProps<{ }>() const miniWidget = toRefs(props).miniWidget +let listenerId: string | undefined watch( () => widgetStore.miniWidgetManagerVars(miniWidget.value.hash).configMenuOpen, @@ -72,7 +73,7 @@ onMounted(() => { }) } if (props.miniWidget.options.dataLakeVariable) { - listenDataLakeVariable(props.miniWidget.options.dataLakeVariable?.name, (value) => { + listenerId = listenDataLakeVariable(props.miniWidget.options.dataLakeVariable?.name, (value) => { miniWidget.value.options.text = value as string }) miniWidget.value.options.text = widgetStore.getMiniWidgetLastValue(miniWidget.value.hash) as string @@ -81,8 +82,10 @@ onMounted(() => { onUnmounted(() => { if (miniWidget.value.options.dataLakeVariable) { - unlistenDataLakeVariable(miniWidget.value.options.dataLakeVariable.name) deleteDataLakeVariable(miniWidget.value.options.dataLakeVariable.id) + if (listenerId) { + unlistenDataLakeVariable(miniWidget.value.options.dataLakeVariable.name, listenerId) + } } }) diff --git a/src/components/custom-widget-elements/Slider.vue b/src/components/custom-widget-elements/Slider.vue index aef3e1cfa..1d5d8c3bb 100644 --- a/src/components/custom-widget-elements/Slider.vue +++ b/src/components/custom-widget-elements/Slider.vue @@ -55,6 +55,7 @@ const props = defineProps<{ const miniWidget = toRefs(props).miniWidget const sliderValue = ref(0) +let listenerId: string | undefined watch( () => widgetStore.miniWidgetManagerVars(miniWidget.value.hash).configMenuOpen, @@ -94,7 +95,7 @@ onMounted(() => { }) } if (miniWidget.value.options.dataLakeVariable) { - listenDataLakeVariable(miniWidget.value.options.dataLakeVariable?.name, (value) => { + listenerId = listenDataLakeVariable(miniWidget.value.options.dataLakeVariable?.name, (value) => { sliderValue.value = value as number }) sliderValue.value = widgetStore.getMiniWidgetLastValue(miniWidget.value.hash) as number @@ -103,8 +104,10 @@ onMounted(() => { onUnmounted(() => { if (miniWidget.value.options.dataLakeVariable) { - unlistenDataLakeVariable(miniWidget.value.options.dataLakeVariable.name) deleteDataLakeVariable(miniWidget.value.options.dataLakeVariable.id) + if (listenerId) { + unlistenDataLakeVariable(miniWidget.value.options.dataLakeVariable.name, listenerId) + } } }) diff --git a/src/components/custom-widget-elements/Switch.vue b/src/components/custom-widget-elements/Switch.vue index eee4361c6..c4805e41e 100644 --- a/src/components/custom-widget-elements/Switch.vue +++ b/src/components/custom-widget-elements/Switch.vue @@ -45,6 +45,7 @@ const props = defineProps<{ const miniWidget = toRefs(props).miniWidget const switchValue = ref(true) +let listenerId: string | undefined watch( () => widgetStore.miniWidgetManagerVars(miniWidget.value.hash).configMenuOpen, @@ -83,7 +84,7 @@ onMounted(() => { switchValue.value = true } else if (miniWidget.value.options.dataLakeVariable) { - listenDataLakeVariable(miniWidget.value.options.dataLakeVariable.name, (value) => { + listenerId = listenDataLakeVariable(miniWidget.value.options.dataLakeVariable.name, (value) => { switchValue.value = value as boolean }) switchValue.value = widgetStore.getMiniWidgetLastValue(miniWidget.value.hash) as boolean @@ -92,8 +93,10 @@ onMounted(() => { onUnmounted(() => { if (miniWidget.value.options.dataLakeVariable) { - unlistenDataLakeVariable(miniWidget.value.options.dataLakeVariable.name) deleteDataLakeVariable(miniWidget.value.options.dataLakeVariable.id) + if (listenerId) { + unlistenDataLakeVariable(miniWidget.value.options.dataLakeVariable.name, listenerId) + } } }) diff --git a/src/components/widgets/Plotter.vue b/src/components/widgets/Plotter.vue index 97bd8c77c..d2bb77d77 100644 --- a/src/components/widgets/Plotter.vue +++ b/src/components/widgets/Plotter.vue @@ -139,6 +139,7 @@ const props = defineProps<{ }>() const widget = toRefs(props).widget const availableDataLakeVariables = ref([]) +let listenerId: string | undefined onBeforeMount(() => { // Set initial widget options if they don't exist @@ -180,11 +181,11 @@ const changeDataLakeVariable = (newId: string): void => { } const oldId = widget.value.options.dataLakeVariableId - if (oldId !== undefined) { - unlistenDataLakeVariable(oldId) + if (oldId !== undefined && listenerId) { + unlistenDataLakeVariable(oldId, listenerId) } - listenDataLakeVariable(newId, (value) => { + listenerId = listenDataLakeVariable(newId, (value) => { valuesHistory.push(value as number) cutExtraSamples() diff --git a/src/libs/actions/data-lake.ts b/src/libs/actions/data-lake.ts index 45f7ca782..6bab006a5 100644 --- a/src/libs/actions/data-lake.ts +++ b/src/libs/actions/data-lake.ts @@ -1,3 +1,5 @@ +import { v4 as uuid } from 'uuid' + /** * A variable to be used on a Cockpit action * @param { string } id - The id of the variable @@ -21,7 +23,7 @@ export class DataLakeVariable { const dataLakeVariableInfo: Record = {} export const dataLakeVariableData: Record = {} -const dataLakeVariableListeners: Record void)[]> = {} +const dataLakeVariableListeners: Record void>> = {} export const getAllDataLakeVariablesInfo = (): Record => { return dataLakeVariableInfo @@ -60,21 +62,34 @@ export const deleteDataLakeVariable = (id: string): void => { delete dataLakeVariableData[id] } -export const listenDataLakeVariable = (id: string, listener: (value: string | number | boolean) => void): void => { - if (!dataLakeVariableListeners[id]) { - dataLakeVariableListeners[id] = [] +export const listenDataLakeVariable = ( + variableId: string, + listener: (value: string | number | boolean) => void +): string => { + if (!dataLakeVariableListeners[variableId]) { + dataLakeVariableListeners[variableId] = {} } - dataLakeVariableListeners[id].push(listener) + const listenerId = uuid() + dataLakeVariableListeners[variableId][listenerId] = listener + return listenerId } -export const unlistenDataLakeVariable = (id: string): void => { - delete dataLakeVariableListeners[id] +export const unlistenDataLakeVariable = (variableId: string, listenerId: string): void => { + if (!dataLakeVariableListeners[variableId]) { + console.warn(`No listeners found for variable with id '${variableId}'.`) + return + } + if (!dataLakeVariableListeners[variableId][listenerId]) { + console.warn(`No listener found with id '${listenerId}' for variable with id '${variableId}'.`) + return + } + delete dataLakeVariableListeners[variableId][listenerId] } const notifyDataLakeVariableListeners = (id: string): void => { if (dataLakeVariableListeners[id]) { const value = dataLakeVariableData[id] if (value === undefined) return - dataLakeVariableListeners[id].forEach((listener) => listener(value)) + Object.values(dataLakeVariableListeners[id]).forEach((listener) => listener(value)) } }