feat: add notification store, add label
continuous-integration/drone/pr Build is passing Details

This commit is contained in:
Dominik Pschenitschni 2022-11-14 15:31:58 +01:00
parent 10e044962e
commit 9fe0b5b766
Signed by: dpschen
GPG Key ID: B257AC0149F43A77
4 changed files with 101 additions and 65 deletions

View File

@ -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>

View File

@ -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.",

View File

@ -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) {

View File

@ -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))
}