From 526292eb273ee7d8fa9a9912ce3b59e9a104c19e Mon Sep 17 00:00:00 2001 From: Joey Pender Date: Tue, 2 Jan 2024 10:15:31 -0600 Subject: [PATCH] fix(web): Implement `retainUntilConsumed` on notifyListeners (#7127) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: jcesarmobile Co-authored-by: Dan Giralté <97970732+giralte-ionic@users.noreply.github.com> --- core/src/tests/web-plugin.spec.ts | 35 ++++++++++++++++++++++++ core/src/web-plugin.ts | 44 ++++++++++++++++++++++++++++--- 2 files changed, 76 insertions(+), 3 deletions(-) diff --git a/core/src/tests/web-plugin.spec.ts b/core/src/tests/web-plugin.spec.ts index d95a5eb20..30bae7c0c 100644 --- a/core/src/tests/web-plugin.spec.ts +++ b/core/src/tests/web-plugin.spec.ts @@ -17,6 +17,24 @@ class MockPlugin extends WebPlugin { }); } + triggerRetained() { + this.notifyListeners( + 'testRetained', + { + value: 'Test Retained Value 1', + }, + true, + ); + + this.notifyListeners( + 'testRetained', + { + value: 'Test Retained Value 2', + }, + true, + ); + } + getListeners() { return this.listeners; } @@ -101,6 +119,23 @@ describe('Web Plugin', () => { handle.remove(); }); + it('Should submit retained events on event registration', async () => { + const lf = jest.fn(); + plugin.triggerRetained(); + + const handle = await plugin.addListener('testRetained', lf); + + expect(lf.mock.calls.length).toEqual(2); + expect(lf.mock.calls[0][0]).toEqual({ + value: 'Test Retained Value 1', + }); + expect(lf.mock.calls[1][0]).toEqual({ + value: 'Test Retained Value 2', + }); + + handle.remove(); + }); + it('Should register and remove window listeners', async () => { const pluginAddWindowListener = jest.spyOn( MockPlugin.prototype as any, diff --git a/core/src/web-plugin.ts b/core/src/web-plugin.ts index 4e54f37f5..46ebe4598 100644 --- a/core/src/web-plugin.ts +++ b/core/src/web-plugin.ts @@ -13,6 +13,7 @@ export class WebPlugin implements Plugin { config?: WebPluginConfig; protected listeners: { [eventName: string]: ListenerCallback[] } = {}; + protected retainedEventArguments: { [eventName: string]: any[] } = {}; protected windowListeners: { [eventName: string]: WindowListenerHandle } = {}; constructor(config?: WebPluginConfig) { @@ -29,9 +30,12 @@ export class WebPlugin implements Plugin { eventName: string, listenerFunc: ListenerCallback, ): Promise { + let firstListener = false; + const listeners = this.listeners[eventName]; if (!listeners) { this.listeners[eventName] = []; + firstListener = true; } this.listeners[eventName].push(listenerFunc); @@ -43,6 +47,10 @@ export class WebPlugin implements Plugin { this.addWindowListener(windowListener); } + if (firstListener) { + this.sendRetainedArgumentsForEvent(eventName); + } + const remove = async () => this.removeListener(eventName, listenerFunc); const p: any = Promise.resolve({ remove }); @@ -58,11 +66,28 @@ export class WebPlugin implements Plugin { this.windowListeners = {}; } - protected notifyListeners(eventName: string, data: any): void { + protected notifyListeners( + eventName: string, + data: any, + retainUntilConsumed?: boolean, + ): void { const listeners = this.listeners[eventName]; - if (listeners) { - listeners.forEach(listener => listener(data)); + if (!listeners) { + if (retainUntilConsumed) { + let args = this.retainedEventArguments[eventName]; + if (!args) { + args = []; + } + + args.push(data); + + this.retainedEventArguments[eventName] = args; + } + + return; } + + listeners.forEach(listener => listener(data)); } protected hasListeners(eventName: string): boolean { @@ -123,6 +148,19 @@ export class WebPlugin implements Plugin { window.removeEventListener(handle.windowEventName, handle.handler); handle.registered = false; } + + private sendRetainedArgumentsForEvent(eventName: string): void { + const args = this.retainedEventArguments[eventName]; + if (!args) { + return; + } + + delete this.retainedEventArguments[eventName]; + + args.forEach(arg => { + this.notifyListeners(eventName, arg); + }); + } } export type ListenerCallback = (err: any, ...args: any[]) => void;