Tell the mirror that Phase 2 is running instead of leaving it on a greyed button
The mirror's own job ends in seconds while Phase 2 runs for minutes on the owner, so the screen sat unchanged on the phase-1 panel — which reads as a hang, and the reasonable response to a hang is to start clicking.
This commit is contained in:
@@ -332,6 +332,33 @@ function vvRenderMirrorOnboard(opts) {
|
|||||||
// Phase 2 means the partnership is actually established, so the instructions have genuinely
|
// Phase 2 means the partnership is actually established, so the instructions have genuinely
|
||||||
// stopped being needed. Until then showing them costs nothing — a step already done reads as a
|
// stopped being needed. Until then showing them costs nothing — a step already done reads as a
|
||||||
// reminder, a step still needed and hidden is a dead end.
|
// reminder, a step still needed and hidden is a dead end.
|
||||||
|
// Phase 2 runs on the OWNER and takes minutes — 4m05s on a measured run, longer as the arr
|
||||||
|
// library grows. The mirror's own job finishes in about three seconds, because all it does is
|
||||||
|
// send the notification, so for the rest of that window the screen showed the unchanged
|
||||||
|
// phase-1 panel with a greyed-out button and no statement that anything was happening
|
||||||
|
// elsewhere. That reads as a hang, and the reasonable response to a hang is to start clicking.
|
||||||
|
if ((opts.phase ?? 0) < 2 && opts.waiting) {
|
||||||
|
const mins = opts.waitingSince
|
||||||
|
? Math.floor((Date.now() - opts.waitingSince) / 60000) : 0;
|
||||||
|
const secs = opts.waitingSince
|
||||||
|
? Math.floor(((Date.now() - opts.waitingSince) % 60000) / 1000) : 0;
|
||||||
|
return `<div style="padding:12px 14px;background:#1a1200;border:1px solid #3a2800;border-radius:4px;">
|
||||||
|
<div style="display:flex;align-items:center;gap:10px;flex-wrap:wrap;">
|
||||||
|
<span style="font-size:18px;line-height:1;color:#ff9800;">⟳</span>
|
||||||
|
<div style="flex:1;min-width:220px;">
|
||||||
|
<div style="color:#ff9800;font-size:12px;font-weight:600;">
|
||||||
|
Onboard in progress — please wait</div>
|
||||||
|
<div style="color:#8a6a2a;font-size:10px;margin-top:3px;line-height:1.5;">
|
||||||
|
${owner} is running Phase 2: deploying containers, syncing auth data and
|
||||||
|
bootstrapping the arr libraries. This normally takes 4–6 minutes and nothing is
|
||||||
|
needed from you here. This page updates itself when it finishes.</div>
|
||||||
|
${opts.waitingSince ? `<div style="color:#6a5020;font-size:10px;margin-top:4px;
|
||||||
|
font-family:monospace;">elapsed ${mins}m ${String(secs).padStart(2,'0')}s</div>` : ''}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>`;
|
||||||
|
}
|
||||||
|
|
||||||
if ((opts.phase ?? 0) >= 2) {
|
if ((opts.phase ?? 0) >= 2) {
|
||||||
// Done state. This used to return the same "Join partnership · ▶ Onboard" panel as the
|
// Done state. This used to return the same "Join partnership · ▶ Onboard" panel as the
|
||||||
// not-started state, under a comment saying it collapsed — so a fully onboarded mirror
|
// not-started state, under a comment saying it collapsed — so a fully onboarded mirror
|
||||||
|
|||||||
@@ -525,6 +525,8 @@ function vvRenderOnboardPanel(d) {
|
|||||||
termCmd: `bash ${dir}/Partnership/partnership_onboard.sh --phase1-only`,
|
termCmd: `bash ${dir}/Partnership/partnership_onboard.sh --phase1-only`,
|
||||||
phase: vvDetectedIdentity.phase ?? 0,
|
phase: vvDetectedIdentity.phase ?? 0,
|
||||||
hasPartner: true,
|
hasPartner: true,
|
||||||
|
waiting: _vvPhase2Waiting,
|
||||||
|
waitingSince: _vvPhase2Since,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -599,23 +601,45 @@ function vvRenderDone(d) {
|
|||||||
// bootstrap in Step 9 pushed a real run to 4m05s and it grows with library size, so the poll was
|
// bootstrap in Step 9 pushed a real run to 4m05s and it grows with library size, so the poll was
|
||||||
// expiring before the flag landed and the wizard sat unchanged on a partnership that had already
|
// expiring before the flag landed and the wizard sat unchanged on a partnership that had already
|
||||||
// succeeded — the exact symptom this function exists to remove.
|
// succeeded — the exact symptom this function exists to remove.
|
||||||
let _vvPartnerPoll = null;
|
let _vvPartnerPoll = null;
|
||||||
|
let _vvPhase2Waiting = false; // Phase 2 is running on the owner right now
|
||||||
|
let _vvPhase2Since = 0; // epoch ms, for the elapsed counter
|
||||||
|
let _vvPhase2Tick = null; // redraws the counter between checklist polls
|
||||||
|
let _vvLastChecklist = null; // last payload, so the ticker can redraw without refetching
|
||||||
function vvOnJobDone(id, status) {
|
function vvOnJobDone(id, status) {
|
||||||
if (!String(id).includes('partnership_onboard')) { vvLoadChecklist(); return; }
|
if (!String(id).includes('partnership_onboard')) { vvLoadChecklist(); return; }
|
||||||
if (_vvPartnerPoll) clearInterval(_vvPartnerPoll);
|
if (_vvPartnerPoll) clearInterval(_vvPartnerPoll);
|
||||||
|
if (_vvPhase2Tick) clearInterval(_vvPhase2Tick);
|
||||||
|
|
||||||
|
// The mirror's job ending is the START of the wait, not the end of the work — Phase 2 begins on
|
||||||
|
// the owner at this moment. Hold the in-progress panel until the checklist actually turns.
|
||||||
|
_vvPhase2Waiting = true;
|
||||||
|
_vvPhase2Since = Date.now();
|
||||||
|
|
||||||
|
const stop = () => {
|
||||||
|
_vvPhase2Waiting = false;
|
||||||
|
if (_vvPartnerPoll) { clearInterval(_vvPartnerPoll); _vvPartnerPoll = null; }
|
||||||
|
if (_vvPhase2Tick) { clearInterval(_vvPhase2Tick); _vvPhase2Tick = null; }
|
||||||
|
vvLoadChecklist();
|
||||||
|
};
|
||||||
|
|
||||||
const deadline = Date.now() + 20 * 60 * 1000;
|
const deadline = Date.now() + 20 * 60 * 1000;
|
||||||
const check = () => {
|
const check = () => {
|
||||||
fetch('/plugins/varaverk/api/checklist.php?_=' + Date.now())
|
fetch('/plugins/varaverk/api/checklist.php?_=' + Date.now())
|
||||||
.then(r => r.json())
|
.then(r => r.json())
|
||||||
.then(d => {
|
.then(d => {
|
||||||
const done = (d.items || []).some(i => i.id === 'partnership' && i.ok);
|
const done = (d.items || []).some(i => i.id === 'partnership' && i.ok);
|
||||||
if (done || Date.now() > deadline) {
|
if (done || Date.now() > deadline) stop();
|
||||||
clearInterval(_vvPartnerPoll); _vvPartnerPoll = null;
|
|
||||||
vvLoadChecklist();
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
.catch(() => {});
|
.catch(() => {});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Redraw between checklist polls so the elapsed counter moves. Without it the panel is static
|
||||||
|
// for six seconds at a time, which reads as frozen — the thing this panel exists to prevent.
|
||||||
|
_vvPhase2Tick = setInterval(() => {
|
||||||
|
if (_vvPhase2Waiting) vvRenderOnboardPanel(_vvLastChecklist || {items: []});
|
||||||
|
}, 1000);
|
||||||
|
|
||||||
vvLoadChecklist();
|
vvLoadChecklist();
|
||||||
check();
|
check();
|
||||||
_vvPartnerPoll = setInterval(check, 6000);
|
_vvPartnerPoll = setInterval(check, 6000);
|
||||||
@@ -625,6 +649,7 @@ function vvLoadChecklist() {
|
|||||||
fetch('/plugins/varaverk/api/checklist.php?_=' + Date.now())
|
fetch('/plugins/varaverk/api/checklist.php?_=' + Date.now())
|
||||||
.then(r => r.json()).then(d => {
|
.then(r => r.json()).then(d => {
|
||||||
const el = document.getElementById('vv-checklist');
|
const el = document.getElementById('vv-checklist');
|
||||||
|
_vvLastChecklist = d;
|
||||||
if (!d.ok || !d.items) { el.innerHTML = '<span style="color:#555">Unable to load checklist</span>'; return; }
|
if (!d.ok || !d.items) { el.innerHTML = '<span style="color:#555">Unable to load checklist</span>'; return; }
|
||||||
vvLabelExit();
|
vvLabelExit();
|
||||||
vvRenderOnboardPanel(d);
|
vvRenderOnboardPanel(d);
|
||||||
|
|||||||
Reference in New Issue
Block a user