NotificationStore
The NotificationStore is a global Svelte 5 store that manages notification state and the Socket.IO connection. It automatically falls back to REST API polling if WebSocket is unavailable.
Automatic Fallback
Section titled “Automatic Fallback”The store intelligently handles connection issues:
- Socket.IO connection succeeds → Real-time notifications via WebSocket
- Socket.IO connection fails → Automatic polling every 30 seconds
- Socket.IO connection drops → Automatic switch to polling
- Socket.IO reconnects → Automatic switch back to WebSocket
// Automatic initializationconst notificationStore = getNotificationStore();await notificationStore.connect(userId);
// The store automatically:// 1. Tries to connect via Socket.IO// 2. If it fails, starts polling// 3. Loads notifications via REST API// 4. Refreshes every 30 seconds if no WebSocket connectionGetting the Store
Section titled “Getting the Store”import { getNotificationStore } from '$lib/stores/notificationStore.svelte';
const notificationStore = getNotificationStore();Reactive State
Section titled “Reactive State”// Notification listconst notifications = $derived(notificationStore.notifications);
// Unread notification countconst unreadCount = $derived(notificationStore.unreadCount);
// Socket.IO connection statusconst isConnected = $derived(notificationStore.isConnected);
// Current critical notification (if any)const currentCritical = $derived(notificationStore.currentCritical);
// Whether there are unread critical notificationsconst hasUnreadCritical = $derived(notificationStore.hasUnreadCritical);In a Svelte Component
Section titled “In a Svelte Component”<script> import { getNotificationStore } from '$lib/stores/notificationStore.svelte';
const notificationStore = getNotificationStore();
const notifications = $derived(notificationStore.notifications); const unreadCount = $derived(notificationStore.unreadCount);</script>
<div> <p>Unread notifications: {unreadCount}</p>
{#each notifications as notification} <div> <h3>{notification.title}</h3> <p>{notification.message}</p> </div> {/each}</div>API Methods
Section titled “API Methods”connect(userId)
Section titled “connect(userId)”Initialize Socket.IO connection and load notifications.
await notificationStore.connect(123);Happens automatically: hooks.client.ts calls this automatically on login.
disconnect()
Section titled “disconnect()”Disconnect Socket.IO connection.
notificationStore.disconnect();loadNotifications(showToast?)
Section titled “loadNotifications(showToast?)”Load notifications via REST API.
await notificationStore.loadNotifications();
// With toast display (in dev mode)await notificationStore.loadNotifications(true);Happens automatically:
- On initialization
- Every 30 seconds if Socket.IO is unavailable
reload()
Section titled “reload()”Reload notifications with toast display (useful in dev mode).
await notificationStore.reload();markAsRead(notificationId)
Section titled “markAsRead(notificationId)”Mark a notification as read.
await notificationStore.markAsRead(123);Effect:
- Updates local state
- REST API call
- Socket.IO emit (if available)
markAllAsRead()
Section titled “markAllAsRead()”Mark all notifications as read.
await notificationStore.markAllAsRead();deleteNotification(notificationId)
Section titled “deleteNotification(notificationId)”Delete a notification.
await notificationStore.deleteNotification(123);deleteAllNotifications()
Section titled “deleteAllNotifications()”Delete all notifications.
await notificationStore.deleteAllNotifications();sendNotification(payload)
Section titled “sendNotification(payload)”Send a new notification.
await notificationStore.sendNotification({ userId: 123, title: { hu: 'Teszt', en: 'Test' }, message: { hu: 'Teszt üzenet', en: 'Test message' }, type: 'info'});Note: Prefer using the sendNotification function from notificationService.
getAppNotifications(appName)
Section titled “getAppNotifications(appName)”Get notifications for a specific app.
const userNotifications = notificationStore.getAppNotifications('users');getAppUnreadCount(appName)
Section titled “getAppUnreadCount(appName)”Get unread notification count for a specific app.
const unreadCount = notificationStore.getAppUnreadCount('users');acknowledgeCritical()
Section titled “acknowledgeCritical()”Acknowledge the current critical notification (remove from queue).
notificationStore.acknowledgeCritical();Happens automatically: The CriticalNotificationDialog component calls this when the OK button is clicked.
State Properties
Section titled “State Properties”notifications
Section titled “notifications”Array of notifications in descending chronological order.
const notifications: Notification[] = notificationStore.notifications;unreadCount
Section titled “unreadCount”Number of unread notifications.
const unreadCount: number = notificationStore.unreadCount;isConnected
Section titled “isConnected”Socket.IO connection status.
const isConnected: boolean = notificationStore.isConnected;currentCritical
Section titled “currentCritical”Current critical notification (first in queue).
const currentCritical: Notification | null = notificationStore.currentCritical;hasUnreadCritical
Section titled “hasUnreadCritical”Whether there are unread critical notifications.
const hasUnreadCritical: boolean = notificationStore.hasUnreadCritical;Initialization
Section titled “Initialization”The store is automatically initialized in hooks.client.ts:
import { getNotificationStore } from '$lib/stores/notificationStore.svelte';
export async function handleSession({ data }) { if (data.user?.id) { const notificationStore = getNotificationStore(); await notificationStore.connect(parseInt(data.user.id)); }}Internal Behavior
Section titled “Internal Behavior”Socket.IO Event Handling
Section titled “Socket.IO Event Handling”// New notification receivedsocket.on('notification:new', (notification: Notification) => { // Add to list this.state.notifications = [notification, ...this.state.notifications]; this.state.unreadCount++;
// Show toast (except critical) this.showToastNotification(notification);
// Browser notification this.showBrowserNotification(notification);});
// Update unread countersocket.on('notification:unread-count', (count: number) => { this.state.unreadCount = count;});Automatic Polling
Section titled “Automatic Polling”// Polling every 30 seconds if Socket.IO is unavailablesetInterval(() => { if (browser && !this.state.isConnected) { console.log('[NotificationStore] WebSocket disconnected, polling for notifications'); this.loadNotifications(); }}, 30000);Toast Display
Section titled “Toast Display”private showToastNotification(notification: Notification) { // Critical notifications go to the dialog if (notification.type === 'critical') { this._criticalQueue = [...this._criticalQueue, notification]; return; }
// Show toast import('svelte-sonner').then(({ toast }) => { const toastFn = toast[notification.type] || toast.info; toastFn(title, { description: message, duration: 5000, action: { label: 'Open', onClick: () => this.openNotificationInApp(notification.id) } }); });}Examples
Section titled “Examples”Display Notifications in a List
Section titled “Display Notifications in a List”<script> import { getNotificationStore } from '$lib/stores/notificationStore.svelte';
const notificationStore = getNotificationStore(); const notifications = $derived(notificationStore.notifications);
async function handleMarkAsRead(id: number) { await notificationStore.markAsRead(id); }
async function handleDelete(id: number) { await notificationStore.deleteNotification(id); }</script>
<div> {#each notifications as notification} <div class:unread={!notification.isRead}> <h3>{notification.title}</h3> <p>{notification.message}</p> <p>{new Date(notification.createdAt).toLocaleString()}</p>
{#if !notification.isRead} <button onclick={() => handleMarkAsRead(notification.id)}> Mark as read </button> {/if}
<button onclick={() => handleDelete(notification.id)}> Delete </button> </div> {/each}</div>Unread Count Badge
Section titled “Unread Count Badge”<script> import { getNotificationStore } from '$lib/stores/notificationStore.svelte';
const notificationStore = getNotificationStore(); const unreadCount = $derived(notificationStore.unreadCount);</script>
<button> Notifications {#if unreadCount > 0} <span class="badge">{unreadCount}</span> {/if}</button>App-Specific Notifications
Section titled “App-Specific Notifications”<script> import { getNotificationStore } from '$lib/stores/notificationStore.svelte';
const notificationStore = getNotificationStore(); const appNotifications = $derived(notificationStore.getAppNotifications('users')); const appUnreadCount = $derived(notificationStore.getAppUnreadCount('users'));</script>
<div> <h2>Users App Notifications ({appUnreadCount} unread)</h2>
{#each appNotifications as notification} <div>{notification.message}</div> {/each}</div>Connection Status Display
Section titled “Connection Status Display”<script> import { getNotificationStore } from '$lib/stores/notificationStore.svelte';
const notificationStore = getNotificationStore(); const isConnected = $derived(notificationStore.isConnected);</script>
<div class="status"> {#if isConnected} <span class="online">● Online (WebSocket)</span> {:else} <span class="offline">● Offline (Polling)</span> {/if}</div>