Added sockets for notifications
This commit is contained in:
@@ -0,0 +1,30 @@
|
||||
<script setup lang="ts">
|
||||
import { onMounted, onUnmounted } from 'vue';
|
||||
import { useNotificationStore } from '@/stores/notificationStore';
|
||||
import { Notification } from '@/Types/types';
|
||||
|
||||
const props = defineProps<{
|
||||
userId: number;
|
||||
}>();
|
||||
|
||||
interface NotificationPayload {
|
||||
notification: Notification;
|
||||
}
|
||||
|
||||
const store = useNotificationStore();
|
||||
let channel: ReturnType<typeof window.Echo.private> | null = null;
|
||||
|
||||
onMounted(() => {
|
||||
channel = window.Echo.private(`App.Models.User.${props.userId}`)
|
||||
.listen('.notification.created', (e: NotificationPayload) => {
|
||||
store.push(e.notification);
|
||||
});
|
||||
});
|
||||
|
||||
onUnmounted(() => {
|
||||
channel?.stopListening('.notification.created');
|
||||
window.Echo.leave(`App.Models.User.${props.userId}`);
|
||||
});
|
||||
</script>
|
||||
|
||||
<template></template>
|
||||
@@ -1,19 +1,19 @@
|
||||
<script setup lang="ts">
|
||||
import {ref, watch} from 'vue'
|
||||
import { ref, watch, onMounted } from 'vue'
|
||||
import axios from "axios";
|
||||
import {Notification} from "@/Types/types";
|
||||
import {Link} from "@inertiajs/vue3";
|
||||
import {local} from "laravel-vite-plugin/fonts";
|
||||
import { Notification } from "@/Types/types";
|
||||
import { Link } from "@inertiajs/vue3";
|
||||
import { useNotificationStore } from '@/stores/notificationStore';
|
||||
|
||||
const props = defineProps<{
|
||||
unreadCount: number
|
||||
}>()
|
||||
|
||||
const localUnreadCount = ref(props.unreadCount)
|
||||
watch(() => props.unreadCount, (val) => {
|
||||
localUnreadCount.value = val
|
||||
})
|
||||
const store = useNotificationStore();
|
||||
|
||||
onMounted(() => {
|
||||
store.setInitialUnreadCount(props.unreadCount);
|
||||
});
|
||||
|
||||
const open = ref(false)
|
||||
const notifications = ref<Notification[]>([])
|
||||
@@ -27,15 +27,10 @@ const markAllRead = async (notifications: Notification[]) => {
|
||||
unread.forEach(n => n.read_at = new Date().toISOString())
|
||||
}
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: 'update:unreadCount', value: number): void
|
||||
}>()
|
||||
|
||||
watch(open, async (isOpen) => {
|
||||
if (!isOpen) return
|
||||
|
||||
localUnreadCount.value = 0
|
||||
emit('update:unreadCount', 0)
|
||||
store.unreadCount = 0
|
||||
|
||||
if (notifications.value.length) return
|
||||
|
||||
@@ -50,7 +45,7 @@ watch(open, async (isOpen) => {
|
||||
<template>
|
||||
<div class="notif-wrapper">
|
||||
<v-btn icon variant="text" @click="open = !open" aria-label="Notifications">
|
||||
<v-badge :content="localUnreadCount" :model-value="localUnreadCount > 0" color="primary">
|
||||
<v-badge :content="store.unreadCount" :model-value="store.unreadCount > 0" color="primary">
|
||||
<v-icon>mdi-bell-outline</v-icon>
|
||||
</v-badge>
|
||||
</v-btn>
|
||||
|
||||
@@ -1,54 +1,14 @@
|
||||
<script setup lang="ts">
|
||||
import { ref, watch } from "vue";
|
||||
import { router } from "@inertiajs/vue3";
|
||||
import { Notification } from "@/Types/types";
|
||||
import axios from "axios";
|
||||
import { useNotificationStore } from '@/stores/notificationStore';
|
||||
|
||||
const activeToasts = ref<Notification[]>([])
|
||||
const seenNotificationIds = ref<Set<number>>(new Set())
|
||||
const achievementSound = new Audio('/sounds/seatBelt.wav')
|
||||
|
||||
function handleNewNotifications(notifications: Notification[]) {
|
||||
if (!notifications?.length) return
|
||||
|
||||
const newToasts: Notification[] = []
|
||||
for (const n of notifications) {
|
||||
if (!seenNotificationIds.value.has(n.id)) {
|
||||
seenNotificationIds.value.add(n.id)
|
||||
newToasts.push(n)
|
||||
}
|
||||
}
|
||||
|
||||
if (!newToasts.length) return
|
||||
activeToasts.value.push(...newToasts)
|
||||
achievementSound.play().catch(() => {})
|
||||
}
|
||||
|
||||
async function dismissToast(notification: Notification) {
|
||||
activeToasts.value = activeToasts.value.filter(n => n.id !== notification.id)
|
||||
await axios.patch(`/notifications/${notification.id}/read`)
|
||||
}
|
||||
|
||||
router.on('success', (event) => {
|
||||
handleNewNotifications(event.detail.page.props.achievement_notifications as Notification[] ?? [])
|
||||
})
|
||||
|
||||
// Handle initial page load notifications
|
||||
import { usePage } from "@inertiajs/vue3";
|
||||
import { SharedProps } from "@/Types/types";
|
||||
const page = usePage<SharedProps>().props;
|
||||
watch(
|
||||
() => page.achievement_notifications,
|
||||
handleNewNotifications,
|
||||
{ immediate: true }
|
||||
)
|
||||
const store = useNotificationStore();
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="toast-stack">
|
||||
<TransitionGroup name="toast">
|
||||
<v-card
|
||||
v-for="notification in activeToasts"
|
||||
v-for="notification in store.toasts"
|
||||
:key="notification.id"
|
||||
class="toast-card glass"
|
||||
rounded="lg"
|
||||
@@ -66,7 +26,7 @@ watch(
|
||||
variant="text"
|
||||
size="small"
|
||||
class="ml-auto"
|
||||
@click="dismissToast(notification)"
|
||||
@click="store.dismiss(notification)"
|
||||
/>
|
||||
</v-card-text>
|
||||
</v-card>
|
||||
|
||||
Reference in New Issue
Block a user