feature/IO-3096-GlobalNotifications - Checkpoint - Notification Center
This commit is contained in:
@@ -3,10 +3,13 @@ import SocketIO from "socket.io-client";
|
||||
import { auth } from "../../firebase/firebase.utils";
|
||||
import { store } from "../../redux/store";
|
||||
import { addAlerts, setWssStatus } from "../../redux/application/application.actions";
|
||||
import client from "../../utils/GraphQLClient";
|
||||
import { useNotification } from "../../contexts/Notifications/notificationContext.jsx";
|
||||
|
||||
const useSocket = (bodyshop) => {
|
||||
const socketRef = useRef(null);
|
||||
const [clientId, setClientId] = useState(null);
|
||||
const notification = useNotification();
|
||||
|
||||
useEffect(() => {
|
||||
const initializeSocket = async (token) => {
|
||||
@@ -25,10 +28,8 @@ const useSocket = (bodyshop) => {
|
||||
|
||||
socketRef.current = socketInstance;
|
||||
|
||||
// Handle socket events
|
||||
const handleBodyshopMessage = (message) => {
|
||||
if (!message || !message.type) return;
|
||||
|
||||
switch (message.type) {
|
||||
case "alert-update":
|
||||
store.dispatch(addAlerts(message.payload));
|
||||
@@ -36,7 +37,6 @@ const useSocket = (bodyshop) => {
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (!import.meta.env.DEV) return;
|
||||
console.log(`Received message for bodyshop ${bodyshop.id}:`, message);
|
||||
};
|
||||
@@ -45,6 +45,7 @@ const useSocket = (bodyshop) => {
|
||||
socketInstance.emit("join-bodyshop-room", bodyshop.id);
|
||||
setClientId(socketInstance.id);
|
||||
store.dispatch(setWssStatus("connected"));
|
||||
console.log("Socket connected, ID:", socketInstance.id);
|
||||
};
|
||||
|
||||
const handleReconnect = () => {
|
||||
@@ -53,13 +54,11 @@ const useSocket = (bodyshop) => {
|
||||
|
||||
const handleConnectionError = (err) => {
|
||||
console.error("Socket connection error:", err);
|
||||
|
||||
// Handle token expiration
|
||||
if (err.message.includes("auth/id-token-expired")) {
|
||||
console.warn("Token expired, refreshing...");
|
||||
auth.currentUser?.getIdToken(true).then((newToken) => {
|
||||
socketInstance.auth = { token: newToken }; // Update socket auth
|
||||
socketInstance.connect(); // Retry connection
|
||||
socketInstance.auth = { token: newToken };
|
||||
socketInstance.connect();
|
||||
});
|
||||
} else {
|
||||
store.dispatch(setWssStatus("error"));
|
||||
@@ -69,39 +68,107 @@ const useSocket = (bodyshop) => {
|
||||
const handleDisconnect = (reason) => {
|
||||
console.warn("Socket disconnected:", reason);
|
||||
store.dispatch(setWssStatus("disconnected"));
|
||||
|
||||
// Manually trigger reconnection if necessary
|
||||
if (!socketInstance.connected && reason !== "io server disconnect") {
|
||||
setTimeout(() => {
|
||||
if (socketInstance.disconnected) {
|
||||
console.log("Manually triggering reconnection...");
|
||||
socketInstance.connect();
|
||||
}
|
||||
}, 2000); // Retry after 2 seconds
|
||||
}, 2000);
|
||||
}
|
||||
};
|
||||
|
||||
const handleNotification = (data) => {
|
||||
const { jobId, bodyShopId, notifications, notificationId } = data;
|
||||
console.log("handleNotification - Received", { jobId, bodyShopId, notificationId, notifications });
|
||||
|
||||
const newNotification = {
|
||||
id: notificationId,
|
||||
jobid: jobId,
|
||||
associationid: null,
|
||||
scenario_text: notifications.map((notif) => notif.body),
|
||||
fcm_text: notifications.map((notif) => notif.body).join(". ") + ".",
|
||||
scenario_meta: notifications.map((notif) => notif.variables || {}),
|
||||
created_at: new Date(notifications[0].timestamp).toISOString(),
|
||||
read: null,
|
||||
__typename: "notifications"
|
||||
};
|
||||
|
||||
try {
|
||||
client.cache.modify({
|
||||
id: "ROOT_QUERY",
|
||||
fields: {
|
||||
notifications(existing = []) {
|
||||
if (existing.some((n) => n.id === newNotification.id)) return existing;
|
||||
console.log("Adding to cache", newNotification);
|
||||
return [newNotification, ...existing];
|
||||
},
|
||||
notifications_aggregate(existing = { aggregate: { count: 0 } }) {
|
||||
// Fallback if aggregate isn’t in schema yet
|
||||
if (!existing) return existing;
|
||||
console.log("Updating unread count", existing.aggregate.count + 1);
|
||||
return {
|
||||
...existing,
|
||||
aggregate: {
|
||||
...existing.aggregate,
|
||||
count: existing.aggregate.count + 1
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
notification.info({
|
||||
message: "New Notification",
|
||||
description: (
|
||||
<ul>
|
||||
{notifications.map((notif, index) => (
|
||||
<li key={index}>{notif.body}</li>
|
||||
))}
|
||||
</ul>
|
||||
),
|
||||
placement: "topRight",
|
||||
duration: 5
|
||||
});
|
||||
} catch (error) {
|
||||
console.error("Error in handleNotification:", error);
|
||||
}
|
||||
};
|
||||
|
||||
// Register event handlers
|
||||
socketInstance.on("connect", handleConnect);
|
||||
socketInstance.on("reconnect", handleReconnect);
|
||||
socketInstance.on("connect_error", handleConnectionError);
|
||||
socketInstance.on("disconnect", handleDisconnect);
|
||||
socketInstance.on("bodyshop-message", handleBodyshopMessage);
|
||||
|
||||
socketInstance.on("message", (message) => {
|
||||
console.log("Raw socket message:", message);
|
||||
try {
|
||||
if (typeof message === "string" && message.startsWith("42")) {
|
||||
const parsedMessage = JSON.parse(message.slice(2));
|
||||
const [event, data] = parsedMessage;
|
||||
if (event === "notification") handleNotification(data);
|
||||
} else if (Array.isArray(message)) {
|
||||
const [event, data] = message;
|
||||
if (event === "notification") handleNotification(data);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error parsing socket message:", error);
|
||||
}
|
||||
});
|
||||
|
||||
socketInstance.on("notification", handleNotification);
|
||||
};
|
||||
|
||||
const unsubscribe = auth.onIdTokenChanged(async (user) => {
|
||||
if (user) {
|
||||
const token = await user.getIdToken();
|
||||
|
||||
if (socketRef.current) {
|
||||
// Update token if socket exists
|
||||
socketRef.current.emit("update-token", { token, bodyshopId: bodyshop.id });
|
||||
} else {
|
||||
// Initialize socket if not already connected
|
||||
initializeSocket(token);
|
||||
}
|
||||
} else {
|
||||
// User is not authenticated
|
||||
if (socketRef.current) {
|
||||
socketRef.current.disconnect();
|
||||
socketRef.current = null;
|
||||
@@ -109,7 +176,6 @@ const useSocket = (bodyshop) => {
|
||||
}
|
||||
});
|
||||
|
||||
// Clean up on unmount
|
||||
return () => {
|
||||
unsubscribe();
|
||||
if (socketRef.current) {
|
||||
@@ -117,7 +183,7 @@ const useSocket = (bodyshop) => {
|
||||
socketRef.current = null;
|
||||
}
|
||||
};
|
||||
}, [bodyshop]);
|
||||
}, [bodyshop, client.cache]);
|
||||
|
||||
return { socket: socketRef.current, clientId };
|
||||
};
|
||||
Reference in New Issue
Block a user