export interface AppNotification { id: number uploadId: string commentId: number isRead: boolean isMention: boolean createdAt: string actorName: string actorColor: string | null content: string uploadType: 'image' | 'video' | 'audio' | 'youtube' | 'twitch' | 'link' url: string thumbUrl: string | null } export function useNotifications() { const notifications = useState('notifications:list', () => []) const unreadCount = useState('notifications:unread', () => 0) const unreadUploadIds = computed(() => new Set(notifications.value.filter(n => !n.isRead).map(n => n.uploadId)), ) async function fetchAll() { const data = await $fetch<{ notifications: AppNotification[], unreadCount: number }>('/api/notifications') notifications.value = data.notifications unreadCount.value = data.unreadCount } function addLive(notification: AppNotification, count?: number) { const existing = notifications.value.some(n => n.uploadId === notification.uploadId && !n.isRead) notifications.value = [notification, ...notifications.value.filter(n => n.uploadId !== notification.uploadId)].slice(0, 30) unreadCount.value = count ?? (unreadCount.value + (existing ? 0 : 1)) } async function markAllRead() { if (!unreadCount.value) return unreadCount.value = 0 notifications.value = notifications.value.map(n => ({ ...n, isRead: true })) await $fetch('/api/notifications/read', { method: 'POST' }) } async function markRead(id: number) { const target = notifications.value.find(n => n.id === id) notifications.value = notifications.value.filter(n => target ? n.uploadId !== target.uploadId : n.id !== id) if (target && !target.isRead) { unreadCount.value = Math.max(0, unreadCount.value - 1) } await $fetch(`/api/notifications/${id}/read`, { method: 'POST' }) } function reset() { notifications.value = [] unreadCount.value = 0 } return { notifications, unreadCount, unreadUploadIds, fetchAll, addLive, markAllRead, markRead, reset } }