| import { useState, useCallback } from 'react'; |
| import { eq, desc, and, sql } from 'drizzle-orm'; |
| import { useDatabase } from './useDatabase'; |
| import { notifications } from '../db/schema'; |
|
|
| |
|
|
| type NotificationType = |
| | 'ride_matched' |
| | 'ride_cancelled' |
| | 'ride_completed' |
| | 'message' |
| | 'friend_request' |
| | 'system'; |
|
|
| interface NotificationData { |
| id: string; |
| userId: string; |
| type: NotificationType; |
| title: string; |
| body: string; |
| data: string | null; |
| read: boolean | null; |
| createdAt: string | null; |
| } |
|
|
| interface UseNotificationsReturn { |
| notifications: NotificationData[]; |
| unreadCount: number; |
| loading: boolean; |
| loadNotifications: () => Promise<void>; |
| markAsRead: (notificationId: string) => Promise<void>; |
| markAllAsRead: () => Promise<void>; |
| addNotification: (data: { |
| userId: string; |
| type: NotificationType; |
| title: string; |
| body: string; |
| data?: string; |
| }) => Promise<NotificationData | null>; |
| } |
|
|
| |
|
|
| export function useNotifications(userId?: string): UseNotificationsReturn { |
| const { db } = useDatabase(); |
| const [notificationsList, setNotificationsList] = useState<NotificationData[]>( |
| [], |
| ); |
| const [loading, setLoading] = useState(false); |
|
|
| |
|
|
| const unreadCount = notificationsList.filter((n) => !n.read).length; |
|
|
| |
|
|
| const loadNotifications = useCallback(async () => { |
| try { |
| setLoading(true); |
|
|
| let results; |
|
|
| if (userId) { |
| results = await db |
| .select() |
| .from(notifications) |
| .where(eq(notifications.userId, userId)) |
| .orderBy(desc(notifications.createdAt)) |
| .all(); |
| } else { |
| results = await db |
| .select() |
| .from(notifications) |
| .orderBy(desc(notifications.createdAt)) |
| .all(); |
| } |
|
|
| setNotificationsList(results as NotificationData[]); |
| } catch (error) { |
| console.error('[useNotifications] Failed to load notifications:', error); |
| setNotificationsList([]); |
| } finally { |
| setLoading(false); |
| } |
| }, [db, userId]); |
|
|
| |
|
|
| const markAsRead = useCallback( |
| async (notificationId: string) => { |
| try { |
| await db |
| .update(notifications) |
| .set({ read: true }) |
| .where(eq(notifications.id, notificationId)) |
| .run(); |
|
|
| await loadNotifications(); |
| } catch (error) { |
| console.error('[useNotifications] Failed to mark as read:', error); |
| } |
| }, |
| [db, loadNotifications], |
| ); |
|
|
| |
|
|
| const markAllAsRead = useCallback(async () => { |
| try { |
| if (userId) { |
| await db |
| .update(notifications) |
| .set({ read: true }) |
| .where(and(eq(notifications.userId, userId), eq(notifications.read, false))) |
| .run(); |
| } else { |
| await db |
| .update(notifications) |
| .set({ read: true }) |
| .where(eq(notifications.read, false)) |
| .run(); |
| } |
|
|
| await loadNotifications(); |
| } catch (error) { |
| console.error('[useNotifications] Failed to mark all as read:', error); |
| } |
| }, [db, userId, loadNotifications]); |
|
|
| |
|
|
| const addNotification = useCallback( |
| async (data: { |
| userId: string; |
| type: NotificationType; |
| title: string; |
| body: string; |
| data?: string; |
| }): Promise<NotificationData | null> => { |
| const id = `${Date.now()}-${Math.random().toString(36).slice(2, 9)}`; |
|
|
| try { |
| await db.insert(notifications).values({ |
| id, |
| userId: data.userId, |
| type: data.type, |
| title: data.title, |
| body: data.body, |
| data: data.data ?? null, |
| read: false, |
| }).run(); |
|
|
| await loadNotifications(); |
|
|
| const created = notificationsList.find((n) => n.id === id); |
| return (created as NotificationData) ?? null; |
| } catch (error) { |
| console.error('[useNotifications] Failed to add notification:', error); |
| return null; |
| } |
| }, |
| [db, loadNotifications, notificationsList], |
| ); |
|
|
| return { |
| notifications: notificationsList, |
| unreadCount, |
| loading, |
| loadNotifications, |
| markAsRead, |
| markAllAsRead, |
| addNotification, |
| }; |
| } |
|
|