|
|
@@ -24,6 +24,7 @@
|
|
|
<v-menu
|
|
|
:activator="btn"
|
|
|
v-model="isOpen"
|
|
|
+ location="bottom left"
|
|
|
>
|
|
|
<v-card max-width="400">
|
|
|
<v-card-title class="bg-neutral text-body-2 font-weight-bold">
|
|
|
@@ -31,13 +32,12 @@
|
|
|
</v-card-title>
|
|
|
|
|
|
<v-card-text class="ma-0 pa-0 header-menu">
|
|
|
- <v-list density="compact" :subheader="true">
|
|
|
+ <v-list density="compact" :subheader="true" class="pa-0">
|
|
|
<v-list-item
|
|
|
v-for="(notification, index) in notifications"
|
|
|
:key="index"
|
|
|
:class="`${notification.notificationUsers.length === 0 ? 'unread' : ''}`"
|
|
|
>
|
|
|
-
|
|
|
<v-list-item-title
|
|
|
class="list_item mt-2 mb-2"
|
|
|
v-text="getMessage(notification)"
|
|
|
@@ -56,22 +56,23 @@
|
|
|
|
|
|
<v-divider></v-divider>
|
|
|
|
|
|
- </v-list>
|
|
|
+ <!--suppress VueUnrecognizedDirective -->
|
|
|
+ <span v-intersect="onLastNotificationIntersect" />
|
|
|
|
|
|
- <!--suppress VueUnrecognizedDirective -->
|
|
|
- <v-card v-intersect="update"></v-card>
|
|
|
+ <v-row
|
|
|
+ v-if="pending"
|
|
|
+ class="fill-height mt-3 mb-3"
|
|
|
+ align="center"
|
|
|
+ justify="center"
|
|
|
+ >
|
|
|
+ <v-progress-circular
|
|
|
+ indeterminate
|
|
|
+ color="neutral"
|
|
|
+ />
|
|
|
+ </v-row>
|
|
|
+
|
|
|
+ </v-list>
|
|
|
|
|
|
- <v-row
|
|
|
- v-if="pending"
|
|
|
- class="fill-height mt-3 mb-3"
|
|
|
- align="center"
|
|
|
- justify="center"
|
|
|
- >
|
|
|
- <v-progress-circular
|
|
|
- indeterminate
|
|
|
- color="neutral"
|
|
|
- />
|
|
|
- </v-row>
|
|
|
</v-card-text>
|
|
|
|
|
|
<v-card-actions class="ma-0 pa-0">
|
|
|
@@ -97,12 +98,13 @@ import {NOTIFICATION_TYPE} from "~/types/enum/enums";
|
|
|
import Notification from "~/models/Core/Notification";
|
|
|
import NotificationUsers from "~/models/Core/NotificationUsers";
|
|
|
import {useAccessProfileStore} from "~/stores/accessProfile";
|
|
|
-import {ComputedRef, Ref, ref} from "@vue/reactivity";
|
|
|
+import {computed, ComputedRef, Ref, ref} from "@vue/reactivity";
|
|
|
import {useEntityFetch} from "~/composables/data/useEntityFetch";
|
|
|
-import {Pagination} from "~/types/data";
|
|
|
+import {AnyJson, Pagination} from "~/types/data";
|
|
|
import {useEntityManager} from "~/composables/data/useEntityManager";
|
|
|
import UrlUtils from "~/services/utils/urlUtils";
|
|
|
-import ArrayUtils from "~/services/utils/arrayUtils";
|
|
|
+import {useRepo} from "pinia-orm";
|
|
|
+import NotificationRepository from "~/stores/repositories/NotificationRepository";
|
|
|
|
|
|
const accessProfileStore = useAccessProfileStore()
|
|
|
|
|
|
@@ -117,15 +119,27 @@ const btn = ref(null)
|
|
|
|
|
|
const { em } = useEntityManager()
|
|
|
const { fetchCollection } = useEntityFetch()
|
|
|
+const notificationRepo = useRepo(NotificationRepository)
|
|
|
+
|
|
|
+const query: ComputedRef<AnyJson> = computed(() => {
|
|
|
+ return { 'page': page.value }
|
|
|
+})
|
|
|
+
|
|
|
|
|
|
-let { data: collection, pending, refresh } = await fetchCollection(Notification)
|
|
|
+let { data: collection, pending, refresh } = await fetchCollection(Notification, null, query)
|
|
|
+
|
|
|
+/**
|
|
|
+ * On récupère les Notifications via le store (sans ça, les mises à jour SSE ne seront pas prises en compte)
|
|
|
+ */
|
|
|
+const notifications: ComputedRef<Array<Notification>> = computed(() => {
|
|
|
+ return notificationRepo.getNotifications()
|
|
|
+})
|
|
|
|
|
|
/**
|
|
|
- * On récupère les Notifications via le store
|
|
|
+ * Retourne les notifications non lues
|
|
|
*/
|
|
|
-const notifications: ComputedRef = computed(() => {
|
|
|
- const items = collection.value !== null ? collection.value.items : []
|
|
|
- return ArrayUtils.sortObjectsByProp(items, 'id')
|
|
|
+const unreadNotification: ComputedRef<Array<Notification>> = computed(() => {
|
|
|
+ return notificationRepo.getUnreadNotifications()
|
|
|
})
|
|
|
|
|
|
/**
|
|
|
@@ -135,17 +149,20 @@ const pagination: ComputedRef<Pagination> = computed(() => {
|
|
|
return collection.value !== null ? collection.value.pagination : {}
|
|
|
})
|
|
|
|
|
|
+const notificationUrl = UrlUtils.join(runtimeConfig.baseUrlAdminLegacy, 'notifications/list/')
|
|
|
+
|
|
|
/**
|
|
|
- * On calcule le nombre de notifications non lues
|
|
|
+ * L'utilisateur a fait défiler le menu jusqu'à la dernière notification affichée
|
|
|
+ * @param isIntersecting
|
|
|
*/
|
|
|
-const unreadNotification: ComputedRef<Array<Notification>> = computed(() => {
|
|
|
- return notifications.value.filter((notification: Notification) => {
|
|
|
- return notification.notificationUsers?.length === 0
|
|
|
- })
|
|
|
-})
|
|
|
+const onLastNotificationIntersect = (isIntersecting: boolean) => {
|
|
|
+ if (isIntersecting) {
|
|
|
+ update()
|
|
|
+ }
|
|
|
+}
|
|
|
|
|
|
/**
|
|
|
- * Lorsque l'utilisateur scroll on regarde la nextPage a charger et on le fait que si le pending du fetch est false
|
|
|
+ * Lorsque l'utilisateur scroll on regarde la nextPage à charger et on le fait que si le pending du fetch est false
|
|
|
* (si on a fini de télécharger les éléments précédents)
|
|
|
*/
|
|
|
const update = async () => {
|
|
|
@@ -191,10 +208,10 @@ const getMessage = (notification: Notification) => {
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
- * Dès l'ouverture du menu, on indique que les notifications non lues, le sont.
|
|
|
+ * Dès la fermeture du menu, on indique que les notifications non lues, le sont.
|
|
|
*/
|
|
|
const unwatch = watch(isOpen, (newValue, oldValue) => {
|
|
|
- if (newValue){
|
|
|
+ if (!newValue){
|
|
|
markNotificationsAsRead()
|
|
|
}
|
|
|
})
|
|
|
@@ -203,30 +220,29 @@ onUnmounted(() => {
|
|
|
})
|
|
|
|
|
|
/**
|
|
|
- * Créer une nouvelle notification users coté back.
|
|
|
- * @param notification
|
|
|
- * @param accessId
|
|
|
+ * Marque une notification comme lue
|
|
|
*/
|
|
|
-const createNewNotificationUsers = (notification: Notification, accessId: number) => {
|
|
|
+const markNotificationAsRead = (notification: Notification) => {
|
|
|
+ if (accessProfileStore.id === null) {
|
|
|
+ throw new Error('Current access id is null')
|
|
|
+ }
|
|
|
const notificationUsers = em.newInstance(NotificationUsers, {
|
|
|
- access:`/api/accesses/${accessId}`,
|
|
|
+ access:`/api/accesses/${accessProfileStore.id}`,
|
|
|
notification:`/api/notifications/${notification.id}`,
|
|
|
isRead: true
|
|
|
})
|
|
|
|
|
|
em.persist(NotificationUsers, notificationUsers)
|
|
|
notification.notificationUsers = ['read']
|
|
|
+ em.save(Notification, notification)
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
- * Marque les notifications non lues comme lues
|
|
|
+ * Marque toutes les notifications non lues comme lues
|
|
|
*/
|
|
|
const markNotificationsAsRead = () => {
|
|
|
unreadNotification.value.map((notification: Notification) => {
|
|
|
- if (accessProfileStore.id === null) {
|
|
|
- throw new Error('Current access id is null')
|
|
|
- }
|
|
|
- createNewNotificationUsers(notification, accessProfileStore.id)
|
|
|
+ markNotificationAsRead(notification)
|
|
|
})
|
|
|
}
|
|
|
|
|
|
@@ -248,7 +264,6 @@ const download = (link: string) => {
|
|
|
window.open(UrlUtils.join(runtimeConfig.baseUrlLegacy, url_parts.join('')));
|
|
|
}
|
|
|
|
|
|
-const notificationUrl = UrlUtils.join(runtimeConfig.baseUrlAdminLegacy, 'notifications/list/')
|
|
|
|
|
|
</script>
|
|
|
|
|
|
@@ -257,6 +272,6 @@ const notificationUrl = UrlUtils.join(runtimeConfig.baseUrlAdminLegacy, 'notific
|
|
|
white-space: normal;
|
|
|
}
|
|
|
.unread{
|
|
|
- background: rgb(var(--v-theme-global-neutral-soft, white));
|
|
|
+ background: rgb(var(--v-theme-neutral-soft, white));
|
|
|
}
|
|
|
</style>
|