1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
|
/**
* Connection-related utility functions and management
*/
import { getBaseUrl } from "./utils.js";
// Connection state
let reconnectionAttempt = 0;
let isReconnecting = false;
let isDisconnected = false;
let reconnectionTimeout = null;
let hasConnectedBefore = false;
let isPageUnloading = false;
/**
* Get the delay for reconnection attempts using exponential backoff
* @param {number} attempt - The current attempt number (1-based)
* @returns {number} The delay in seconds
*/
export function getReconnectionDelay(attempt) {
const delays = [1, 2, 4, 8, 16];
return delays[Math.min(attempt - 1, delays.length - 1)];
}
/**
* Check if the server connection is available
* @returns {Promise<boolean>} true if connection is OK, false otherwise
*/
export async function checkConnection() {
try {
const baseUrl = getBaseUrl();
const response = await fetch(`${baseUrl}/info/version`, {
method: "GET",
timeout: 5000,
});
return response.ok;
} catch (error) {
return false;
}
}
/**
* Handle intentional disconnection by the host (close code 4001)
* @returns {Promise<void>}
*/
export async function handleDisconnected() {
if (isDisconnected || isPageUnloading) {
return;
}
isDisconnected = true;
await showErrorModal("Disconnected", "You have been disconnected by the host.");
isDisconnected = false;
}
/**
* Handle reconnection attempts with exponential backoff
* @returns {Promise<void>}
*/
export async function handleReconnection() {
if (isReconnecting || !hasConnectedBefore || isPageUnloading) {
return;
}
isReconnecting = true;
let currentModal = null;
while (isReconnecting) {
reconnectionAttempt++;
const delaySeconds = getReconnectionDelay(reconnectionAttempt);
const result = await showReconnectionModal(
reconnectionAttempt,
delaySeconds
);
if (result.action === "cancel") {
if (result.cleanup) result.cleanup();
isReconnecting = false;
reconnectionAttempt = 0;
return;
}
if (result.action === "reconnect") {
currentModal = result.modal;
const connectionOk = await checkConnection();
if (connectionOk) {
if (result.cleanup) result.cleanup();
isReconnecting = false;
reconnectionAttempt = 0;
window.location.reload();
return;
} else {
if (result.cleanup) result.cleanup();
continue;
}
}
}
}
/**
* Initialize connection handlers and event listeners
*/
export function initConnectionHandlers() {
window.addEventListener("beforeunload", () => {
isPageUnloading = true;
});
window.addEventListener("pagehide", () => {
isPageUnloading = true;
});
}
/**
* Mark that a connection has been established
*/
export function markConnectionEstablished() {
hasConnectedBefore = true;
}
/**
* Reset connection state
*/
export function resetConnectionState() {
reconnectionAttempt = 0;
isReconnecting = false;
isDisconnected = false;
reconnectionTimeout = null;
hasConnectedBefore = false;
isPageUnloading = false;
}
|