@nanocollective/roster 0.1.0-alpha.46 → 0.1.0-alpha.47
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/docs/portal.md
CHANGED
|
@@ -95,7 +95,8 @@ Where the portal opens. An Ask box at the top, then these, in order.
|
|
|
95
95
|
- a pull request has **Merge**, unless its checks fail, are still running, or it conflicts.
|
|
96
96
|
|
|
97
97
|
Answers go out as you, with the staff member's `@handle`, so they act on them straight away.
|
|
98
|
-
|
|
98
|
+
Once you have had the last word, the card leaves Needs you for Your requests, since it is
|
|
99
|
+
waiting on them now. If they reply, it comes back. When nothing is waiting, it says so.
|
|
99
100
|
- **Unread.** Anything on a staff member's own tracker, or elsewhere, with activity you have not
|
|
100
101
|
seen: a peer's ask, a status issue, their own work. Unread comes from your GitHub
|
|
101
102
|
notifications, as on Trackers. Cards and rows anywhere on Home carry the same blue mark, and
|
|
@@ -105,7 +106,7 @@ Where the portal opens. An Ask box at the top, then these, in order.
|
|
|
105
106
|
they are idle, how their last run ended. Peer and follow-on runs today are counted against
|
|
106
107
|
`max_runs_per_day`. This asks GitHub every few seconds while a run is going and every half
|
|
107
108
|
minute otherwise, and only while Home is on screen.
|
|
108
|
-
- **Your requests.** What you asked for: waiting, being worked on (a run for it is going), or
|
|
109
|
+
- **Your requests.** What you asked for, and asks of theirs you have answered: waiting, being worked on (a run for it is going), or
|
|
109
110
|
answered (a staff member had the last word).
|
|
110
111
|
- **Closed today.** What the staff closed today, with the line they closed it with, and
|
|
111
112
|
**Reopen** beside each. Staff close finished issues themselves, so this is where you check.
|
package/package.json
CHANGED
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
/* Home: what needs you, who is working, your requests, closed today. */
|
|
2
2
|
|
|
3
3
|
.home{max-width:820px}
|
|
4
|
+
/* Secondary text on Home is read, not scanned for numbers: the readable face and colour, not
|
|
5
|
+
the faint monospace the rest of the portal uses for metadata. */
|
|
6
|
+
.home .meta{font:12.5px/1.5 var(--sans);color:var(--ink-dim)}
|
|
4
7
|
.home h2{font:600 13px var(--sans);color:var(--ink-dim);text-transform:none;letter-spacing:0;
|
|
5
8
|
margin:26px 0 10px;display:flex;align-items:center;gap:8px}
|
|
6
9
|
.home h2 .pill{font-size:10.5px}
|
|
@@ -59,13 +62,18 @@
|
|
|
59
62
|
|
|
60
63
|
.homerow{display:flex;align-items:baseline;gap:10px;flex-wrap:wrap;padding:8px 2px;
|
|
61
64
|
border-bottom:1px solid var(--line);font-size:13px}
|
|
62
|
-
.homerow .homeopen{font-weight:500;font-size:
|
|
65
|
+
.homerow .homeopen{font-weight:500;font-size:13.5px}
|
|
66
|
+
.homelist{display:grid;grid-template-columns:auto 1fr;align-items:start;gap:4px 12px}
|
|
67
|
+
.homelist > .chip{margin-top:2px}
|
|
68
|
+
.homelist .meta{margin-top:3px}
|
|
63
69
|
.homeclick{cursor:pointer;border-radius:8px;padding-left:8px;padding-right:8px;margin:0 -8px}
|
|
64
70
|
.homeclick:hover{background:var(--hover)}
|
|
65
71
|
.homeclick:focus-visible{outline:2px solid var(--accent);outline-offset:-2px}
|
|
66
72
|
.homeclosed{display:grid;grid-template-columns:1fr auto;align-items:center;gap:4px 14px}
|
|
67
|
-
.homeclosed .meta{margin-top:3px
|
|
73
|
+
.homeclosed .meta{margin-top:3px}
|
|
68
74
|
.homerest{margin-top:26px}
|
|
75
|
+
.homecard.gone,.homerow.gone{opacity:0;transform:translateX(12px);
|
|
76
|
+
transition:opacity .2s var(--ease),transform .2s var(--ease)}
|
|
69
77
|
|
|
70
78
|
/* Activity you have not seen, as Trackers marks it: a blue edge and a bold title. Opening the
|
|
71
79
|
thread marks it read, here and on GitHub. */
|
|
@@ -121,17 +121,22 @@ export function placeOf(item, owner, people, markers) {
|
|
|
121
121
|
(item.assignees ?? []).some((a) => people.has(lower(a))) ||
|
|
122
122
|
labels.some((l) => markers.has(l));
|
|
123
123
|
const byPerson = people.has(lower(item.author));
|
|
124
|
+
/* You had the last word on an ask: approved it, answered it, said go with the default. It is
|
|
125
|
+
waiting on them now, so it is one of your requests until they reply or close it. If they
|
|
126
|
+
reply, the last word is theirs and it is back on you. */
|
|
127
|
+
const youAnswered = Boolean(item.lastComment) && people.has(lower(item.lastComment.author));
|
|
124
128
|
|
|
125
129
|
if (!owner) {
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
130
|
+
if (!onPerson || byPerson) {
|
|
131
|
+
return { place: "elsewhere", why: item.role === "product" ? "contributor" : "other" };
|
|
132
|
+
}
|
|
133
|
+
return youAnswered ? { place: "requests" } : { place: "needs", kind: kindOf(labels) };
|
|
129
134
|
}
|
|
130
135
|
if (item.number === owner.statusIssue || labels.includes("keep-open")) {
|
|
131
136
|
return { place: "staff", why: "status" };
|
|
132
137
|
}
|
|
133
138
|
if (byPerson) return { place: "requests" };
|
|
134
|
-
if (onPerson) return { place: "needs", kind: kindOf(labels) };
|
|
139
|
+
if (onPerson) return youAnswered ? { place: "requests" } : { place: "needs", kind: kindOf(labels) };
|
|
135
140
|
return {
|
|
136
141
|
place: "staff",
|
|
137
142
|
why: labels.some((l) => l.startsWith("from-")) ? "peer" : "own",
|
|
@@ -246,6 +246,12 @@ async function act(note, actions, payload, ok) {
|
|
|
246
246
|
try {
|
|
247
247
|
await post(payload);
|
|
248
248
|
note.textContent = ok;
|
|
249
|
+
// Done is done: the card goes now, rather than when GitHub next answers.
|
|
250
|
+
const card = actions.closest?.(".homecard, .homerow");
|
|
251
|
+
if (card) {
|
|
252
|
+
card.classList.add("gone");
|
|
253
|
+
setTimeout(() => card.remove(), 220);
|
|
254
|
+
}
|
|
249
255
|
refreshSoon();
|
|
250
256
|
} catch (err) {
|
|
251
257
|
note.textContent = String(err.message || err);
|
|
@@ -338,6 +344,16 @@ function outcome(r) {
|
|
|
338
344
|
return r.conclusion === "success" ? "finished" : r.conclusion === "failure" ? "failed" : (r.conclusion ?? "ended");
|
|
339
345
|
}
|
|
340
346
|
|
|
347
|
+
/**
|
|
348
|
+
* A timer that never keeps a process alive. In the browser this is setTimeout; under the
|
|
349
|
+
* tests' Node shim the polling loop would otherwise hold a test file open forever.
|
|
350
|
+
*/
|
|
351
|
+
function later(fn, ms) {
|
|
352
|
+
const t = setTimeout(fn, ms);
|
|
353
|
+
t?.unref?.();
|
|
354
|
+
return t;
|
|
355
|
+
}
|
|
356
|
+
|
|
341
357
|
function poll() {
|
|
342
358
|
clearTimeout(timer);
|
|
343
359
|
getLive()
|
|
@@ -351,7 +367,7 @@ function poll() {
|
|
|
351
367
|
.finally(() => {
|
|
352
368
|
if (S.view !== "home") return;
|
|
353
369
|
const busy = (S.live?.staff ?? []).some((l) => l.running?.length);
|
|
354
|
-
timer =
|
|
370
|
+
timer = later(poll, busy ? FAST : SLOW);
|
|
355
371
|
});
|
|
356
372
|
}
|
|
357
373
|
|
|
@@ -370,18 +386,25 @@ const WHY = {
|
|
|
370
386
|
|
|
371
387
|
/** Something on a staff member's tracker, or elsewhere, with activity you have not seen. */
|
|
372
388
|
function unreadRow(e) {
|
|
373
|
-
return clickable(e.item, "homerow", [
|
|
389
|
+
return clickable(e.item, "homerow homelist", [
|
|
374
390
|
el("span", { className: "chip", textContent: WHY[e.why] ?? "Issue" }),
|
|
375
|
-
|
|
376
|
-
|
|
391
|
+
el("div", {}, [
|
|
392
|
+
openLink(e.item),
|
|
393
|
+
el("div", { className: "meta", textContent: whereOf(e) + " · " + ago(e.item.updatedAt) }),
|
|
394
|
+
]),
|
|
377
395
|
]);
|
|
378
396
|
}
|
|
379
397
|
|
|
380
398
|
function requestRow(e) {
|
|
381
|
-
return clickable(e.item, "homerow", [
|
|
382
|
-
el("span", {
|
|
383
|
-
|
|
384
|
-
|
|
399
|
+
return clickable(e.item, "homerow homelist", [
|
|
400
|
+
el("span", {
|
|
401
|
+
className: "chip " + (e.status === "answered" ? "cool" : e.status === "working" ? "warm" : ""),
|
|
402
|
+
textContent: STATUS[e.status],
|
|
403
|
+
}),
|
|
404
|
+
el("div", {}, [
|
|
405
|
+
openLink(e.item),
|
|
406
|
+
el("div", { className: "meta", textContent: whereOf(e) + " · " + ago(e.item.updatedAt) }),
|
|
407
|
+
]),
|
|
385
408
|
]);
|
|
386
409
|
}
|
|
387
410
|
|
|
@@ -491,5 +514,5 @@ function refreshSoon() {
|
|
|
491
514
|
ensureInbox(true)
|
|
492
515
|
.then(() => S.view === "home" && !document.querySelector("dialog[open]") && repaintInPlace())
|
|
493
516
|
.catch(() => {});
|
|
494
|
-
|
|
517
|
+
later(poll, 3_000);
|
|
495
518
|
}
|