diff --git a/packages/technical-features/messaging/electron/main/src/message-broadcaster-listener/message-broadcaster-listener.injectable.ts b/packages/technical-features/messaging/electron/main/src/message-broadcaster-listener/message-broadcaster-listener.injectable.ts new file mode 100644 index 0000000000..146e72c91b --- /dev/null +++ b/packages/technical-features/messaging/electron/main/src/message-broadcaster-listener/message-broadcaster-listener.injectable.ts @@ -0,0 +1,20 @@ +import { getMessageChannel, getMessageChannelListenerInjectable } from "@k8slens/messaging"; +import { sendMessageToChannelInjectionToken } from "@k8slens/messaging"; + +type BroadcasterChannelMessage = { targetChannelId: string; message: unknown }; + +const broadcasterChannel = getMessageChannel("messaging-broadcaster-in-main"); + +export const messageBroadcasterListenerInjectable = getMessageChannelListenerInjectable({ + id: "message-broadcaster-listener", + channel: broadcasterChannel, + + getHandler: (di) => { + const sendMessageToChannel = di.inject(sendMessageToChannelInjectionToken); + + return ({ targetChannelId, message }, extraData) => { + console.log("mikkomikko", { targetChannelId, message }); + sendMessageToChannel({ id: targetChannelId }, message, extraData); + }; + }, +}); diff --git a/packages/technical-features/messaging/electron/main/src/message-broadcaster-listener/message-broadcaster-listener.test.ts b/packages/technical-features/messaging/electron/main/src/message-broadcaster-listener/message-broadcaster-listener.test.ts new file mode 100644 index 0000000000..1a4878158f --- /dev/null +++ b/packages/technical-features/messaging/electron/main/src/message-broadcaster-listener/message-broadcaster-listener.test.ts @@ -0,0 +1,57 @@ +import { registerFeature } from "@k8slens/feature-core"; +import { createContainer } from "@ogre-tools/injectable"; +import { registerMobX } from "@ogre-tools/injectable-extension-for-mobx"; +import { messagingFeatureForMain } from "../feature"; +import sendMessageToChannelInjectable from "../send-message-to-channel/send-message-to-channel.injectable"; +import { startApplicationInjectionToken } from "@k8slens/application"; +import { runInAction } from "mobx"; +import ipcMainInjectable from "../ipc-main/ipc-main.injectable"; +import type { IpcMainEvent } from "electron"; + +describe("message-broadcaster-listener", () => { + let sendMessageToChannelMock: jest.Mock; + let onMock: jest.Mock; + + beforeEach(async () => { + const di = createContainer("irrelevant"); + + registerMobX(di); + + runInAction(() => { + registerFeature(di, messagingFeatureForMain); + }); + + sendMessageToChannelMock = jest.fn(); + + onMock = jest.fn(); + + di.override( + ipcMainInjectable, + + () => + ({ + on: onMock, + off: () => {}, + } as unknown), + ); + + di.override(sendMessageToChannelInjectable, () => sendMessageToChannelMock); + + const startApplication = di.inject(startApplicationInjectionToken); + + await startApplication(); + }); + + describe("when message to broadcaster channel arrives", () => { + beforeEach(() => { + onMock.mock.calls[0][1]({ frameId: 42, processId: 84 } as IpcMainEvent, { + targetChannelId: "some-target-channel-id", + message: "some-message", + }); + }); + + it("rebroadcasts message to the target channel", () => { + expect(sendMessageToChannelMock).toHaveBeenCalledWith({ id: "some-target-channel-id" }, "some-message"); + }); + }); +}); diff --git a/packages/technical-features/messaging/electron/main/src/send-message-to-channel/send-message-to-channel.injectable.test.ts b/packages/technical-features/messaging/electron/main/src/send-message-to-channel/send-message-to-channel.injectable.test.ts index 5c5a486658..7aead6d4b9 100644 --- a/packages/technical-features/messaging/electron/main/src/send-message-to-channel/send-message-to-channel.injectable.test.ts +++ b/packages/technical-features/messaging/electron/main/src/send-message-to-channel/send-message-to-channel.injectable.test.ts @@ -5,6 +5,7 @@ import { getMessageChannel, sendMessageToChannelInjectionToken } from "@k8slens/ import getWebContentsInjectable from "./get-web-contents.injectable"; import type { WebContents } from "electron"; import allowCommunicationListenerInjectable from "./allow-communication-listener.injectable"; +import { getMessageChannelListenerInjectable } from "@k8slens/messaging"; const someChannel = getMessageChannel("some-channel"); @@ -17,12 +18,24 @@ describe("send-message-to-channel", () => { registerFeature(di, messagingFeatureForMain); }); - it("given no web contents, when sending a message, does not do anything", () => { + it("given no web contents but with local listeners, when sending a message, messages the local listeners", () => { di.override(getWebContentsInjectable, () => () => []); + const localHandlerMock = jest.fn(); + + const someLocalListenerInjectable = getMessageChannelListenerInjectable({ + id: "some-local-listener", + channel: someChannel, + getHandler: () => localHandlerMock, + }); + + di.register(someLocalListenerInjectable); + const sendMessageToChannel = di.inject(sendMessageToChannelInjectionToken); - expect(() => sendMessageToChannel(someChannel, "some-message")).not.toThrow(); + sendMessageToChannel(someChannel, "some-message"); + + expect(localHandlerMock).toHaveBeenCalledWith("some-message"); }); describe("given web content that is alive", () => { diff --git a/packages/technical-features/messaging/electron/main/src/send-message-to-channel/send-message-to-channel.injectable.ts b/packages/technical-features/messaging/electron/main/src/send-message-to-channel/send-message-to-channel.injectable.ts index e68d8e1577..a4b8825008 100644 --- a/packages/technical-features/messaging/electron/main/src/send-message-to-channel/send-message-to-channel.injectable.ts +++ b/packages/technical-features/messaging/electron/main/src/send-message-to-channel/send-message-to-channel.injectable.ts @@ -2,9 +2,10 @@ import { getInjectable } from "@ogre-tools/injectable"; import { pipeline } from "@ogre-tools/fp"; import { SendMessageToChannel, sendMessageToChannelInjectionToken } from "@k8slens/messaging"; import getWebContentsInjectable from "./get-web-contents.injectable"; -import { flatMap, reject } from "lodash/fp"; +import { filter, flatMap, reject } from "lodash/fp"; import type { WebContents } from "electron"; import frameIdsInjectable from "./frameIds.injectable"; +import { messageChannelListenerInjectionToken } from "@k8slens/messaging"; const isDestroyed = (webContent: WebContents) => webContent.isDestroyed(); const isCrashed = (webContent: WebContents) => webContent.isCrashed(); @@ -20,25 +21,43 @@ const sendMessageToChannelInjectable = getInjectable({ instantiate: (di) => { const getWebContents = di.inject(getWebContentsInjectable); const frameIds = di.inject(frameIdsInjectable); + const getMessageChannelListeners = () => di.injectMany(messageChannelListenerInjectionToken); - return ((channel, message) => { - pipeline( - getWebContents(), - reject(isDestroyed), - reject(isCrashed), + return ((channel, message, ...asd) => { + const messageChannelListeners = getMessageChannelListeners(); - flatMap((webContent) => [ - (channelId: string, ...args: any[]) => webContent.send(channelId, ...args), - - ...[...frameIds].map(({ frameId, processId }) => (channelId: string, ...args: any[]) => { - webContent.sendToFrame([frameId, processId], channelId, ...args); + const sendToListenersInMain = () => { + pipeline( + messageChannelListeners, + filter((listener) => listener.channel.id === channel.id), + forEach(({ channel, handler }) => { + handler(message, ...asd); }), - ]), + ); + }; - forEach((send) => { - send(channel.id, message); - }), - ); + const sendToListenersInRenderers = () => { + pipeline( + getWebContents(), + reject(isDestroyed), + reject(isCrashed), + + flatMap((webContent) => [ + (channelId: string, ...args: any[]) => webContent.send(channelId, ...args), + + ...[...frameIds].map(({ frameId, processId }) => (channelId: string, ...args: any[]) => { + webContent.sendToFrame([frameId, processId], channelId, ...args); + }), + ]), + + forEach((send) => { + send(channel.id, message); + }), + ); + }; + + sendToListenersInMain(); + sendToListenersInRenderers(); }) as SendMessageToChannel; }, diff --git a/packages/technical-features/messaging/electron/renderer/src/sending-of-messages/message-to-channel.injectable.ts b/packages/technical-features/messaging/electron/renderer/src/sending-of-messages/message-to-channel.injectable.ts index 469a1548ab..cd337e341c 100644 --- a/packages/technical-features/messaging/electron/renderer/src/sending-of-messages/message-to-channel.injectable.ts +++ b/packages/technical-features/messaging/electron/renderer/src/sending-of-messages/message-to-channel.injectable.ts @@ -1,6 +1,11 @@ import { getInjectable } from "@ogre-tools/injectable"; import sendToIpcInjectable from "./send-to-ipc.injectable"; import { SendMessageToChannel, sendMessageToChannelInjectionToken } from "@k8slens/messaging"; +import { getMessageChannel } from "@k8slens/messaging"; + +type BroadcasterChannelMessage = { targetChannelId: string; message: unknown }; + +const broadcasterChannel = getMessageChannel("messaging-broadcaster-in-main"); const messageToChannelInjectable = getInjectable({ id: "message-to-channel", @@ -8,8 +13,8 @@ const messageToChannelInjectable = getInjectable({ instantiate: (di) => { const sendToIpc = di.inject(sendToIpcInjectable); - return ((channel, message) => { - sendToIpc(channel.id, message); + return ((targetChannel, message) => { + sendToIpc(broadcasterChannel.id, { targetChannelId: targetChannel.id, message }); }) as SendMessageToChannel; }, diff --git a/packages/technical-features/messaging/electron/renderer/src/sending-of-messages/message-to-channel.test.ts b/packages/technical-features/messaging/electron/renderer/src/sending-of-messages/message-to-channel.test.ts index 07989cf16e..0edae34b76 100644 --- a/packages/technical-features/messaging/electron/renderer/src/sending-of-messages/message-to-channel.test.ts +++ b/packages/technical-features/messaging/electron/renderer/src/sending-of-messages/message-to-channel.test.ts @@ -31,8 +31,11 @@ describe("message-from-channel", () => { sendMessageToChannel(someChannel, 42); }); - it("sends to ipcRenderer of Electron", () => { - expect(sendToIpcMock).toHaveBeenCalledWith("some-channel-id", 42); + it("sends message to broadcasting channel using IPC", () => { + expect(sendToIpcMock).toHaveBeenCalledWith("messaging-broadcaster-in-main", { + targetChannelId: "some-channel-id", + message: 42, + }); }); }); });