This commit is contained in:
Benjamin Toby
2025-01-05 07:25:38 +01:00
parent 998158369a
commit 5587024789
30 changed files with 4756 additions and 75 deletions
@@ -0,0 +1,66 @@
import React from "react";
type Param = {
elementRef?: React.MutableRefObject<Element | undefined>;
className?: string;
options?: IntersectionObserverInit;
removeIntersected?: boolean;
};
export default function useIntersectionObserver({
elementRef,
className,
options,
removeIntersected,
}: Param) {
const [isIntersecting, setIsIntersecting] = React.useState(false);
const [refresh, setRefresh] = React.useState(0);
const observerCallback: IntersectionObserverCallback = React.useCallback(
(entries, observer) => {
const entry = entries[0];
if (entry.isIntersecting) {
setIsIntersecting(true);
if (removeIntersected) {
observer.unobserve(entry.target);
}
} else {
setIsIntersecting(false);
}
},
[]
);
React.useEffect(() => {
const element = elementRef?.current;
const elements = className
? document.querySelectorAll(`.${className}`)
: null;
if (!element && !className && refresh < 5) {
requestAnimationFrame(() => {
setTimeout(() => {
setRefresh(refresh + 1);
}, 2000);
});
return;
}
const observer = new IntersectionObserver(observerCallback, {
rootMargin: "0px 0px 0px 0px",
...options,
});
if (elements) {
elements.forEach((el) => {
observer.observe(el);
});
} else if (element) {
observer.observe(element);
}
}, [refresh]);
return { isIntersecting };
}
+25
View File
@@ -0,0 +1,25 @@
import { LocalStorageDict } from "@/dict/local-storage-dict";
import { DATASQUIREL_LoggedInUser } from "@moduletrace/datasquirel/package-shared/types";
import React from "react";
export default function useLocalUser() {
const [user, setUser] = React.useState<
DATASQUIREL_LoggedInUser | null | undefined
>(undefined);
React.useEffect(() => {
try {
const localUserJSON = localStorage.getItem(
LocalStorageDict["User"]
);
if (localUserJSON) {
const localUser = JSON.parse(localUserJSON);
setUser(localUser);
}
} catch (error) {
setUser(null);
}
}, []);
return { user };
}
+144
View File
@@ -0,0 +1,144 @@
import React from "react";
export type UseWebsocketHookParams = {
debounce?: number;
url: string;
};
let reconnectInterval: any;
let msgInterval: any;
let sendInterval: any;
let tries = 0;
export const WebSocketEventNames = ["wsDataEvent", "wsMessageEvent"] as const;
/**
* # Use Websocket Hook
* @event wsDataEvent Listen for event named `wsDataEvent` on `window` to receive Data events
* @event wsMessageEvent Listen for event named `wsMessageEvent` on `window` to receive Message events
*
* @example window.addEventLiatener("wsDataEvent", (e)=>{
* console.log(e.detail.data) // type object
* })
* @example window.addEventLiatener("wsMessageEvent", (e)=>{
* console.log(e.detail.message) // type string
* })
*/
export default function useWebSocket<T>({
url,
debounce,
}: UseWebsocketHookParams) {
const DEBOUNCE = debounce || 200;
const [socket, setSocket] = React.useState<WebSocket | undefined>(
undefined
);
const messageQueueRef = React.useRef<string[]>([]);
const sendMessageQueueRef = React.useRef<string[]>([]);
// const [message, setMessage] = React.useState<string>("");
// const [data, setData] = React.useState<T | null>(null);
const [refresh, setRefresh] = React.useState(0);
const dispatchCustomEvent = React.useCallback(
(evtName: (typeof WebSocketEventNames)[number], value: string | T) => {
const event = new CustomEvent(evtName, {
detail: {
data: value,
message: value,
},
});
window.dispatchEvent(event);
},
[]
);
React.useEffect(() => {
const wsURL = url;
if (!wsURL) return;
const ws = new WebSocket(wsURL);
ws.onopen = (ev) => {
window.clearInterval(reconnectInterval);
setSocket(ws);
tries = 0;
};
ws.onmessage = (ev) => {
window.clearInterval(msgInterval);
messageQueueRef.current.push(ev.data);
msgInterval = setInterval(handleReceivedMessageQueue, DEBOUNCE);
};
ws.onclose = (ev) => {
console.log("Websocket closed ... Attempting to reconnect ...");
reconnectInterval = setInterval(() => {
if (tries >= 3) {
return window.clearInterval(reconnectInterval);
}
console.log("Attempting to reconnect ...");
setRefresh(refresh + 1);
tries++;
}, 1000);
};
return function () {
window.clearInterval(reconnectInterval);
};
}, [refresh]);
/**
* 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);
// setData(jsonData);
dispatchCustomEvent("wsMessageEvent", newMessage);
dispatchCustomEvent("wsDataEvent", jsonData);
} catch (error) {
console.log("Unable to parse string. Returning string.");
}
} else {
window.clearInterval(msgInterval);
}
}, []);
/**
* 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);
}
}, [socket]);
/**
* # Send Data Function
*/
const sendData = React.useCallback(
(data: T) => {
try {
window.clearInterval(sendInterval);
sendMessageQueueRef.current.push(JSON.stringify(data));
sendInterval = setInterval(handleSendMessageQueue, DEBOUNCE);
} catch (error: any) {
console.log("Error Sending socket message", error.message);
}
},
[socket]
);
return { socket, sendData };
}