diff --git a/src/core/stores/deviceStore.ts b/src/core/stores/deviceStore.ts
index 53adeb8d..af2318f8 100644
--- a/src/core/stores/deviceStore.ts
+++ b/src/core/stores/deviceStore.ts
@@ -98,6 +98,7 @@ export interface Device {
state: MessageState,
) => void;
setDialogOpen: (dialog: DialogVariant, open: boolean) => void;
+ getDialogOpen: (dialog: DialogVariant) => boolean;
processPacket: (data: ProcessPacketParams) => void;
setMessageDraft: (message: string) => void;
}
@@ -597,6 +598,13 @@ export const useDeviceStore = createStore((set, get) => ({
}),
);
},
+ getDialogOpen: (dialog: DialogVariant) => {
+ const device = get().devices.get(id);
+ if (!device) {
+ throw new Error("Device not found");
+ }
+ return device.dialog[dialog];
+ },
processPacket(data: ProcessPacketParams) {
set(
produce((draft) => {
diff --git a/src/core/utils/eventBus.test.ts b/src/core/utils/eventBus.test.ts
new file mode 100644
index 00000000..39258804
--- /dev/null
+++ b/src/core/utils/eventBus.test.ts
@@ -0,0 +1,71 @@
+import { describe, it, expect, vi, beforeEach } from "vitest";
+import { eventBus } from "@core/utils/eventBus.ts";
+
+describe("EventBus", () => {
+ beforeEach(() => {
+ // Reset event listeners before each test
+ (eventBus as any).listeners = {};
+ });
+
+ it("should register an event listener and trigger it on emit", () => {
+ const mockCallback = vi.fn();
+
+ eventBus.on("dialog:unsafeRoles", mockCallback);
+ eventBus.emit("dialog:unsafeRoles", { action: "confirm" });
+
+ expect(mockCallback).toHaveBeenCalledWith({ action: "confirm" });
+ });
+
+ it("should remove an event listener with off", () => {
+ const mockCallback = vi.fn();
+
+ eventBus.on("dialog:unsafeRoles", mockCallback);
+ eventBus.off("dialog:unsafeRoles", mockCallback);
+ eventBus.emit("dialog:unsafeRoles", { action: "dismiss" });
+
+ expect(mockCallback).not.toHaveBeenCalled();
+ });
+
+ it("should return an unsubscribe function from on", () => {
+ const mockCallback = vi.fn();
+ const unsubscribe = eventBus.on("dialog:unsafeRoles", mockCallback);
+
+ unsubscribe();
+ eventBus.emit("dialog:unsafeRoles", { action: "confirm" });
+
+ expect(mockCallback).not.toHaveBeenCalled();
+ });
+
+ it("should allow multiple listeners for the same event", () => {
+ const mockCallback1 = vi.fn();
+ const mockCallback2 = vi.fn();
+
+ eventBus.on("dialog:unsafeRoles", mockCallback1);
+ eventBus.on("dialog:unsafeRoles", mockCallback2);
+ eventBus.emit("dialog:unsafeRoles", { action: "confirm" });
+
+ expect(mockCallback1).toHaveBeenCalledWith({ action: "confirm" });
+ expect(mockCallback2).toHaveBeenCalledWith({ action: "confirm" });
+ });
+
+ it("should only remove the specific listener when off is called", () => {
+ const mockCallback1 = vi.fn();
+ const mockCallback2 = vi.fn();
+
+ eventBus.on("dialog:unsafeRoles", mockCallback1);
+ eventBus.on("dialog:unsafeRoles", mockCallback2);
+ eventBus.off("dialog:unsafeRoles", mockCallback1);
+ eventBus.emit("dialog:unsafeRoles", { action: "dismiss" });
+
+ expect(mockCallback1).not.toHaveBeenCalled();
+ expect(mockCallback2).toHaveBeenCalledWith({ action: "dismiss" });
+ });
+
+ it("should not fail when calling off on a non-existent listener", () => {
+ const mockCallback = vi.fn();
+ eventBus.off("dialog:unsafeRoles", mockCallback);
+ eventBus.emit("dialog:unsafeRoles", { action: "confirm" });
+
+ expect(mockCallback).not.toHaveBeenCalled(); // No error should occur
+ });
+});
diff --git a/src/core/utils/eventBus.ts b/src/core/utils/eventBus.ts
new file mode 100644
index 00000000..afb922fa
--- /dev/null
+++ b/src/core/utils/eventBus.ts
@@ -0,0 +1,44 @@
+export type EventMap = {
+ 'dialog:unsafeRoles': {
+ action: 'confirm' | 'dismiss';
+ };
+ // add more events as required
+};
+
+export type EventName = keyof EventMap;
+export type EventCallback = (data: EventMap[T]) => void;
+
+class EventBus {
+ private listeners: { [K in EventName]?: Array> } = {};
+
+ public on(event: T, callback: EventCallback): () => void {
+ if (!this.listeners[event]) {
+ this.listeners[event] = [];
+ }
+
+ this.listeners[event]?.push(callback as any);
+
+ return () => {
+ this.off(event, callback);
+ };
+ }
+
+ public off(event: T, callback: EventCallback): void {
+ if (!this.listeners[event]) return;
+
+ const callbackIndex = this.listeners[event]?.indexOf(callback as any);
+ if (callbackIndex !== undefined && callbackIndex > -1) {
+ this.listeners[event]?.splice(callbackIndex, 1);
+ }
+ }
+
+ public emit(event: T, data: EventMap[T]): void {
+ if (!this.listeners[event]) return;
+
+ this.listeners[event]?.forEach(callback => {
+ callback(data);
+ });
+ }
+}
+
+export const eventBus = new EventBus();
\ No newline at end of file
diff --git a/src/tests/setupTests.ts b/src/tests/setupTests.ts
index cc80d782..7e571d4c 100644
--- a/src/tests/setupTests.ts
+++ b/src/tests/setupTests.ts
@@ -1,4 +1,3 @@
-import { vi } from 'vitest';
import "@testing-library/jest-dom";
// Enable auto mocks for our UI components