From b0d28c8a2e7f4b8b1832dbd01f15fd4dfa61ad23 Mon Sep 17 00:00:00 2001 From: rob Date: Tue, 23 Apr 2024 14:11:39 -0400 Subject: [PATCH] Desktop Notifications When user enters a chat room, app prompts for notification permissions. Then, if app doesn't have focus and a message arrives, a desktop summary notification is shown. --- client/js/chat-client.js | 37 ++++++++++++++++++++++++++++++++++--- 1 file changed, 34 insertions(+), 3 deletions(-) diff --git a/client/js/chat-client.js b/client/js/chat-client.js index 788572d..75a240d 100644 --- a/client/js/chat-client.js +++ b/client/js/chat-client.js @@ -32,6 +32,8 @@ export class ChatApp extends DtpApp { this.loadSettings(); this.log.info('constructor', 'DTP app client online'); + this.notificationPermission = 'default'; + this.chat = { form: document.querySelector('#chat-input-form'), messageList: document.querySelector('#chat-message-list'), @@ -63,9 +65,22 @@ export class ChatApp extends DtpApp { window.addEventListener('dtp-load', this.onDtpLoad.bind(this)); window.addEventListener('unload', this.onDtpUnload.bind(this)); + window.addEventListener('focus', this.onWindowFocus.bind(this)); + window.addEventListener('blur', this.onWindowBlur.bind(this)); + this.updateTimestamps(); } + async onWindowFocus (event) { + this.log.debug('onWindowFocus', 'window has received focus', { event }); + this.haveFocus = true; + } + + async onWindowBlur (event) { + this.log.debug('onWindowBlur', 'window has lost focus', { event }); + this.haveFocus = false; + } + async startAudio ( ) { this.log.info('startAudio', 'starting ChatAudio'); this.audio = new ChatAudio(); @@ -294,18 +309,28 @@ export class ChatApp extends DtpApp { }); await this.processResponse(response); } catch (error) { - this.log.error('sendEmojiReact', 'failed to send emoji react', { error }); + this.log.error('toggleMessageReaction', 'failed to send emoji react', { error }); UIkit.modal.alert(`Failed to send emoji react: ${error.message}`); } } async onDtpLoad ( ) { - this.log.info('dtp-load event received. Connecting to platform.'); + this.log.info('onDtpLoad', 'dtp-load event received. Connecting to platform.'); + await this.connect({ mode: 'User', onSocketConnect: this.onChatSocketConnect.bind(this), onSocketDisconnect: this.onChatSocketDisconnect.bind(this), }); + + if (this.chat.messageList) { + try { + this.notificationPermission = await Notification.requestPermission(); + this.log.debug('onDtpLoad', 'Notification permission status', { permission: this.notificationPermission }); + } catch (error) { + this.log.error('onDtpLoad', 'failed to request Notification permission', { error }); + } + } } async onDtpUnload ( ) { @@ -332,10 +357,16 @@ export class ChatApp extends DtpApp { async onChatMessage (message) { const isAtBottom = this.chat.isAtBottom; - this.log.info('onChatMessage', 'chat message received', { message }); this.chat.messageList.insertAdjacentHTML('beforeend', message.html); this.audio.playSound(ChatApp.SFX_CHAT_MESSAGE); this.scrollChatToBottom(isAtBottom); + + if (!this.haveFocus && (this.notificationPermission === 'granted')) { + const chatMessage = message.message; + new Notification(chatMessage.channel.name, { + body: `Message received from ${chatMessage.author.displayName || chatMessage.author.username}`, + }); + } } async onChatControl (message) {