From f5d5a3df5946aa5810976642d6632ced358ba22a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joost=20Z=C3=B6llner?= Date: Fri, 21 Sep 2018 11:11:06 +0200 Subject: [PATCH] feat(service-worker): close notifications and focus window on click (#25860) - Serialize notification object before using postMessage - Close notification on click - Focus browser if it is not already focused on click PR Close #25860 --- packages/service-worker/worker/src/driver.ts | 31 ++++++++++++++++--- .../service-worker/worker/testing/scope.ts | 7 +++-- 2 files changed, 31 insertions(+), 7 deletions(-) diff --git a/packages/service-worker/worker/src/driver.ts b/packages/service-worker/worker/src/driver.ts index dd27462024..4837f1244a 100644 --- a/packages/service-worker/worker/src/driver.ts +++ b/packages/service-worker/worker/src/driver.ts @@ -33,6 +33,10 @@ const NOTIFICATION_OPTION_NAMES = [ 'actions', 'badge', 'body', 'dir', 'icon', 'lang', 'renotify', 'requireInteraction', 'tag', 'vibrate', 'data' ]; +const NOTIFICATION_CLICK_OPTION_NAMES = [ + 'actions', 'badge', 'title', 'body', 'dir', 'icon', 'lang', 'renotify', 'requireInteraction', + 'tag', 'vibrate', 'data' +]; interface LatestEntry { latest: string; @@ -276,7 +280,7 @@ export class Driver implements Debuggable, UpdateSource { msg.waitUntil(this.handlePush(msg.data.json())); } - private onClick(event: NotificationClickEvent): void { + private onClick(event: NotificationEvent): void { // Handle the click event and keep the SW alive until it's handled. event.waitUntil(this.handleClick(event.notification, event.action)); } @@ -305,10 +309,17 @@ export class Driver implements Debuggable, UpdateSource { await this.scope.registration.showNotification(desc['title'] !, options); } - private async handleClick(notification: Notification, action?: string): Promise { - await this.broadcast({ + private async handleClick(notification: any, action?: string): Promise { + (notification as Notification).close(); + + const desc = notification as{[key: string]: string | undefined}; + let options: {[key: string]: string | undefined} = {}; + NOTIFICATION_CLICK_OPTION_NAMES.filter(name => desc.hasOwnProperty(name)) + .forEach(name => options[name] = desc[name]); + + await this.broadcastAndFocus({ type: 'NOTIFICATION_CLICK', - data: {action, notification}, + data: {action, notification: options}, }); } @@ -996,6 +1007,18 @@ export class Driver implements Debuggable, UpdateSource { }, Promise.resolve()); } + async broadcastAndFocus(msg: Object): Promise { + const clients = await this.scope.clients.matchAll(); + clients.forEach((client: any) => { + if ('focus' in client) { + if (!client.focused) { + client.focus(); + } + } + client.postMessage(msg); + }); + } + async broadcast(msg: Object): Promise { const clients = await this.scope.clients.matchAll(); clients.forEach(client => { client.postMessage(msg); }); diff --git a/packages/service-worker/worker/testing/scope.ts b/packages/service-worker/worker/testing/scope.ts index 88cd7d1f49..4ecf148a69 100644 --- a/packages/service-worker/worker/testing/scope.ts +++ b/packages/service-worker/worker/testing/scope.ts @@ -232,7 +232,7 @@ export class SwTestHarness implements ServiceWorkerGlobalScope, Adapter, Context if (!this.eventHandlers.has('notificationclick')) { throw new Error('No notificationclick handler registered'); } - const event = new MockNotificationClickEvent(notification, action); + const event = new MockNotificationEvent(notification, action); this.eventHandlers.get('notificationclick') !.call(this, event); return event.ready; } @@ -349,8 +349,9 @@ class MockPushEvent extends MockExtendableEvent { json: () => this._data, }; } -class MockNotificationClickEvent extends MockExtendableEvent { - constructor(readonly notification: Object, readonly action?: string) { super(); } +class MockNotificationEvent extends MockExtendableEvent { + constructor(private _notification: any, readonly action?: string) { super(); } + readonly notification = {...this._notification, close: () => { return; }}; } class MockInstallEvent extends MockExtendableEvent {}