132 lines
3.8 KiB
JavaScript
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 };
|
|
}
|