pike/glyph Public Public: anyone can read this; nobody can change it.

forked from apps/glyph

workbenchthis machine
Files 65c469b86b750b044d3235f2387522d1b8b0ce0b
68 lines · 2.1 KB · ~606 tok Raw History
1
// The page stays current without a reload. /events is a stream of what
2
// changed; each event becomes `live` and `live:<type>` on `document`, and
3
// any element with data-live="<types>" is refetched and swapped when one of
4
// its types arrives — server-rendered, so the page never renders twice.
5
// Without this file the page is the same page, one reload behind.
6
7
let since = "";
8
let tries = 0;
9
let source;
10
11
function connect() {
12
  source = new EventSource(since ? `/events?since=${since}` : "/events");
13
  source.onopen = () => {
14
    if (tries > 0) refresh(); // Reconnected: whatever was missed, catch up.
15
    tries = 0;
16
  };
17
  source.onmessage = (message) => {
18
    since = message.lastEventId;
19
    const { type, data } = JSON.parse(message.data);
20
    document.dispatchEvent(new CustomEvent("live", { detail: { type, data } }));
21
    document.dispatchEvent(new CustomEvent(`live:${type}`, { detail: data }));
22
  };
23
  // The browser retries a dropped connection itself; a refused one it gives
24
  // up on, and then we try again, later each time.
25
  source.onerror = () => {
26
    if (source.readyState !== EventSource.CLOSED) return;
27
    source.close();
28
    setTimeout(connect, Math.min(30_000, 1000 * 2 ** tries++));
29
  };
30
}
31
32
let refreshing = false;
33
let again = false;
34
async function refresh(types) {
35
  const regions = [...document.querySelectorAll("[data-live]")].filter(
36
    (el) =>
37
      types === undefined ||
38
      el.dataset.live.split(" ").some((t) => types.includes(t)),
39
  );
40
  if (regions.length === 0) return;
41
  if (refreshing) {
42
    again = true;
43
    return;
44
  }
45
  refreshing = true;
46
  try {
47
    const response = await fetch(location.href, {
48
      headers: { accept: "text/html" },
49
    });
50
    const fresh = new DOMParser().parseFromString(
51
      await response.text(),
52
      "text/html",
53
    );
54
    for (const el of regions) {
55
      const next = fresh.getElementById(el.id);
56
      if (next) el.replaceWith(next);
57
    }
58
  } finally {
59
    refreshing = false;
60
    if (again) {
61
      again = false;
62
      refresh();
63
    }
64
  }
65
}
66
67
document.addEventListener("live", (event) => refresh([event.detail.type]));
68
connect();
↑↓ navigate ↵ open esc close
↑↓ navigate ↵ open esc close

Keyboard shortcuts