notificationStores.js raw
1 import { writable, derived } from 'svelte/store';
2
3 // ==================== Notification Categories ====================
4
5 // Each category: { items: [], unreadCount: number, lastChecked: number }
6 export const replyNotifications = writable({ items: [], unreadCount: 0, lastChecked: 0 });
7 export const reactionNotifications = writable({ items: [], unreadCount: 0, lastChecked: 0 });
8 export const zapNotifications = writable({ items: [], unreadCount: 0, lastChecked: 0 });
9
10 // DM and channel unread counts are derived from chatStores (imported by components, not here)
11 // These are placeholder counts updated by the notification system
12 export const dmUnreadCount = writable(0);
13 export const channelUnreadCount = writable(0);
14
15 // ==================== Derived ====================
16
17 // Total unread across all categories
18 export const totalUnreadCount = derived(
19 [replyNotifications, reactionNotifications, zapNotifications, dmUnreadCount, channelUnreadCount],
20 ([$replies, $reactions, $zaps, $dms, $channels]) =>
21 $replies.unreadCount + $reactions.unreadCount + $zaps.unreadCount + $dms + $channels
22 );
23
24 // ==================== Actions ====================
25
26 /**
27 * Add notification items to a category
28 * @param {Function} store - The writable store to update
29 * @param {Array} items - New notification items
30 */
31 function addNotifications(store, items) {
32 store.update(cat => {
33 const existingIds = new Set(cat.items.map(i => i.id));
34 const newItems = items.filter(i => !existingIds.has(i.id));
35 const newUnread = newItems.filter(i => i.created_at > cat.lastChecked).length;
36 return {
37 items: [...newItems, ...cat.items].sort((a, b) => b.created_at - a.created_at).slice(0, 100),
38 unreadCount: cat.unreadCount + newUnread,
39 lastChecked: cat.lastChecked
40 };
41 });
42 }
43
44 export function addReplyNotifications(items) { addNotifications(replyNotifications, items); }
45 export function addReactionNotifications(items) { addNotifications(reactionNotifications, items); }
46 export function addZapNotifications(items) { addNotifications(zapNotifications, items); }
47
48 /**
49 * Mark a category as read
50 * @param {string} category - "replies" | "reactions" | "zaps"
51 */
52 export function markCategoryRead(category) {
53 const stores = { replies: replyNotifications, reactions: reactionNotifications, zaps: zapNotifications };
54 const store = stores[category];
55 if (store) {
56 store.update(cat => ({ ...cat, unreadCount: 0, lastChecked: Date.now() }));
57 }
58 }
59
60 /**
61 * Mark all notifications as read
62 */
63 export function markAllRead() {
64 const now = Date.now();
65 replyNotifications.update(c => ({ ...c, unreadCount: 0, lastChecked: now }));
66 reactionNotifications.update(c => ({ ...c, unreadCount: 0, lastChecked: now }));
67 zapNotifications.update(c => ({ ...c, unreadCount: 0, lastChecked: now }));
68 }
69
70 /**
71 * Reset all notification state (on logout)
72 */
73 export function resetNotificationState() {
74 replyNotifications.set({ items: [], unreadCount: 0, lastChecked: 0 });
75 reactionNotifications.set({ items: [], unreadCount: 0, lastChecked: 0 });
76 zapNotifications.set({ items: [], unreadCount: 0, lastChecked: 0 });
77 dmUnreadCount.set(0);
78 channelUnreadCount.set(0);
79 }
80