supersam/src/hooks/useNotifications.js

132 lines
3.8 KiB
JavaScript

import React from "react";
import { supabase, hasSupabaseConfig } from "../supabaseClient";
export function useNotifications(userId) {
const [notifications, setNotifications] = React.useState([]);
const [unreadCount, setUnreadCount] = React.useState(0);
const [isLoading, setIsLoading] = React.useState(true);
const fetchNotifications = React.useCallback(async () => {
if (!hasSupabaseConfig || !userId) {
setIsLoading(false);
return;
}
const { data, error } = await supabase
.from("notifications")
.select("*")
.eq("user_id", userId)
.order("created_at", { ascending: false })
.limit(50);
if (!error && data) {
setNotifications(data);
setUnreadCount(data.filter((n) => !n.read).length);
}
setIsLoading(false);
}, [userId]);
// Initial fetch
React.useEffect(() => {
fetchNotifications();
}, [fetchNotifications]);
// NOTE: realtime (WebSocket) subscription intentionally removed (2026-08).
// The supabase realtime container kept dying and the client hung in WS retry
// loops (30-40s stalls on open). Notifications now update via REST fetch on
// page load / focus only — see handleFocus below for refresh on tab focus.
// Refresh notifications when the tab regains focus so the unread badge stays
// reasonably fresh without a live WebSocket connection.
React.useEffect(() => {
if (!hasSupabaseConfig || !userId) return;
const handleFocus = () => fetchNotifications();
window.addEventListener("focus", handleFocus);
return () => window.removeEventListener("focus", handleFocus);
}, [fetchNotifications, userId]);
// Fix unread count: always recalculate from notifications
React.useEffect(() => {
setUnreadCount(notifications.filter((n) => !n.read).length);
}, [notifications]);
const markAsRead = React.useCallback(
async (notificationId) => {
if (!hasSupabaseConfig) return;
await supabase
.from("notifications")
.update({ read: true })
.eq("id", notificationId);
setNotifications((prev) =>
prev.map((n) => (n.id === notificationId ? { ...n, read: true } : n))
);
},
[]
);
const markAllAsRead = React.useCallback(async () => {
if (!hasSupabaseConfig || !userId) return;
await supabase
.from("notifications")
.update({ read: true })
.eq("user_id", userId)
.eq("read", false);
setNotifications((prev) => prev.map((n) => ({ ...n, read: true })));
}, [userId]);
return {
notifications,
unreadCount,
isLoading,
markAsRead,
markAllAsRead,
refresh: fetchNotifications,
};
}
export function useNotificationPreferences(userId) {
const [prefs, setPrefs] = React.useState({
push_enabled: true,
order_status_change: true,
driver_assigned: true,
delivery_problem: true,
});
const [isLoading, setIsLoading] = React.useState(true);
React.useEffect(() => {
if (!hasSupabaseConfig || !userId) {
setIsLoading(false);
return;
}
supabase
.from("notification_preferences")
.select("*")
.eq("user_id", userId)
.single()
.then(({ data, error }) => {
if (data) {
setPrefs({
push_enabled: data.push_enabled,
order_status_change: data.order_status_change,
driver_assigned: data.driver_assigned,
delivery_problem: data.delivery_problem,
});
}
setIsLoading(false);
});
}, [userId]);
const updatePref = React.useCallback(
async (key, value) => {
if (!hasSupabaseConfig || !userId) return;
setPrefs((prev) => ({ ...prev, [key]: value }));
await supabase
.from("notification_preferences")
.upsert({ user_id: userId, [key]: value, updated_at: new Date().toISOString() });
},
[userId]
);
return { prefs, isLoading, updatePref };
}