feat: add notification store, add label
continuous-integration/drone/pr Build is passing
Details
continuous-integration/drone/pr Build is passing
Details
This commit is contained in:
parent
10e044962e
commit
9fe0b5b766
|
@ -1,27 +1,25 @@
|
|||
<template>
|
||||
<div class="notifications">
|
||||
<!-- FIXME: add label -->
|
||||
<slot :togglePopup="togglePopup" :hasUnreadNotifications="hasUnreadNotifications">
|
||||
<NavbarTriggerButton
|
||||
:pressed="showNotifications"
|
||||
ref="toggleButton"
|
||||
@click="togglePopup"
|
||||
>
|
||||
<span v-if="hasUnreadNotifications" class="unread-indicator" />
|
||||
<icon icon="bell"/>
|
||||
</NavbarTriggerButton>
|
||||
</slot>
|
||||
<NavbarTriggerButton
|
||||
:pressed="modalIsOpen"
|
||||
ref="toggleButton"
|
||||
:aria-label="modalIsOpen ? $t('notification.hideNotifications') : $t('notification.showNotifications')"
|
||||
@click="togglePopup"
|
||||
>
|
||||
<span v-if="hasUnreadNotifications" class="unread-indicator" />
|
||||
<icon icon="bell"/>
|
||||
</NavbarTriggerButton>
|
||||
|
||||
<!-- FIXME: create dedicated dropdown menu -->
|
||||
<transition name="fade">
|
||||
<div class="notifications-list" v-if="showNotifications" ref="popup">
|
||||
<div class="notifications-list" v-if="modalIsOpen" ref="popup">
|
||||
<h3 class="head">{{ $t('notification.title') }}</h3>
|
||||
<NotificationItem
|
||||
v-for="(n, index) in notifications"
|
||||
v-for="n in notifications"
|
||||
:key="n.id"
|
||||
class="notification-item"
|
||||
:notification="n"
|
||||
@markNotificationAsRead="markNotificationAsRead(index, n)"
|
||||
@markNotificationAsRead="markNotificationAsRead(n)"
|
||||
/>
|
||||
<p class="nothing" v-if="notifications.length === 0">
|
||||
{{ $t('notification.none') }}<br/>
|
||||
|
@ -35,75 +33,42 @@
|
|||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import {computed, onMounted, onUnmounted, ref} from 'vue'
|
||||
import {onClickOutside} from '@vueuse/core'
|
||||
import {ref} from 'vue'
|
||||
import {onClickOutside, tryOnUnmounted} from '@vueuse/core'
|
||||
|
||||
import type {INotification} from '@/modelTypes/INotification'
|
||||
|
||||
import NotificationService from '@/services/notification'
|
||||
import NavbarTriggerButton from '@/components/home/NavbarTriggerButton.vue'
|
||||
import NotificationItem from '@/components/notifications/NotificationItem.vue'
|
||||
import {useNotificationStore} from '@/stores/notifications'
|
||||
|
||||
import {findIndexById} from '@/helpers/utils'
|
||||
|
||||
const NOTIFICATIONS_PULL_INTERVAL = 10000
|
||||
|
||||
const allNotifications = ref<INotification[]>([])
|
||||
const showNotifications = ref(false)
|
||||
const modalIsOpen = ref(false)
|
||||
const popup = ref(null)
|
||||
const toggleButton = ref(null)
|
||||
|
||||
function togglePopup() {
|
||||
showNotifications.value = !showNotifications.value
|
||||
modalIsOpen.value = !modalIsOpen.value
|
||||
}
|
||||
|
||||
onClickOutside(
|
||||
popup,
|
||||
() => {
|
||||
if (!showNotifications.value) {
|
||||
if (!modalIsOpen.value) {
|
||||
return
|
||||
}
|
||||
showNotifications.value = false
|
||||
modalIsOpen.value = false
|
||||
},
|
||||
{ ignore: [toggleButton]},
|
||||
)
|
||||
|
||||
const notifications = computed(() => {
|
||||
return allNotifications.value ? allNotifications.value.filter(n => n.name !== '') : []
|
||||
})
|
||||
const unreadNotifications = computed(() => {
|
||||
return notifications.value.filter(n => n.readAt === null).length
|
||||
})
|
||||
const {
|
||||
notifications,
|
||||
hasUnreadNotifications,
|
||||
|
||||
const hasUnreadNotifications = computed(() => unreadNotifications.value > 0)
|
||||
startNotificationPulling,
|
||||
markNotificationAsRead,
|
||||
} = useNotificationStore()
|
||||
|
||||
let interval: ReturnType<typeof setInterval>
|
||||
onMounted(() => {
|
||||
interval = setInterval(loadNotifications, NOTIFICATIONS_PULL_INTERVAL)
|
||||
})
|
||||
|
||||
onUnmounted(() => clearInterval(interval))
|
||||
|
||||
const notificationService = new NotificationService()
|
||||
|
||||
loadNotifications()
|
||||
async function loadNotifications() {
|
||||
allNotifications.value = await notificationService.getAll()
|
||||
}
|
||||
|
||||
async function markNotificationAsRead(notification: INotification) {
|
||||
const index = findIndexById(allNotifications.value, notification.id)
|
||||
if (index === -1) {
|
||||
return
|
||||
}
|
||||
|
||||
allNotifications.value[index] = {
|
||||
...notification,
|
||||
read: true,
|
||||
}
|
||||
|
||||
await notificationService.update(allNotifications.value[index])
|
||||
}
|
||||
const stopNotificationPulling = startNotificationPulling()
|
||||
tryOnUnmounted(stopNotificationPulling)
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
|
|
@ -943,6 +943,8 @@
|
|||
"contact": "contact us"
|
||||
},
|
||||
"notification": {
|
||||
"showNotifications": "show Notifications",
|
||||
"hideNotifications": "hide Notifications",
|
||||
"title": "Notifications",
|
||||
"none": "You don't have any notifications. Have a nice day!",
|
||||
"explainer": "Notifications will appear here when actions on namespaces, lists or tasks you subscribed to happen.",
|
||||
|
|
|
@ -1,10 +1,13 @@
|
|||
import AbstractService from '@/services/abstractService'
|
||||
import {formatISO} from 'date-fns'
|
||||
import NotificationModel from '@/models/notification'
|
||||
import {NOTIFICATION_NAMES, type INotification} from '@/modelTypes/INotification'
|
||||
|
||||
import type {IUser} from '@/modelTypes/IUser'
|
||||
import {NOTIFICATION_NAMES, type INotification} from '@/modelTypes/INotification'
|
||||
|
||||
import AbstractService from '@/services/abstractService'
|
||||
import NotificationModel from '@/models/notification'
|
||||
import {getTextIdentifier} from '@/models/task'
|
||||
import {getDisplayName} from '@/models/user'
|
||||
|
||||
import {i18n} from '@/i18n'
|
||||
|
||||
export function getNotificationTitle(notificationItem: INotification, user: IUser | null) {
|
||||
|
|
|
@ -0,0 +1,66 @@
|
|||
import {computed, ref} from 'vue'
|
||||
import type {INotification} from '@/modelTypes/INotification'
|
||||
import NotificationService from '@/services/notification'
|
||||
import {acceptHMRUpdate, defineStore} from 'pinia'
|
||||
import {findIndexById} from '@/helpers/utils'
|
||||
|
||||
const NOTIFICATIONS_PULL_INTERVAL = 10000
|
||||
|
||||
export const useNotificationStore = defineStore('notification', () => {
|
||||
const allNotifications = ref<INotification[]>([])
|
||||
|
||||
const notifications = computed(() => {
|
||||
return allNotifications.value ? allNotifications.value.filter(n => n.name !== '') : []
|
||||
})
|
||||
const unreadNotifications = computed(() => {
|
||||
return notifications.value.filter(n => n.readAt === null).length
|
||||
})
|
||||
const hasUnreadNotifications = computed(() => unreadNotifications.value > 0)
|
||||
|
||||
let interval: ReturnType<typeof setInterval>
|
||||
|
||||
const notificationService = new NotificationService()
|
||||
|
||||
async function loadNotifications() {
|
||||
allNotifications.value = await notificationService.getAll()
|
||||
}
|
||||
|
||||
function startNotificationPulling() {
|
||||
loadNotifications()
|
||||
interval = setInterval(loadNotifications, NOTIFICATIONS_PULL_INTERVAL)
|
||||
return stopNotificationPulling
|
||||
}
|
||||
|
||||
function stopNotificationPulling() {
|
||||
clearInterval(interval)
|
||||
}
|
||||
|
||||
async function markNotificationAsRead(notificationItem: INotification) {
|
||||
const index = findIndexById(allNotifications.value, notificationItem.id)
|
||||
if (index === -1) {
|
||||
return
|
||||
}
|
||||
|
||||
allNotifications.value[index] = {
|
||||
...notificationItem,
|
||||
read: true,
|
||||
}
|
||||
|
||||
await notificationService.update(allNotifications.value[index])
|
||||
}
|
||||
|
||||
return {
|
||||
notifications,
|
||||
unreadNotifications,
|
||||
hasUnreadNotifications,
|
||||
|
||||
startNotificationPulling,
|
||||
stopNotificationPulling,
|
||||
markNotificationAsRead,
|
||||
}
|
||||
})
|
||||
|
||||
// support hot reloading
|
||||
if (import.meta.hot) {
|
||||
import.meta.hot.accept(acceptHMRUpdate(useNotificationStore, import.meta.hot))
|
||||
}
|
Reference in New Issue