Skip to content

Commit

Permalink
feat(notification): Notify user for new message through browser API
Browse files Browse the repository at this point in the history
Check if notifications API is  supported
Add a switch allowing the user to received notifications when new message is received
Close #59
  • Loading branch information
nioc committed Feb 5, 2023
1 parent df9f92e commit 55e4900
Show file tree
Hide file tree
Showing 5 changed files with 222 additions and 9 deletions.
150 changes: 142 additions & 8 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "xmpp-web",
"version": "0.9.8",
"version": "0.9.9",
"private": true,
"description": "Lightweight web chat client for XMPP server",
"homepage": "https://github.com/nioc/xmpp-web",
Expand Down Expand Up @@ -28,6 +28,7 @@
"@creativebulma/bulma-divider": "^1.1.0",
"@oruga-ui/oruga-next": "^0.5.4",
"@oruga-ui/theme-bulma": "^0.2.6",
"@vueuse/core": "^9.12.0",
"@xmpp/client": "^0.13.1",
"@xmpp/debug": "^0.13.0",
"@xmpp/error": "^0.13.1",
Expand Down
48 changes: 48 additions & 0 deletions src/components/NotificationsSwitch.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
<template>
<div v-if="isNotificationsSupported">
<o-switch v-model="hasNotificationsEnabledSwitch" title="Allow the browser to send you notifications when you miss messages">Notifications</o-switch>
</div>
</template>

<script>
import { mapState, mapActions } from 'pinia'
import { useStore } from '@/store'
import { useWebNotification } from '@vueuse/core'
const lsNotificationKey = 'hasNotificationsEnabled'
export default {
name: 'NotificationsSwitch',
setup() {
const { isSupported } = useWebNotification()
return { isNotificationsSupported: isSupported }
},
computed: {
...mapState(useStore, [
'hasNotificationsEnabled',
]),
hasNotificationsEnabledSwitch: {
get() {
return this.hasNotificationsEnabled
},
set(hasNotificationsEnabled) {
if (hasNotificationsEnabled) {
localStorage.setItem(lsNotificationKey, hasNotificationsEnabled)
} else {
localStorage.removeItem(lsNotificationKey)
}
this.setNotificationStatus(hasNotificationsEnabled)
},
},
},
mounted () {
const hasNotificationsEnabled = localStorage.getItem(lsNotificationKey)
if (hasNotificationsEnabled) {
this.setNotificationStatus(hasNotificationsEnabled)
}
},
methods: {
...mapActions(useStore, ['setNotificationStatus']),
},
}
</script>
3 changes: 3 additions & 0 deletions src/components/PresenceController.vue
Original file line number Diff line number Diff line change
Expand Up @@ -11,20 +11,23 @@
<div :class="[isNavbarItem ? 'navbar-item' : 'dropdown-item']">
<o-switch v-model="isAutoPresence" title="You will be seen away when the browser is not active" @change="setAutoPresence">Set away when inactive</o-switch>
</div>
<notifications-switch :class="[isNavbarItem ? 'navbar-item' : 'dropdown-item']" />
</div>
</div>
</div>
</template>

<script>
import presence from '../components/Presence.vue'
import NotificationsSwitch from '../components/NotificationsSwitch.vue'
import { mapState } from 'pinia'
import { useStore } from '@/store'
export default {
name: 'PresenceController',
components: {
presence,
NotificationsSwitch,
},
props: {
isNavbarItem: {
Expand Down
27 changes: 27 additions & 0 deletions src/store/index.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
import { defineStore } from 'pinia'
import { useWebNotification, useDocumentVisibility } from '@vueuse/core'

let showNotification = null

const getDefaultState = () => {
return {
Expand All @@ -11,6 +14,7 @@ const getDefaultState = () => {
httpFileUploadMaxSize: null,
isOnline: false,
presence: 'chat',
hasNotificationsEnabled: false,
}
}

Expand Down Expand Up @@ -227,6 +231,16 @@ export const useStore = defineStore('main', {
}
return copy
}
if (this.hasNotificationsEnabled) {
const visibility = useDocumentVisibility()
if (visibility.value === 'hidden' && showNotification !== null) {
showNotification({
body: 'You have received new message',
renotify: false,
tag: 'unread',
})
}
}
if (payload.message.from.bare === this.activeChat) {
// message is in the displayed chat, do not increment counter
return
Expand Down Expand Up @@ -301,6 +315,19 @@ export const useStore = defineStore('main', {
}
},

setNotificationStatus (hasNotificationsEnabled) {
this.hasNotificationsEnabled = hasNotificationsEnabled
if (hasNotificationsEnabled && showNotification === null) {
// trigger Notifications API for requesting user permission (only one time) and intialize showNotification function
({ show: showNotification } = useWebNotification({
title: window.config.name,
icon: '/img/icons/android-chrome-192x192.png',
dir: 'auto',
lang: 'en',
}))
}
},

// clear state
clear () {
const defaultState = getDefaultState()
Expand Down

0 comments on commit 55e4900

Please sign in to comment.