ux: FIX reconnect after leaving the page and returning #32
This commit is contained in:
parent
ba36703a71
commit
52f870f33b
1 changed files with 37 additions and 1 deletions
38
app.py
38
app.py
|
|
@ -389,6 +389,9 @@ $("#padname").textContent = "/"+docId+"/";
|
||||||
|
|
||||||
let ws, ver = 0, clientId = Math.random().toString(36).slice(2), debounce;
|
let ws, ver = 0, clientId = Math.random().toString(36).slice(2), debounce;
|
||||||
let isProtected = false, isAuthed = false;
|
let isProtected = false, isAuthed = false;
|
||||||
|
// dirty: local edits the server has not acknowledged. Guards the reconnect path
|
||||||
|
// from overwriting text typed while the socket was down.
|
||||||
|
let dirty = false, reconnectTimer = null, reconnectDelay = 500;
|
||||||
const urlPw = new URLSearchParams(location.search).get("pw") || "";
|
const urlPw = new URLSearchParams(location.search).get("pw") || "";
|
||||||
|
|
||||||
// --- Line numbers ---
|
// --- Line numbers ---
|
||||||
|
|
@ -572,6 +575,7 @@ function connect(){
|
||||||
$("#status").classList.remove("connected");
|
$("#status").classList.remove("connected");
|
||||||
ws = new WebSocket(`${proto}://${location.host}/ws/${encodeURIComponent(docId)}`);
|
ws = new WebSocket(`${proto}://${location.host}/ws/${encodeURIComponent(docId)}`);
|
||||||
ws.onopen = () => {
|
ws.onopen = () => {
|
||||||
|
reconnectDelay = 500;
|
||||||
$("#status").textContent = "connected";
|
$("#status").textContent = "connected";
|
||||||
$("#status").classList.add("connected");
|
$("#status").classList.add("connected");
|
||||||
};
|
};
|
||||||
|
|
@ -590,7 +594,14 @@ function connect(){
|
||||||
} else {
|
} else {
|
||||||
isAuthed = true;
|
isAuthed = true;
|
||||||
hideOverlay();
|
hideOverlay();
|
||||||
ver = msg.ver; ta.value = msg.text; refresh();
|
ver = msg.ver;
|
||||||
|
if (dirty && ta.value !== msg.text) {
|
||||||
|
// Reconnected with unsent edits: push them rather than lose them.
|
||||||
|
ws.send(JSON.stringify({type: "edit", ver, text: ta.value, clientId}));
|
||||||
|
} else {
|
||||||
|
ta.value = msg.text;
|
||||||
|
}
|
||||||
|
refresh();
|
||||||
applyLang(msg.lang || "");
|
applyLang(msg.lang || "");
|
||||||
// ?pw= on an unprotected pad sets the password instead of unlocking.
|
// ?pw= on an unprotected pad sets the password instead of unlocking.
|
||||||
// Guard on !isProtected: the server re-sends init after a successful
|
// Guard on !isProtected: the server re-sends init after a successful
|
||||||
|
|
@ -634,8 +645,31 @@ function connect(){
|
||||||
$("#status").textContent = "disconnected";
|
$("#status").textContent = "disconnected";
|
||||||
$("#status").classList.remove("connected");
|
$("#status").classList.remove("connected");
|
||||||
$("#peers").style.display = "none";
|
$("#peers").style.display = "none";
|
||||||
|
scheduleReconnect();
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function scheduleReconnect() {
|
||||||
|
if (reconnectTimer) return;
|
||||||
|
reconnectTimer = setTimeout(() => { reconnectTimer = null; connect(); }, reconnectDelay);
|
||||||
|
reconnectDelay = Math.min(reconnectDelay * 2, 10000); // back off, cap at 10s
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reconnect if the socket died while we were not looking. A pad restored from the
|
||||||
|
// browser's back/forward cache resumes with its socket already closed and no close
|
||||||
|
// event delivered, which is why leaving a pad and pressing Back left it dead.
|
||||||
|
function ensureConnected() {
|
||||||
|
if (!ws || ws.readyState === 2 || ws.readyState === 3) {
|
||||||
|
clearTimeout(reconnectTimer); reconnectTimer = null;
|
||||||
|
reconnectDelay = 500;
|
||||||
|
connect();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
window.addEventListener("pageshow", (e) => { if (e.persisted) ensureConnected(); });
|
||||||
|
document.addEventListener("visibilitychange", () => {
|
||||||
|
if (document.visibilityState === "visible") ensureConnected();
|
||||||
|
});
|
||||||
|
window.addEventListener("online", ensureConnected);
|
||||||
$("#newpad").addEventListener("click", (e) => { e.preventDefault(); window.open("/" + rand() + "/", "_blank"); });
|
$("#newpad").addEventListener("click", (e) => { e.preventDefault(); window.open("/" + rand() + "/", "_blank"); });
|
||||||
|
|
||||||
// Copy to clipboard function
|
// Copy to clipboard function
|
||||||
|
|
@ -688,10 +722,12 @@ ta.addEventListener("keydown", (e) => {
|
||||||
|
|
||||||
// Send edits (debounced)
|
// Send edits (debounced)
|
||||||
ta.addEventListener("input", () => {
|
ta.addEventListener("input", () => {
|
||||||
|
dirty = true;
|
||||||
clearTimeout(debounce);
|
clearTimeout(debounce);
|
||||||
debounce = setTimeout(() => {
|
debounce = setTimeout(() => {
|
||||||
if (ws?.readyState === 1 && isAuthed) {
|
if (ws?.readyState === 1 && isAuthed) {
|
||||||
ws.send(JSON.stringify({type:"edit", ver, text: ta.value, clientId}));
|
ws.send(JSON.stringify({type:"edit", ver, text: ta.value, clientId}));
|
||||||
|
dirty = false;
|
||||||
}
|
}
|
||||||
}, 120);
|
}, 120);
|
||||||
});
|
});
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue