mobileapp / src /hooks /useNotifications.ts
Antaram Dev Bot
feat: complete ANTARAM.ORG ride-sharing app frontend
5c876be
import { useState, useCallback } from 'react';
import { eq, desc, and, sql } from 'drizzle-orm';
import { useDatabase } from './useDatabase';
import { notifications } from '../db/schema';
// ─── Types ─────────────────────────────────────────────────────────────────────
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>;
}
// ─── Hook ──────────────────────────────────────────────────────────────────────
export function useNotifications(userId?: string): UseNotificationsReturn {
const { db } = useDatabase();
const [notificationsList, setNotificationsList] = useState<NotificationData[]>(
[],
);
const [loading, setLoading] = useState(false);
// ── Compute unread count ─────────────────────────────────────────────────────
const unreadCount = notificationsList.filter((n) => !n.read).length;
// ── Load notifications ───────────────────────────────────────────────────────
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]);
// ── Mark single notification as read ─────────────────────────────────────────
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],
);
// ── Mark all as read ─────────────────────────────────────────────────────────
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]);
// ── Add notification ─────────────────────────────────────────────────────────
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,
};
}