Build and deployment update: Refined registration forms, stepper navigation, and chat synchronization fixes
This commit is contained in:
1 parent
68f97c40dc
commit
c467271927
27 files changed
+3461
-4178
No files matched your search
@@ -0,0 +1,168 @@
|
||||
import { useEffect, useRef, useState } from 'react';
|
||||
|
||||
const WS_URL = "wss://www.thirukalyanam.amrithaa.net/backend/reverb/app/xk30gjh2ggmel5szmm5w?protocol=7&client=js&version=1.0";
|
||||
|
||||
// SINGLETON state to share across all components
|
||||
let globalSocket = null;
|
||||
let globalMessages = [];
|
||||
let globalIsConnected = false;
|
||||
let globalActiveChannels = new Set();
|
||||
let subscribers = new Set();
|
||||
let reconnectTimer = null;
|
||||
let heartbeatTimer = null;
|
||||
|
||||
const notifySubscribers = () => {
|
||||
subscribers.forEach(callback => callback({
|
||||
messages: [...globalMessages],
|
||||
isConnected: globalIsConnected
|
||||
}));
|
||||
};
|
||||
|
||||
const connect = () => {
|
||||
if (globalSocket?.readyState === WebSocket.OPEN) return;
|
||||
if (globalSocket?.readyState === WebSocket.CONNECTING) return;
|
||||
|
||||
console.log("[WS] Connecting to:", WS_URL);
|
||||
const socket = new WebSocket(WS_URL);
|
||||
globalSocket = socket;
|
||||
|
||||
socket.onopen = () => {
|
||||
console.log("[WS] Open - Waiting for handshake...");
|
||||
};
|
||||
|
||||
socket.onmessage = (event) => {
|
||||
try {
|
||||
const data = JSON.parse(event.data);
|
||||
console.log("[WS] RAW:", data);
|
||||
|
||||
// 1. Handshake
|
||||
if (data.event === "pusher:connection_established") {
|
||||
console.log("[WS] Handshake Complete | Socket ID:", data.data ? JSON.parse(data.data).socket_id : "N/A");
|
||||
globalIsConnected = true;
|
||||
|
||||
// Auto-subscribe to all pending channels
|
||||
if (globalActiveChannels.size > 0) {
|
||||
console.log(`[WS] Re-subscribing to ${globalActiveChannels.size} channels...`);
|
||||
globalActiveChannels.forEach(channel => {
|
||||
socket.send(JSON.stringify({
|
||||
event: "pusher:subscribe",
|
||||
data: { channel }
|
||||
}));
|
||||
console.log(`[WS] Subscribing to: ${channel}`);
|
||||
});
|
||||
}
|
||||
|
||||
notifySubscribers();
|
||||
return;
|
||||
}
|
||||
|
||||
// 2. Heartbeat
|
||||
if (data.event === "pusher:ping") {
|
||||
socket.send(JSON.stringify({ event: "pusher:pong", data: {} }));
|
||||
return;
|
||||
}
|
||||
if (data.event === "pusher:pong") return;
|
||||
|
||||
// 3. Subscription Succeeded
|
||||
if (data.event === "pusher_internal:subscription_succeeded") {
|
||||
console.log(`[WS] ✅ Subscribed to ${data.channel}`);
|
||||
return;
|
||||
}
|
||||
|
||||
// 4. Subscription Error
|
||||
if (data.event === "pusher:subscription_error") {
|
||||
console.error(`[WS] ❌ Subscription failed for ${data.channel}`, data.data);
|
||||
return;
|
||||
}
|
||||
|
||||
// 5. Regular Events
|
||||
console.log(`[WS] 📩 Event: ${data.event} | Channel: ${data.channel}`);
|
||||
globalMessages = [...globalMessages, data];
|
||||
notifySubscribers();
|
||||
} catch (err) {
|
||||
console.error("[WS] Parse Error:", err, event.data);
|
||||
}
|
||||
};
|
||||
|
||||
socket.onclose = (event) => {
|
||||
console.log(`[WS] Disconnected (Code: ${event.code}) - Reconnecting in 5s...`);
|
||||
globalIsConnected = false;
|
||||
notifySubscribers();
|
||||
|
||||
if (reconnectTimer) clearTimeout(reconnectTimer);
|
||||
reconnectTimer = setTimeout(connect, 5000);
|
||||
};
|
||||
|
||||
socket.onerror = (err) => {
|
||||
console.error("[WS] Error:", err);
|
||||
socket.close();
|
||||
};
|
||||
|
||||
// Heartbeat
|
||||
if (heartbeatTimer) clearInterval(heartbeatTimer);
|
||||
heartbeatTimer = setInterval(() => {
|
||||
if (socket.readyState === WebSocket.OPEN) {
|
||||
socket.send(JSON.stringify({ event: "pusher:ping", data: {} }));
|
||||
}
|
||||
}, 20000);
|
||||
};
|
||||
|
||||
export const useWebSocket = (channels = []) => {
|
||||
const [state, setState] = useState({
|
||||
messages: globalMessages,
|
||||
isConnected: globalIsConnected
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
// Add this component's listener
|
||||
const callback = (newState) => setState(newState);
|
||||
subscribers.add(callback);
|
||||
|
||||
// Initialize connection if needed
|
||||
if (!globalSocket) {
|
||||
connect();
|
||||
}
|
||||
|
||||
return () => {
|
||||
subscribers.delete(callback);
|
||||
};
|
||||
}, []);
|
||||
|
||||
// Handle Dynamic Subscriptions
|
||||
useEffect(() => {
|
||||
if (!channels || channels.length === 0) return;
|
||||
|
||||
channels.forEach(channel => {
|
||||
if (channel && !globalActiveChannels.has(channel)) {
|
||||
globalActiveChannels.add(channel);
|
||||
|
||||
if (globalIsConnected && globalSocket?.readyState === WebSocket.OPEN) {
|
||||
globalSocket.send(JSON.stringify({
|
||||
event: "pusher:subscribe",
|
||||
data: { channel }
|
||||
}));
|
||||
console.log(`[WS] Dynamic Subscribe: ${channel}`);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Optional: Cleanup old channels if they are no longer in the provided array
|
||||
// But for a chat app, we often want to keep listening to notification channels.
|
||||
// So we'll leave them for now unless we implement a more complex cleanup.
|
||||
}, [channels]);
|
||||
|
||||
const unsubscribe = (channel) => {
|
||||
if (channel && globalActiveChannels.has(channel)) {
|
||||
globalActiveChannels.delete(channel);
|
||||
if (globalIsConnected && globalSocket?.readyState === WebSocket.OPEN) {
|
||||
globalSocket.send(JSON.stringify({
|
||||
event: "pusher:unsubscribe",
|
||||
data: { channel }
|
||||
}));
|
||||
console.log(`[WS] Unsubscribed from: ${channel}`);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
return { ...state, unsubscribe };
|
||||
};
|
||||
Reference in new issue
Block a user