mirror of
https://github.com/codeaashu/claude-code.git
synced 2026-04-08 22:28:48 +03:00
claude-code
This commit is contained in:
149
web/lib/notifications.ts
Normal file
149
web/lib/notifications.ts
Normal file
@@ -0,0 +1,149 @@
|
||||
import { create } from "zustand";
|
||||
import { persist } from "zustand/middleware";
|
||||
import { nanoid } from "nanoid";
|
||||
|
||||
export type ToastVariant = "success" | "error" | "warning" | "info" | "loading";
|
||||
export type NotificationCategory = "error" | "activity" | "system";
|
||||
|
||||
export interface ToastItem {
|
||||
id: string;
|
||||
variant: ToastVariant;
|
||||
title: string;
|
||||
description?: string;
|
||||
duration: number; // ms, 0 = no auto-dismiss
|
||||
action?: { label: string; onClick: () => void };
|
||||
details?: string;
|
||||
createdAt: number;
|
||||
}
|
||||
|
||||
export interface NotificationItem {
|
||||
id: string;
|
||||
title: string;
|
||||
description: string;
|
||||
category: NotificationCategory;
|
||||
link?: string;
|
||||
read: boolean;
|
||||
createdAt: number;
|
||||
}
|
||||
|
||||
const MAX_VISIBLE_TOASTS = 5;
|
||||
const MAX_NOTIFICATIONS = 100;
|
||||
|
||||
export const DEFAULT_DURATIONS: Record<ToastVariant, number> = {
|
||||
success: 5000,
|
||||
info: 5000,
|
||||
warning: 7000,
|
||||
error: 10000,
|
||||
loading: 0,
|
||||
};
|
||||
|
||||
interface NotificationStore {
|
||||
// Toast state
|
||||
toasts: ToastItem[];
|
||||
toastQueue: ToastItem[];
|
||||
// Notification center state
|
||||
notifications: NotificationItem[];
|
||||
// Preferences
|
||||
browserNotificationsEnabled: boolean;
|
||||
soundEnabled: boolean;
|
||||
|
||||
// Toast actions
|
||||
addToast: (options: Omit<ToastItem, "id" | "createdAt">) => string;
|
||||
dismissToast: (id: string) => void;
|
||||
dismissAllToasts: () => void;
|
||||
|
||||
// Notification actions
|
||||
addNotification: (options: Omit<NotificationItem, "id" | "read" | "createdAt">) => void;
|
||||
markRead: (id: string) => void;
|
||||
markAllRead: () => void;
|
||||
clearHistory: () => void;
|
||||
|
||||
// Preference actions
|
||||
setBrowserNotificationsEnabled: (enabled: boolean) => void;
|
||||
setSoundEnabled: (enabled: boolean) => void;
|
||||
}
|
||||
|
||||
export const useNotificationStore = create<NotificationStore>()(
|
||||
persist(
|
||||
(set) => ({
|
||||
toasts: [],
|
||||
toastQueue: [],
|
||||
notifications: [],
|
||||
browserNotificationsEnabled: true,
|
||||
soundEnabled: false,
|
||||
|
||||
addToast: (options) => {
|
||||
const id = nanoid();
|
||||
const toast: ToastItem = { ...options, id, createdAt: Date.now() };
|
||||
set((state) => {
|
||||
if (state.toasts.length < MAX_VISIBLE_TOASTS) {
|
||||
return { toasts: [...state.toasts, toast] };
|
||||
}
|
||||
return { toastQueue: [...state.toastQueue, toast] };
|
||||
});
|
||||
return id;
|
||||
},
|
||||
|
||||
dismissToast: (id) => {
|
||||
set((state) => {
|
||||
const toasts = state.toasts.filter((t) => t.id !== id);
|
||||
const [next, ...queue] = state.toastQueue;
|
||||
if (next) {
|
||||
return { toasts: [...toasts, next], toastQueue: queue };
|
||||
}
|
||||
return { toasts };
|
||||
});
|
||||
},
|
||||
|
||||
dismissAllToasts: () => {
|
||||
set({ toasts: [], toastQueue: [] });
|
||||
},
|
||||
|
||||
addNotification: (options) => {
|
||||
const notification: NotificationItem = {
|
||||
...options,
|
||||
id: nanoid(),
|
||||
read: false,
|
||||
createdAt: Date.now(),
|
||||
};
|
||||
set((state) => ({
|
||||
notifications: [notification, ...state.notifications].slice(0, MAX_NOTIFICATIONS),
|
||||
}));
|
||||
},
|
||||
|
||||
markRead: (id) => {
|
||||
set((state) => ({
|
||||
notifications: state.notifications.map((n) =>
|
||||
n.id === id ? { ...n, read: true } : n
|
||||
),
|
||||
}));
|
||||
},
|
||||
|
||||
markAllRead: () => {
|
||||
set((state) => ({
|
||||
notifications: state.notifications.map((n) => ({ ...n, read: true })),
|
||||
}));
|
||||
},
|
||||
|
||||
clearHistory: () => {
|
||||
set({ notifications: [] });
|
||||
},
|
||||
|
||||
setBrowserNotificationsEnabled: (enabled) => {
|
||||
set({ browserNotificationsEnabled: enabled });
|
||||
},
|
||||
|
||||
setSoundEnabled: (enabled) => {
|
||||
set({ soundEnabled: enabled });
|
||||
},
|
||||
}),
|
||||
{
|
||||
name: "claude-code-notifications",
|
||||
partialize: (state) => ({
|
||||
notifications: state.notifications,
|
||||
browserNotificationsEnabled: state.browserNotificationsEnabled,
|
||||
soundEnabled: state.soundEnabled,
|
||||
}),
|
||||
}
|
||||
)
|
||||
);
|
||||
Reference in New Issue
Block a user