@classic-homes/notifications
v0.1.36
Published
Notification services and Svelte bindings for Classic Theme apps
Readme
@classic-homes/notifications
Framework-agnostic notifications core with Svelte bindings for the Classic Theme design system.
Features
- Persistent notifications from API with pagination
- Mark as read/unread functionality
- Batch operations (mark all read, delete all)
- Svelte reactive stores for notification state
- Derived stores for unread count and loading state
- TypeScript-first with full type safety
- Integrates with
@classic-homes/authfor API authentication
Installation
npm install @classic-homes/notificationsPrerequisites
This package requires @classic-homes/auth to be initialized first, as it uses the shared HTTP client and authentication headers.
import { initAuth } from '@classic-homes/auth';
initAuth({
baseUrl: 'https://api.example.com',
// ... other config
});Quick Start
1. Fetch Notifications
import { notificationStore } from '@classic-homes/notifications/svelte';
// Fetch initial notifications
await notificationStore.fetch();
// With pagination
await notificationStore.fetch({ page: 1, limit: 20 });
// Filter by type or read status
await notificationStore.fetch({ type: 'info', unreadOnly: true });2. Use in Svelte Components
<script lang="ts">
import {
notificationStore,
notifications,
unreadCount,
isLoadingNotifications,
} from '@classic-homes/notifications/svelte';
// Load notifications on mount
$effect(() => {
notificationStore.fetch();
});
</script>
{#if $isLoadingNotifications}
<Spinner />
{:else}
<p>You have {$unreadCount} unread notifications</p>
{#each $notifications as notification}
<div class:unread={!notification.read}>
<h4>{notification.title}</h4>
<p>{notification.message}</p>
<button onclick={() => notificationStore.markRead(notification.id)}> Mark as read </button>
</div>
{/each}
{/if}API Reference
Core Exports
import {
// API
notificationsApi,
// Service
notificationService,
NotificationService,
// Utilities
getNotificationIconName,
getNotificationIconColor,
getPriorityBadgeVariant,
getTypeBadgeVariant,
getPriorityLabel,
getTypeLabel,
formatRelativeTime,
formatFullTimestamp,
formatNotificationTime,
getPrioritySortValue,
// Types
type Notification,
type NotificationType,
type NotificationPriority,
type NotificationPreference,
type NotificationChannel,
type NotificationFrequency,
type NotificationFilter,
type NotificationCounts,
type BadgeVariant,
} from '@classic-homes/notifications';Svelte Exports
import {
// Main store
notificationStore,
// Derived stores
notifications, // Notification[]
unreadCount, // number
isLoadingNotifications, // boolean
// Types
type Notification,
type NotificationType,
type NotificationPriority,
} from '@classic-homes/notifications/svelte';Notification Store Methods
Fetching
// Fetch notifications with optional filters
await notificationStore.fetch({
page?: number,
limit?: number,
type?: NotificationType,
unreadOnly?: boolean,
});
// Refresh current page
await notificationStore.refresh();
// Load more (next page)
await notificationStore.loadMore();Marking as Read
// Mark single notification as read
await notificationStore.markRead(notificationId);
// Mark multiple notifications as read
await notificationStore.markManyRead([id1, id2, id3]);
// Mark all notifications as read
await notificationStore.markAllRead();Deleting
// Dismiss single notification
await notificationStore.dismiss(notificationId);
// Delete all notifications
await notificationStore.deleteAll();State Management
// Clear error state
notificationStore.clearError();
// Reset store to initial state
notificationStore.reset();Notification Types
type NotificationType = 'security' | 'system' | 'feature' | 'update' | 'alert' | 'info';
type NotificationPriority = 'urgent' | 'high' | 'normal' | 'low';
interface Notification {
id: string;
userId: string;
type: NotificationType;
category: string;
title: string;
message: string;
icon?: string;
color?: string;
priority: NotificationPriority;
read: boolean;
dismissed: boolean;
actionUrl?: string;
actionLabel?: string;
metadata?: Record<string, unknown>;
createdAt: string;
readAt?: string;
dismissedAt?: string;
}Channels and Preferences
The package supports notification channels (email, sms, push, in_app) and user preferences for fine-grained control.
Getting Channels
import { notificationService } from '@classic-homes/notifications/core';
const { channels } = await notificationService.getChannels();
// channels: [{ id, name, displayName, enabled, priority }]Managing Preferences
import { notificationService } from '@classic-homes/notifications/core';
// Get user preferences
const { preferences } = await notificationService.getPreferences();
// Update a preference
await notificationService.updatePreference({
channelId: 'email',
notificationType: 'security',
enabled: true,
frequency: 'immediate',
minPriority: 'normal',
});
// Batch update preferences
await notificationService.batchUpdatePreferences([
{ eventType: 'security', channels: ['email', 'push'], enabled: true },
{ eventType: 'feature', channels: ['in_app'], enabled: true },
]);Preference Types
type ChannelType = 'email' | 'sms' | 'push' | 'in_app';
type NotificationFrequency = 'immediate' | 'digest_hourly' | 'digest_daily' | 'digest_weekly';
interface NotificationPreference {
id: string;
userId: string;
channelId: string;
notificationType: NotificationType;
notificationCategory?: string;
enabled: boolean;
frequency: NotificationFrequency;
quietHoursStart?: string;
quietHoursEnd?: string;
quietHoursTimezone?: string;
minPriority: NotificationPriority;
tags?: string[];
createdAt: string;
updatedAt?: string;
}Utility Functions
Display helpers for consistent notification rendering:
import {
getNotificationIconName,
getNotificationIconColor,
getPriorityBadgeVariant,
getTypeBadgeVariant,
getPriorityLabel,
getTypeLabel,
formatRelativeTime,
formatNotificationTime,
} from '@classic-homes/notifications/core';
// Get icon for notification type
getNotificationIconName('security'); // 'shield'
getNotificationIconColor('security'); // 'text-red-500'
// Get badge variants
getPriorityBadgeVariant('urgent'); // 'destructive'
getTypeBadgeVariant('update'); // 'success'
// Get display labels
getPriorityLabel('high'); // 'High'
getTypeLabel('feature'); // 'Feature'
// Format timestamps
formatRelativeTime('2024-01-22T10:00:00Z'); // '2h ago'
formatNotificationTime('2024-01-15T10:00:00Z'); // 'Jan 15'Using with Toast Notifications
For client-side only toast notifications (not persisted), use toastStore from @classic-homes/theme-svelte:
<script lang="ts">
import { toastStore } from '@classic-homes/theme-svelte';
function showSuccess() {
toastStore.success('Operation completed!');
}
function showError() {
toastStore.error('Something went wrong');
}
</script>Store State
interface NotificationState {
notifications: Notification[];
unreadCount: number;
isLoading: boolean;
error: string | null;
pagination: {
page: number;
limit: number;
total: number;
totalPages: number;
};
}Example: Notification Bell
<script lang="ts">
import {
notificationStore,
notifications,
unreadCount,
} from '@classic-homes/notifications/svelte';
import { Badge, Button, DropdownMenu } from '@classic-homes/theme-svelte';
let isOpen = $state(false);
$effect(() => {
if (isOpen) {
notificationStore.fetch({ limit: 5 });
}
});
</script>
<DropdownMenu bind:open={isOpen}>
<Button slot="trigger" variant="ghost">
<BellIcon />
{#if $unreadCount > 0}
<Badge variant="destructive">{$unreadCount}</Badge>
{/if}
</Button>
<div slot="content">
{#each $notifications as notification}
<div class="notification-item">
<span>{notification.title}</span>
{#if !notification.read}
<button onclick={() => notificationStore.markRead(notification.id)}> Mark read </button>
{/if}
</div>
{/each}
{#if $unreadCount > 0}
<button onclick={() => notificationStore.markAllRead()}> Mark all as read </button>
{/if}
</div>
</DropdownMenu>Direct API Access
For direct API access without the Svelte store:
import { notificationsApi } from '@classic-homes/notifications/core';
// List notifications
const response = await notificationsApi.list({ page: 1, limit: 20 });
// Get single notification
const notification = await notificationsApi.get('notification-id');
// Mark as read
await notificationsApi.markRead('notification-id');
await notificationsApi.markManyRead(['id1', 'id2']);
await notificationsApi.markAllRead();
// Delete notifications
await notificationsApi.dismiss('notification-id');
await notificationsApi.bulkDelete(['id1', 'id2']);
await notificationsApi.deleteAll();
// Channels and preferences
const channels = await notificationsApi.getChannels();
const preferences = await notificationsApi.getPreferences();
await notificationsApi.updatePreference(preferenceData);
await notificationsApi.deletePreference('subscription-id');
// Test notifications (dev/admin only)
await notificationsApi.generateTestNotifications({ type: 'info' });License
MIT
