ux: FIX reconnect after leaving the page and returning #32
This commit is contained in:
parent
ba36703a71
commit
c12703e782
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 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);
|
||||
});
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue