@parley-im/parley 0.2.2 → 0.2.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/web/app.css +2 -0
- package/web/chat.js +4 -0
- package/web/dashboard.js +53 -16
package/package.json
CHANGED
package/web/app.css
CHANGED
|
@@ -74,6 +74,8 @@ button, input, textarea, select { font: inherit; color: inherit; }
|
|
|
74
74
|
.crumbs { display: flex; align-items: baseline; gap: 8px; min-width: 0; flex: 1; }
|
|
75
75
|
.crumbs .product { color: var(--muted); font-weight: 500; }
|
|
76
76
|
.crumbs .slash { color: var(--faint); }
|
|
77
|
+
.brand { display: inline-flex; align-items: center; gap: 10px; font-weight: 600; color: inherit; text-decoration: none !important; }
|
|
78
|
+
.brand:hover .mark { filter: brightness(1.1); }
|
|
77
79
|
.crumbs .here { font-weight: 600; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
|
|
78
80
|
.crumbs a { text-decoration: none; }
|
|
79
81
|
.crumbs a:hover { text-decoration: underline; }
|
package/web/chat.js
CHANGED
|
@@ -743,6 +743,10 @@
|
|
|
743
743
|
clearTimeout(retry);
|
|
744
744
|
retry = null;
|
|
745
745
|
ws = new WebSocket(`${location.origin.replace(/^http/, "ws")}${base}/ws`);
|
|
746
|
+
// Keep-alives, answered by parley.im at the edge (no cost), keep phone
|
|
747
|
+
// networks and proxies from dropping a quiet connection.
|
|
748
|
+
const ping = setInterval(() => ws.readyState === WebSocket.OPEN && ws.send('{"t":"ping"}'), 30_000);
|
|
749
|
+
ws.addEventListener("close", () => clearInterval(ping));
|
|
746
750
|
ws.onopen = () => {
|
|
747
751
|
failures = 0;
|
|
748
752
|
h.offline(false, "connection");
|
package/web/dashboard.js
CHANGED
|
@@ -1,7 +1,12 @@
|
|
|
1
1
|
// Keeps the parley.im dashboard current: who's on each session's page right
|
|
2
|
-
// now, when they were last active, and whether a reply is coming in.
|
|
3
|
-
//
|
|
2
|
+
// now, when they were last active, and whether a reply is coming in. Each
|
|
3
|
+
// linked computer pushes news over its own WebSocket, which costs nothing
|
|
4
|
+
// while it's quiet, so nothing here polls. It never shows what was said;
|
|
5
|
+
// parley.im doesn't have that.
|
|
4
6
|
(() => {
|
|
7
|
+
const REPLYING_MS = 6000;
|
|
8
|
+
const live = new Map(); // session id -> { here, active, replying, online }
|
|
9
|
+
|
|
5
10
|
const ago = (ms) => {
|
|
6
11
|
const s = Math.max(0, Math.round((Date.now() - ms) / 1000));
|
|
7
12
|
if (s < 60) return "just now";
|
|
@@ -17,25 +22,57 @@
|
|
|
17
22
|
// Matches live() in relay/src/pages.ts.
|
|
18
23
|
const liveNodes = (s) => {
|
|
19
24
|
if (s.online === false) return dot("off", "computer offline");
|
|
20
|
-
if (s.replying) return dot("busy", "replying");
|
|
25
|
+
if (s.replying && Date.now() - s.replying < REPLYING_MS) return dot("busy", "replying");
|
|
21
26
|
if (s.here) return dot("on", s.here > 1 ? `${s.here} people here` : "here now");
|
|
22
27
|
if (s.active) return dot("", `active ${ago(s.active)}`);
|
|
23
28
|
return dot("", "not opened yet");
|
|
24
29
|
};
|
|
25
30
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
31
|
+
function paint() {
|
|
32
|
+
for (const row of document.querySelectorAll("[data-session]")) {
|
|
33
|
+
const s = live.get(row.dataset.session);
|
|
34
|
+
if (s) row.querySelector(".live")?.replaceChildren(...liveNodes(s));
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function apply(computer, m) {
|
|
39
|
+
if (m.t === "changed") return location.reload();
|
|
40
|
+
if (m.t !== "live") return;
|
|
41
|
+
// Times come from parley.im's clock; shift them onto this one.
|
|
42
|
+
const shift = Date.now() - m.now;
|
|
43
|
+
for (const row of document.querySelectorAll(`[data-device="${computer}"]`)) {
|
|
44
|
+
const s = m.sessions[row.dataset.session] ?? {};
|
|
45
|
+
live.set(row.dataset.session, {
|
|
46
|
+
online: m.on,
|
|
47
|
+
here: s.here ?? 0,
|
|
48
|
+
active: s.active ? s.active + shift : null,
|
|
49
|
+
replying: s.replying ? s.replying + shift : null,
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
const row = document.querySelector(`[data-computer="${computer}"]`);
|
|
53
|
+
if (row) {
|
|
54
|
+
row.querySelector(".dot").className = `dot${m.on ? " on" : ""}`;
|
|
55
|
+
row.querySelector(".computer-status").textContent = m.on ? `online now${m.version ? ` · parley ${m.version}` : ""}` : "offline";
|
|
56
|
+
}
|
|
57
|
+
paint();
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function watch(computer, delay = 2000) {
|
|
61
|
+
const ws = new WebSocket(`${location.origin.replace(/^http/, "ws")}/dashboard/live?d=${encodeURIComponent(computer)}`);
|
|
62
|
+
const ping = setInterval(() => ws.readyState === WebSocket.OPEN && ws.send('{"t":"ping"}'), 30_000);
|
|
63
|
+
ws.onopen = () => (delay = 2000);
|
|
64
|
+
ws.onmessage = (event) => {
|
|
65
|
+
try {
|
|
66
|
+
apply(computer, JSON.parse(event.data));
|
|
67
|
+
} catch {}
|
|
68
|
+
};
|
|
69
|
+
ws.onclose = () => {
|
|
70
|
+
clearInterval(ping);
|
|
71
|
+
setTimeout(() => watch(computer, Math.min(delay * 2, 60_000)), delay);
|
|
72
|
+
};
|
|
37
73
|
}
|
|
38
74
|
|
|
39
|
-
|
|
40
|
-
|
|
75
|
+
for (const row of document.querySelectorAll("[data-computer]")) watch(row.dataset.computer);
|
|
76
|
+
// "active 3m ago" and the end of "replying" move on with the clock alone.
|
|
77
|
+
setInterval(paint, 5000);
|
|
41
78
|
})();
|