diff --git a/desktop/flipper-plugin/src/__tests__/TestPlugin.tsx b/desktop/flipper-plugin/src/__tests__/TestPlugin.tsx index 433ac5fed..8c0c557b8 100644 --- a/desktop/flipper-plugin/src/__tests__/TestPlugin.tsx +++ b/desktop/flipper-plugin/src/__tests__/TestPlugin.tsx @@ -36,6 +36,7 @@ export function plugin(client: PluginClient) { persist: 'counter', }, ); + const unhandledMessages = createState([]); client.onConnect(connectStub); client.onDisconnect(disconnectStub); @@ -47,6 +48,11 @@ export function plugin(client: PluginClient) { draft.count += delta; }); }); + client.onUnhandledMessage((event, params) => { + unhandledMessages.update((draft) => { + draft.push({event, params}); + }); + }); function _unused_JustTypeChecks() { // @ts-expect-error Argument of type '"bla"' is not assignable @@ -77,6 +83,7 @@ export function plugin(client: PluginClient) { disconnectStub, getCurrentState, state, + unhandledMessages, appId: client.appId, appName: client.appName, }; diff --git a/desktop/flipper-plugin/src/__tests__/test-utils.node.tsx b/desktop/flipper-plugin/src/__tests__/test-utils.node.tsx index 0ad83cb8b..cd222bc06 100644 --- a/desktop/flipper-plugin/src/__tests__/test-utils.node.tsx +++ b/desktop/flipper-plugin/src/__tests__/test-utils.node.tsx @@ -180,6 +180,15 @@ test('a plugin can receive messages', async () => { }, } `); + + expect(instance.unhandledMessages.get().length).toBe(0); + sendEvent('unhandled' as any, {hello: 'world'}); + expect(instance.unhandledMessages.get()).toEqual([ + { + event: 'unhandled', + params: {hello: 'world'}, + }, + ]); }); test('plugins support non-serializable state', async () => { diff --git a/desktop/flipper-plugin/src/plugin/Plugin.tsx b/desktop/flipper-plugin/src/plugin/Plugin.tsx index 3fdf08400..2b3d1ac97 100644 --- a/desktop/flipper-plugin/src/plugin/Plugin.tsx +++ b/desktop/flipper-plugin/src/plugin/Plugin.tsx @@ -63,6 +63,13 @@ export interface PluginClient< callback: (params: Events[Event]) => void, ): void; + /** + * Subscribe to all messages arriving from the devices not handled by another listener. + * + * This handler is untyped, and onMessage should be favored over using onUnhandledMessage if the event name is known upfront. + */ + onUnhandledMessage(callback: (event: string, params: any) => void): void; + /** * Send a message to the connected client */ @@ -156,6 +163,9 @@ export class SandyPluginInstance extends BasePluginInstance { onMessage: (event, callback) => { this.events.on('event-' + event, callback); }, + onUnhandledMessage: (callback) => { + this.events.on('unhandled-event', callback); + }, supportsMethod: async (method) => { this.assertConnected(); return await realClient.supportsMethod( @@ -212,7 +222,11 @@ export class SandyPluginInstance extends BasePluginInstance { receiveMessages(messages: Message[]) { messages.forEach((message) => { - this.events.emit('event-' + message.method, message.params); + if (this.events.listenerCount('event-' + message.method) > 0) { + this.events.emit('event-' + message.method, message.params); + } else { + this.events.emit('unhandled-event', message.method, message.params); + } }); }