feature/IO-3586-Socket-Reconnect-Issues - Fix
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
import { useEffect, useRef, useState } from "react";
|
||||
import { useCallback, useEffect, useRef, useState } from "react";
|
||||
import SocketIO from "socket.io-client";
|
||||
import { auth } from "../../firebase/firebase.utils";
|
||||
import { store } from "../../redux/store";
|
||||
@@ -18,6 +18,7 @@ import { useTreatmentsWithConfig } from "@splitsoftware/splitio-react";
|
||||
import { INITIAL_NOTIFICATIONS, SocketContext } from "./useSocket.js";
|
||||
|
||||
const LIMIT = INITIAL_NOTIFICATIONS;
|
||||
const TOKEN_SYNC_INTERVAL_MS = 10 * 60 * 1000;
|
||||
|
||||
/**
|
||||
* Socket Provider - Scenario Notifications / Web Socket related items
|
||||
@@ -30,6 +31,7 @@ const LIMIT = INITIAL_NOTIFICATIONS;
|
||||
*/
|
||||
const SocketProvider = ({ children, bodyshop, navigate, currentUser }) => {
|
||||
const socketRef = useRef(null);
|
||||
const tokenSyncIntervalRef = useRef(null);
|
||||
const [clientId, setClientId] = useState(null);
|
||||
const [isConnected, setIsConnected] = useState(false);
|
||||
const notification = useNotification();
|
||||
@@ -147,6 +149,30 @@ const SocketProvider = ({ children, bodyshop, navigate, currentUser }) => {
|
||||
onError: (err) => console.error("MARK_ALL_NOTIFICATIONS_READ error:", err)
|
||||
});
|
||||
|
||||
const reconnectSocket = useCallback(
|
||||
async ({ forceRefreshToken = true } = {}) => {
|
||||
const socketInstance = socketRef.current;
|
||||
if (!socketInstance || !auth.currentUser || !bodyshop?.id) return false;
|
||||
|
||||
try {
|
||||
const token = await auth.currentUser.getIdToken(forceRefreshToken);
|
||||
socketInstance.auth = { token, bodyshopId: bodyshop.id };
|
||||
|
||||
if (socketInstance.connected) {
|
||||
socketInstance.emit("update-token", { token, bodyshopId: bodyshop.id });
|
||||
}
|
||||
|
||||
socketInstance.disconnect();
|
||||
socketInstance.connect();
|
||||
return true;
|
||||
} catch (error) {
|
||||
console.error("Socket reconnect failed:", error?.message || error);
|
||||
return false;
|
||||
}
|
||||
},
|
||||
[bodyshop?.id]
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
const initializeSocket = async (token) => {
|
||||
if (!bodyshop?.id || socketRef.current) return;
|
||||
@@ -254,25 +280,60 @@ const SocketProvider = ({ children, bodyshop, navigate, currentUser }) => {
|
||||
}
|
||||
};
|
||||
|
||||
const syncCurrentTokenToSocket = async () => {
|
||||
try {
|
||||
if (!auth.currentUser || !bodyshop?.id) return;
|
||||
const token = await auth.currentUser.getIdToken();
|
||||
socketInstance.auth = { token, bodyshopId: bodyshop.id };
|
||||
socketInstance.emit("update-token", { token, bodyshopId: bodyshop.id });
|
||||
} catch (error) {
|
||||
console.error("Failed to sync token to socket:", error?.message || error);
|
||||
}
|
||||
};
|
||||
|
||||
const forceRefreshAndSyncToken = async () => {
|
||||
try {
|
||||
if (!auth.currentUser || !bodyshop?.id) return;
|
||||
const token = await auth.currentUser.getIdToken(true);
|
||||
socketInstance.auth = { token, bodyshopId: bodyshop.id };
|
||||
socketInstance.emit("update-token", { token, bodyshopId: bodyshop.id });
|
||||
} catch (error) {
|
||||
console.error("Failed to force-refresh token for socket:", error?.message || error);
|
||||
}
|
||||
};
|
||||
|
||||
const handleConnect = () => {
|
||||
socketInstance.emit("join-bodyshop-room", bodyshop.id);
|
||||
syncCurrentTokenToSocket();
|
||||
setClientId(socketInstance.id);
|
||||
setIsConnected(true);
|
||||
store.dispatch(setWssStatus("connected"));
|
||||
};
|
||||
|
||||
const handleReconnect = () => {
|
||||
forceRefreshAndSyncToken();
|
||||
setIsConnected(true);
|
||||
store.dispatch(setWssStatus("connected"));
|
||||
};
|
||||
|
||||
const handleTokenUpdated = ({ success, error }) => {
|
||||
if (success) return;
|
||||
const err = String(error || "");
|
||||
if (/stale token|id-token-expired/i.test(err)) {
|
||||
forceRefreshAndSyncToken();
|
||||
}
|
||||
};
|
||||
|
||||
const handleConnectionError = (err) => {
|
||||
console.error("Socket connection error:", err);
|
||||
setIsConnected(false);
|
||||
if (err.message.includes("auth/id-token-expired")) {
|
||||
if (err?.message?.includes("auth/id-token-expired")) {
|
||||
console.warn("Token expired, refreshing...");
|
||||
auth.currentUser?.getIdToken(true).then((newToken) => {
|
||||
socketInstance.auth = { token: newToken };
|
||||
socketInstance.auth = { token: newToken, bodyshopId: bodyshop.id };
|
||||
if (socketInstance.connected) {
|
||||
socketInstance.emit("update-token", { token: newToken, bodyshopId: bodyshop.id });
|
||||
}
|
||||
socketInstance.connect();
|
||||
});
|
||||
} else {
|
||||
@@ -513,10 +574,23 @@ const SocketProvider = ({ children, bodyshop, navigate, currentUser }) => {
|
||||
socketInstance.on("notification", handleNotification);
|
||||
socketInstance.on("sync-notification-read", handleSyncNotificationRead);
|
||||
socketInstance.on("sync-all-notifications-read", handleSyncAllNotificationsRead);
|
||||
socketInstance.on("token-updated", handleTokenUpdated);
|
||||
|
||||
if (tokenSyncIntervalRef.current) {
|
||||
clearInterval(tokenSyncIntervalRef.current);
|
||||
}
|
||||
tokenSyncIntervalRef.current = setInterval(() => {
|
||||
if (!socketInstance.connected) return;
|
||||
syncCurrentTokenToSocket();
|
||||
}, TOKEN_SYNC_INTERVAL_MS);
|
||||
};
|
||||
|
||||
const unsubscribe = auth.onIdTokenChanged(async (user) => {
|
||||
if (!user) {
|
||||
if (tokenSyncIntervalRef.current) {
|
||||
clearInterval(tokenSyncIntervalRef.current);
|
||||
tokenSyncIntervalRef.current = null;
|
||||
}
|
||||
socketRef.current?.disconnect();
|
||||
socketRef.current = null;
|
||||
setIsConnected(false);
|
||||
@@ -525,7 +599,10 @@ const SocketProvider = ({ children, bodyshop, navigate, currentUser }) => {
|
||||
|
||||
const token = await user.getIdToken();
|
||||
if (socketRef.current) {
|
||||
socketRef.current.emit("update-token", { token, bodyshopId: bodyshop.id });
|
||||
socketRef.current.auth = { token, bodyshopId: bodyshop.id };
|
||||
if (socketRef.current.connected) {
|
||||
socketRef.current.emit("update-token", { token, bodyshopId: bodyshop.id });
|
||||
}
|
||||
} else {
|
||||
initializeSocket(token).catch((err) =>
|
||||
console.error("Something went wrong Initializing Sockets:", err?.message || "")
|
||||
@@ -535,6 +612,10 @@ const SocketProvider = ({ children, bodyshop, navigate, currentUser }) => {
|
||||
|
||||
return () => {
|
||||
unsubscribe();
|
||||
if (tokenSyncIntervalRef.current) {
|
||||
clearInterval(tokenSyncIntervalRef.current);
|
||||
tokenSyncIntervalRef.current = null;
|
||||
}
|
||||
if (socketRef.current) {
|
||||
socketRef.current.disconnect();
|
||||
socketRef.current = null;
|
||||
@@ -549,6 +630,7 @@ const SocketProvider = ({ children, bodyshop, navigate, currentUser }) => {
|
||||
socket: socketRef.current,
|
||||
clientId,
|
||||
isConnected,
|
||||
reconnectSocket,
|
||||
markNotificationRead,
|
||||
markAllNotificationsRead,
|
||||
scenarioNotificationsOn: Realtime_Notifications_UI?.treatment === "on"
|
||||
|
||||
Reference in New Issue
Block a user