|
@@ -61,6 +61,18 @@ export function useDispatchWithEffects(
|
|
|
return dispatchWithEffects;
|
|
return dispatchWithEffects;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+const getConnectAttemptDelayMs = (connectAttemptNumber: number): number =>
|
|
|
|
|
+ Math.min(16000, 1000 * 2 ** connectAttemptNumber);
|
|
|
|
|
+
|
|
|
|
|
+type SocketState = {
|
|
|
|
|
+ uniqueName: string;
|
|
|
|
|
+ tempName: string;
|
|
|
|
|
+ socket: WebSocket | null;
|
|
|
|
|
+ error: boolean;
|
|
|
|
|
+ connecting: boolean;
|
|
|
|
|
+ connectAttemptNumber: number;
|
|
|
|
|
+};
|
|
|
|
|
+
|
|
|
export function useSocket(
|
|
export function useSocket(
|
|
|
onMessage: OnMessage,
|
|
onMessage: OnMessage,
|
|
|
onLogin: (name: string) => void,
|
|
onLogin: (name: string) => void,
|
|
@@ -73,33 +85,46 @@ export function useSocket(
|
|
|
connected: boolean;
|
|
connected: boolean;
|
|
|
} {
|
|
} {
|
|
|
const [storedName, saveName] = useStorageState<string>(localStorage, clientNameKey, '');
|
|
const [storedName, saveName] = useStorageState<string>(localStorage, clientNameKey, '');
|
|
|
- const [uniqueName, setUniqueName] = useState<string>(getUniqueName(storedName));
|
|
|
|
|
- const [tempName, onIdentify] = useState<string>(storedName);
|
|
|
|
|
|
|
+ const [{ uniqueName, tempName, socket, error, connecting }, setState] = useState<SocketState>({
|
|
|
|
|
+ uniqueName: getUniqueName(storedName),
|
|
|
|
|
+ tempName: storedName,
|
|
|
|
|
+ socket: null,
|
|
|
|
|
+ error: false,
|
|
|
|
|
+ connecting: false,
|
|
|
|
|
+ connectAttemptNumber: 0,
|
|
|
|
|
+ });
|
|
|
|
|
|
|
|
- const [socket, setSocket] = useState<WebSocket | null>(null);
|
|
|
|
|
- const [error, setError] = useState<boolean>(false);
|
|
|
|
|
-
|
|
|
|
|
- const [connecting, setConnecting] = useState<boolean>(false);
|
|
|
|
|
|
|
+ const connectAttemptTimer = useRef<number>();
|
|
|
|
|
|
|
|
useEffect(() => {
|
|
useEffect(() => {
|
|
|
|
|
+ if (!tempName) {
|
|
|
|
|
+ setState((last) => ({ ...last, connecting: false, error: false }));
|
|
|
|
|
+ return (): void => {
|
|
|
|
|
+ // pass
|
|
|
|
|
+ };
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
let cancelled = false;
|
|
let cancelled = false;
|
|
|
let ws: WebSocket | undefined;
|
|
let ws: WebSocket | undefined;
|
|
|
- if (tempName) {
|
|
|
|
|
- setConnecting(true);
|
|
|
|
|
|
|
+
|
|
|
|
|
+ const connect = (): void => {
|
|
|
|
|
+ setState((last) => ({ ...last, connecting: true }));
|
|
|
|
|
|
|
|
const uniqueTempName = getUniqueName(tempName);
|
|
const uniqueTempName = getUniqueName(tempName);
|
|
|
ws = new WebSocket(`${getPubsubUrl()}?client-name=${uniqueTempName}`);
|
|
ws = new WebSocket(`${getPubsubUrl()}?client-name=${uniqueTempName}`);
|
|
|
|
|
|
|
|
ws.onopen = (): void => {
|
|
ws.onopen = (): void => {
|
|
|
if (!cancelled && ws && ws.readyState === ws.OPEN) {
|
|
if (!cancelled && ws && ws.readyState === ws.OPEN) {
|
|
|
- setError(false);
|
|
|
|
|
- setConnecting(false);
|
|
|
|
|
|
|
+ setState((last) => ({
|
|
|
|
|
+ ...last,
|
|
|
|
|
+ error: false,
|
|
|
|
|
+ connecting: false,
|
|
|
|
|
+ connectAttemptNumber: 0,
|
|
|
|
|
+ uniqueName: uniqueTempName,
|
|
|
|
|
+ socket: ws ?? null,
|
|
|
|
|
+ }));
|
|
|
|
|
|
|
|
- onIdentify('');
|
|
|
|
|
saveName(tempName);
|
|
saveName(tempName);
|
|
|
- setUniqueName(uniqueTempName);
|
|
|
|
|
-
|
|
|
|
|
- setSocket(ws);
|
|
|
|
|
onLogin(uniqueTempName);
|
|
onLogin(uniqueTempName);
|
|
|
}
|
|
}
|
|
|
};
|
|
};
|
|
@@ -107,19 +132,35 @@ export function useSocket(
|
|
|
ws.onmessage = onMessage;
|
|
ws.onmessage = onMessage;
|
|
|
|
|
|
|
|
ws.onclose = (): void => {
|
|
ws.onclose = (): void => {
|
|
|
- setError(false);
|
|
|
|
|
- setSocket(null);
|
|
|
|
|
|
|
+ setState((last) => ({ ...last, error: false, socket: null }));
|
|
|
};
|
|
};
|
|
|
- } else {
|
|
|
|
|
- setConnecting(false);
|
|
|
|
|
- setError(false);
|
|
|
|
|
- }
|
|
|
|
|
|
|
+
|
|
|
|
|
+ ws.onerror = (): void => {
|
|
|
|
|
+ setState((last) => {
|
|
|
|
|
+ clearTimeout(connectAttemptTimer.current);
|
|
|
|
|
+ connectAttemptTimer.current = setTimeout(
|
|
|
|
|
+ connect,
|
|
|
|
|
+ getConnectAttemptDelayMs(last.connectAttemptNumber),
|
|
|
|
|
+ );
|
|
|
|
|
+
|
|
|
|
|
+ return { ...last, error: true, connectAttemptNumber: last.connectAttemptNumber + 1 };
|
|
|
|
|
+ });
|
|
|
|
|
+ };
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
|
|
+ connect();
|
|
|
|
|
|
|
|
return (): void => {
|
|
return (): void => {
|
|
|
|
|
+ clearTimeout(connectAttemptTimer.current);
|
|
|
cancelled = true;
|
|
cancelled = true;
|
|
|
};
|
|
};
|
|
|
}, [onMessage, onLogin, tempName, saveName]);
|
|
}, [onMessage, onLogin, tempName, saveName]);
|
|
|
|
|
|
|
|
|
|
+ const onIdentify = useCallback(
|
|
|
|
|
+ (name: string) => setState((last) => ({ ...last, tempName: name })),
|
|
|
|
|
+ [],
|
|
|
|
|
+ );
|
|
|
|
|
+
|
|
|
return {
|
|
return {
|
|
|
name: uniqueName,
|
|
name: uniqueName,
|
|
|
onIdentify,
|
|
onIdentify,
|