ux: FIX reconnect after leaving the page and returning #32

This commit is contained in:
Caffeine Fueled 2026-09-02 18:50:26 +02:00
parent ba36703a71
commit c12703e782
Signed by: cf7
GPG key ID: CA295D643074C68C

38
app.py
View file

@ -389,6 +389,9 @@ $("#padname").textContent = "/"+docId+"/";
let ws, ver = 0, clientId = Math.random().toString(36).slice(2), debounce;
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") || "";
// --- Line numbers ---
@ -572,6 +575,7 @@ function connect(){
$("#status").classList.remove("connected");
ws = new WebSocket(`${proto}://${location.host}/ws/${encodeURIComponent(docId)}`);
ws.onopen = () => {
reconnectDelay = 500;
$("#status").textContent = "connected";
$("#status").classList.add("connected");
};
@ -590,7 +594,14 @@ function connect(){
} else {
isAuthed = true;
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 || "");
// ?pw= on an unprotected pad sets the password instead of unlocking.
// Guard on !isProtected: the server re-sends init after a successful
@ -634,8 +645,31 @@ function connect(){
$("#status").textContent = "disconnected";
$("#status").classList.remove("connected");
$("#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"); });
// Copy to clipboard function
@ -688,10 +722,12 @@ ta.addEventListener("keydown", (e) => {
// Send edits (debounced)
ta.addEventListener("input", () => {
dirty = true;
clearTimeout(debounce);
debounce = setTimeout(() => {
if (ws?.readyState === 1 && isAuthed) {
ws.send(JSON.stringify({type:"edit", ver, text: ta.value, clientId}));
dirty = false;
}
}, 120);
});