2fa8a7f16a
* [#451] added websocket connexion * [#451] added tests * [#451] add missing cleanup for web socket causing memory leaks
105 lines
2.9 KiB
TypeScript
105 lines
2.9 KiB
TypeScript
import { useEffect, useRef, useState } from "react";
|
|
import { useAppSelector } from "../app/hooks";
|
|
import { useSelectedCalendars } from "../utils/storage/useSelectedCalendars";
|
|
import {
|
|
createWebSocketConnection,
|
|
WebSocketWithCleanup,
|
|
} from "./createWebSocketConnection";
|
|
import { registerToCalendars } from "./ws/registerToCalendars";
|
|
import { unregisterToCalendars } from "./ws/unregisterToCalendars";
|
|
|
|
export function WebSocketGate() {
|
|
const socketRef = useRef<WebSocketWithCleanup | null>(null);
|
|
const previousCalendarListRef = useRef<string[]>([]);
|
|
|
|
const isAuthenticated = useAppSelector((state) =>
|
|
Boolean(state.user.userData && state.user.tokens)
|
|
);
|
|
const [isSocketOpen, setIsSocketOpen] = useState(false);
|
|
|
|
const calendarList = useSelectedCalendars();
|
|
|
|
// Manage WebSocket connection
|
|
useEffect(() => {
|
|
if (!isAuthenticated) {
|
|
if (socketRef.current) {
|
|
socketRef.current.cleanup();
|
|
socketRef.current.close();
|
|
socketRef.current = null;
|
|
setIsSocketOpen(false);
|
|
}
|
|
return;
|
|
}
|
|
|
|
const connect = async () => {
|
|
try {
|
|
const socket = await createWebSocketConnection();
|
|
socketRef.current = socket;
|
|
socket.addEventListener("close", () => {
|
|
setIsSocketOpen(false);
|
|
socketRef.current = null;
|
|
});
|
|
// Check if socket closed during setup
|
|
if (socket.readyState === WebSocket.OPEN) {
|
|
setIsSocketOpen(true);
|
|
}
|
|
} catch (error) {
|
|
console.error("Failed to create WebSocket connection:", error);
|
|
}
|
|
};
|
|
|
|
connect();
|
|
|
|
return () => {
|
|
if (socketRef.current) {
|
|
socketRef.current.cleanup();
|
|
socketRef.current.close();
|
|
socketRef.current = null;
|
|
setIsSocketOpen(false);
|
|
}
|
|
};
|
|
}, [isAuthenticated]);
|
|
|
|
// Register using a diff with previous calendars
|
|
useEffect(() => {
|
|
if (
|
|
!isSocketOpen ||
|
|
!socketRef.current ||
|
|
socketRef.current.readyState !== WebSocket.OPEN
|
|
)
|
|
return;
|
|
|
|
const currentPaths = calendarList.map((cal) => `/calendars/${cal}`);
|
|
const previousPaths = previousCalendarListRef.current.map(
|
|
(cal) => `/calendars/${cal}`
|
|
);
|
|
|
|
// calendars to register
|
|
const toRegister = currentPaths.filter(
|
|
(path) => !previousPaths.includes(path)
|
|
);
|
|
|
|
// calendars to unregister
|
|
const toUnregister = previousPaths.filter(
|
|
(path) => !currentPaths.includes(path)
|
|
);
|
|
|
|
try {
|
|
if (toRegister.length > 0) {
|
|
registerToCalendars(socketRef.current, toRegister);
|
|
}
|
|
|
|
if (toUnregister.length > 0) {
|
|
unregisterToCalendars(socketRef.current, toUnregister);
|
|
}
|
|
|
|
// Only update the ref if operations succeeded
|
|
previousCalendarListRef.current = calendarList;
|
|
} catch (error) {
|
|
console.error("Failed to update calendar registrations:", error);
|
|
}
|
|
}, [isSocketOpen, calendarList]);
|
|
|
|
return null;
|
|
}
|