summaryrefslogtreecommitdiff
path: root/apps/web/lib/stores/notification-store.ts
diff options
context:
space:
mode:
authorFuwn <[email protected]>2026-02-07 01:42:57 -0800
committerFuwn <[email protected]>2026-02-07 01:42:57 -0800
commit5c5b1993edd890a80870ee05607ac5f088191d4e (patch)
treea721b76bcd49ba10826c53efc87302c7a689512f /apps/web/lib/stores/notification-store.ts
downloadasa.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.ts66
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",
+ }
+ )
+)