This commit is contained in:
2026-02-13 19:04:07 +01:00
parent 9505331165
commit 69d432b6af
42 changed files with 650 additions and 360 deletions
+1 -1
View File
@@ -6,7 +6,7 @@ type Params = {
let timeout: any;
export default function twuiUseReady(params?: Params) {
export default function useReady(params?: Params) {
const [ready, setReady] = React.useState(false);
const finalTimeout = params?.timeout || 300;
+31
View File
@@ -0,0 +1,31 @@
import React from "react";
type Params = {
initialLoading?: boolean;
initialReady?: boolean;
};
export type UseStatusStatusType = {
msg?: string;
error?: boolean;
};
export default function useStatus(params?: Params) {
const [refresh, setRefresh] = React.useState(0);
const [loading, setLoading] = React.useState(
params?.initialLoading || false
);
const [status, setStatus] = React.useState<UseStatusStatusType>({});
const [ready, setReady] = React.useState(params?.initialReady || false);
return {
refresh,
setRefresh,
loading,
setLoading,
status,
setStatus,
ready,
setReady,
};
}
+72 -111
View File
@@ -1,17 +1,16 @@
import React from "react";
import React, { useRef } from "react";
export type UseWebsocketHookParams = {
debounce?: number;
url: string;
disableReconnect?: boolean;
/** Interval to ping the websocket. So that the connection doesn't go down. Default 30000ms (30 seconds) */
keepAliveDuration?: number;
refreshConnection?: number;
};
export const WebSocketEventNames = ["wsDataEvent", "wsMessageEvent"] as const;
let tries = 0;
/**
* # Use Websocket Hook
* @event wsDataEvent Listen for event named `wsDataEvent` on `window` to receive Data events
@@ -33,18 +32,20 @@ export default function useWebSocket<
keepAliveDuration,
refreshConnection,
}: UseWebsocketHookParams) {
const DEBOUNCE = debounce || 200;
const DEBOUNCE = debounce || 500;
const KEEP_ALIVE_DURATION = keepAliveDuration || 1000 * 30;
const KEEP_ALIVE_TIMEOUT = 1000 * 60 * 3;
const KEEP_ALIVE_MESSAGE = "twui::ping";
let uptime = 0;
let tries = useRef(0);
let reconnectInterval: any;
let msgInterval: any;
let sendInterval: any;
let keepAliveInterval: any;
// const queue: string[] = [];
const msgInterval = useRef<any>(null);
const sendInterval = useRef<any>(null);
const keepAliveInterval = useRef<any>(null);
const [socket, setSocket] = React.useState<WebSocket | undefined>(
undefined
@@ -77,141 +78,94 @@ export default function useWebSocket<
const wsURL = url.startsWith(`ws`)
? url
: domain.replace(/^http/, "ws") + ("/" + url).replace(/\/\//g, "/");
if (!wsURL) return;
let ws = new WebSocket(wsURL);
ws.onopen = (ev) => {
window.clearInterval(reconnectInterval);
window.clearInterval(keepAliveInterval);
keepAliveInterval = setInterval(() => {
if (ws.readyState === WebSocket.OPEN) {
ws.send(KEEP_ALIVE_MESSAGE);
uptime += KEEP_ALIVE_DURATION;
if (uptime >= KEEP_ALIVE_TIMEOUT) {
console.log("Websocket connection timed out ...");
window.clearInterval(keepAliveInterval);
ws.close();
}
}
}, KEEP_ALIVE_DURATION);
setSocket(ws);
tries = 0;
console.log(`Websocket connected to ${wsURL}`);
uptime = 0;
ws.onerror = (ev) => {
console.log(`Websocket ERROR:`);
};
ws.onmessage = (ev) => {
window.clearInterval(msgInterval);
messageQueueRef.current.push(ev.data);
msgInterval = setInterval(handleReceivedMessageQueue, DEBOUNCE);
if (ev.data !== KEEP_ALIVE_MESSAGE) {
uptime = 0;
}
};
ws.onopen = (ev) => {
window.clearInterval(keepAliveInterval.current);
keepAliveInterval.current = window.setInterval(() => {
if (ws.readyState === WebSocket.OPEN) {
ws.send(KEEP_ALIVE_MESSAGE);
}
}, KEEP_ALIVE_DURATION);
setSocket(ws);
console.log(`Websocket connected to ${wsURL}`);
};
ws.onclose = (ev) => {
console.log("Websocket closed!");
console.log("Websocket closed!", {
code: ev.code,
reason: ev.reason,
wasClean: ev.wasClean,
});
if (disableReconnect) return;
console.log("Attempting to reconnect ...");
console.log("URL:", url);
window.clearInterval(keepAliveInterval);
window.clearInterval(keepAliveInterval.current);
reconnectInterval = setInterval(() => {
if (tries >= 3) {
return window.clearInterval(reconnectInterval);
}
console.log("tries", tries);
console.log("Attempting to reconnect ...");
tries++;
if (tries.current >= 3) {
return;
}
connect();
}, 1000);
console.log("Attempting to reconnect ...");
tries.current += 1;
connect();
};
}, []);
/**
* # Window Close Handler
*/
const handleWindowClose = React.useCallback(() => {
console.log("Window Unloaded ...");
}, [socket]);
/**
* # Window Focus Handler
*/
const handleWindowFocus = React.useCallback(() => {
if (socket?.readyState === WebSocket.CLOSED) {
console.log("Websocket closed ... Attempting to reconnect ...");
connect();
}
if (socket?.readyState === WebSocket.OPEN) {
console.log("Websocket connection alive ...");
socket.send(KEEP_ALIVE_MESSAGE);
uptime = 0;
}
}, [socket]);
/**
* # Initial Connection
*/
React.useEffect(() => {
connect();
if (socket) return;
return function () {
window.clearInterval(reconnectInterval);
};
connect();
}, []);
/**
* # Window Close and Focus Handlers
*/
React.useEffect(() => {
if (!socket) return;
window.addEventListener("beforeunload", handleWindowClose, {
once: true,
});
window.addEventListener("focus", handleWindowFocus);
sendInterval.current = setInterval(handleSendMessageQueue, DEBOUNCE);
msgInterval.current = setInterval(handleReceivedMessageQueue, DEBOUNCE);
return function () {
window.removeEventListener("focus", handleWindowFocus);
window.removeEventListener("beforeunload", handleWindowClose);
window.clearInterval(sendInterval.current);
window.clearInterval(msgInterval.current);
};
}, [socket]);
/**
* # Refresh Connection
*/
React.useEffect(() => {
console.log("Refreshing connection ...");
if (!socket) return;
if (socket.readyState !== WebSocket.CLOSED) {
socket?.close();
}
connect();
}, [refreshConnection]);
/**
* Received Message Queue Handler
*/
const handleReceivedMessageQueue = React.useCallback(() => {
if (messageQueueRef.current.length > 0) {
const newMessage = messageQueueRef.current.shift();
if (!newMessage) return;
try {
const jsonData = JSON.parse(newMessage);
dispatchCustomEvent("wsMessageEvent", newMessage);
dispatchCustomEvent("wsDataEvent", jsonData);
} catch (error) {
console.log("Unable to parse string. Returning string.");
}
} else {
window.clearInterval(msgInterval);
uptime = 0;
try {
const msg = messageQueueRef.current.shift();
if (!msg) return;
const jsonData = JSON.parse(msg);
dispatchCustomEvent("wsMessageEvent", msg);
dispatchCustomEvent("wsDataEvent", jsonData);
} catch (error) {
console.log("Unable to parse string. Returning string.");
}
}, []);
@@ -219,13 +173,15 @@ export default function useWebSocket<
* Send Message Queue Handler
*/
const handleSendMessageQueue = React.useCallback(() => {
if (sendMessageQueueRef.current.length > 0) {
const newMessage = sendMessageQueueRef.current.shift();
if (!newMessage) return;
socket?.send(newMessage);
} else {
window.clearInterval(sendInterval);
if (!socket || socket.readyState !== WebSocket.OPEN) {
window.clearInterval(sendInterval.current);
return;
}
const newMessage = sendMessageQueueRef.current.shift();
if (!newMessage) return;
socket.send(newMessage);
}, [socket]);
/**
@@ -234,9 +190,14 @@ export default function useWebSocket<
const sendData = React.useCallback(
(data: T) => {
try {
window.clearInterval(sendInterval);
sendMessageQueueRef.current.push(JSON.stringify(data));
sendInterval = setInterval(handleSendMessageQueue, DEBOUNCE);
const queueItemJSON = JSON.stringify(data);
const existingQueue = sendMessageQueueRef.current.find(
(q) => q == queueItemJSON
);
if (!existingQueue) {
sendMessageQueueRef.current.push(queueItemJSON);
}
} catch (error: any) {
console.log("Error Sending socket message", error.message);
}
+24
View File
@@ -0,0 +1,24 @@
import { useCallback, useEffect, useState } from "react";
export default function useWindowFocus() {
const [isWindowFocused, setIsWindowFocused] = useState(false);
const windowFocusCb = useCallback(() => {
setIsWindowFocused(true);
}, []);
const windowBlurCb = useCallback(() => {
setIsWindowFocused(false);
}, []);
useEffect(() => {
window.addEventListener("focus", windowFocusCb);
window.addEventListener("blur", windowBlurCb);
return function () {
window.removeEventListener("focus", windowFocusCb);
window.removeEventListener("blur", windowBlurCb);
};
}, []);
return { isWindowFocused };
}