diff options
| author | Fuwn <[email protected]> | 2026-02-07 01:42:57 -0800 |
|---|---|---|
| committer | Fuwn <[email protected]> | 2026-02-07 01:42:57 -0800 |
| commit | 5c5b1993edd890a80870ee05607ac5f088191d4e (patch) | |
| tree | a721b76bcd49ba10826c53efc87302c7a689512f /apps/web/lib/stores/notification-store.ts | |
| download | asa.news-5c5b1993edd890a80870ee05607ac5f088191d4e.tar.xz asa.news-5c5b1993edd890a80870ee05607ac5f088191d4e.zip | |
feat: asa.news RSS reader with developer tier, REST API, and webhooks
Full-stack RSS reader SaaS: Supabase + Next.js + Go worker.
Includes three subscription tiers (free/pro/developer), API key auth,
read-only REST API, webhook push notifications, Stripe billing with
proration, and PWA support.
Diffstat (limited to 'apps/web/lib/stores/notification-store.ts')
| -rw-r--r-- | apps/web/lib/stores/notification-store.ts | 66 |
1 files changed, 66 insertions, 0 deletions
diff --git a/apps/web/lib/stores/notification-store.ts b/apps/web/lib/stores/notification-store.ts new file mode 100644 index 0000000..d7eee57 --- /dev/null +++ b/apps/web/lib/stores/notification-store.ts @@ -0,0 +1,66 @@ +import { create } from "zustand" +import { persist } from "zustand/middleware" + +const MAXIMUM_NOTIFICATIONS = 50 + +export interface StoredNotification { + identifier: string + message: string + timestamp: string + type: "info" | "success" | "error" + actionUrl?: string +} + +interface NotificationState { + notifications: StoredNotification[] + lastViewedAt: string | null + + addNotification: ( + message: string, + type?: "info" | "success" | "error", + actionUrl?: string + ) => void + dismissNotification: (identifier: string) => void + clearAllNotifications: () => void + markAllAsViewed: () => void +} + +export const useNotificationStore = create<NotificationState>()( + persist( + (set) => ({ + notifications: [], + lastViewedAt: null, + + addNotification: (message, type = "info", actionUrl) => + set((state) => { + const newNotification: StoredNotification = { + identifier: crypto.randomUUID(), + message, + timestamp: new Date().toISOString(), + type, + ...(actionUrl ? { actionUrl } : {}), + } + const updated = [newNotification, ...state.notifications].slice( + 0, + MAXIMUM_NOTIFICATIONS + ) + return { notifications: updated } + }), + + dismissNotification: (identifier) => + set((state) => ({ + notifications: state.notifications.filter( + (notification) => notification.identifier !== identifier + ), + })), + + clearAllNotifications: () => set({ notifications: [] }), + + markAllAsViewed: () => + set({ lastViewedAt: new Date().toISOString() }), + }), + { + name: "asa-news-notifications", + } + ) +) |