fix: replay reconnect after workspace bootstrap
Some checks failed
CI / lint (pull_request) Successful in 2m49s
CI / build-release (pull_request) Successful in 6s
CI / browser-journey (pull_request) Failing after 2m44s
CI / release-candidate (pull_request) Has been skipped

This commit is contained in:
timmy 2026-08-17 01:23:44 +00:00
parent 1a4f930b50
commit 0a4c9a0ae8
5 changed files with 42 additions and 7 deletions

View File

@ -1,5 +1,5 @@
(async function(){
await loadWorkspace({ document });
const workspaceLifecycle = await loadWorkspace({ document, window });
const qs = (s, el=document) => el.querySelector(s);
const fmt = (d) => new Date(d).toLocaleString();
const cardPlanning = createCardPlanning(document);
@ -7389,6 +7389,7 @@
if (!navigator.onLine) await showOfflineStatus();
window.addEventListener('offline', showOfflineStatus);
window.addEventListener('online', reconnectOutboxes);
workspaceLifecycle.replayOnline(reconnectOutboxes);
qs('#refresh').addEventListener('click', load);
qs('#plan-today').addEventListener('click', event => openPlanToday(event.currentTarget));

View File

@ -1,4 +1,8 @@
async function loadWorkspace({ document, createLoader = createFeatureLoader }) {
async function loadWorkspace({ document, window = null, createLoader = createFeatureLoader }) {
let cameOnline = false;
let replayed = false;
const captureOnline = () => { cameOnline = true; };
window?.addEventListener('online', captureOnline);
const status = document.querySelector('#my-work-action-status');
const url = document.querySelector(
'meta[name="stackchain-feature-today-timer"]'
@ -11,7 +15,16 @@ async function loadWorkspace({ document, createLoader = createFeatureLoader }) {
try {
await loader.load('today-timer');
if (status) status.textContent = '';
return {
replayOnline(callback) {
if (replayed) return;
replayed = true;
window?.removeEventListener('online', captureOnline);
if (cameOnline) callback();
},
};
} catch (error) {
window?.removeEventListener('online', captureOnline);
if (status) {
status.textContent =
'Workspace could not load. Check your connection, then reload to retry.';

View File

@ -31,7 +31,7 @@ FEATURE_SOURCES = {
"security-center": ("static/security-center.js",),
"today-timer": (
"static/conversation.js", "static/voice-transcript-store.js", "static/voice-conversation-capture.js", "static/mobile-launch.js", "static/mobile-insights.js", "static/mobile-app-shortcuts.js", "static/mobile-plan-today-nav.js", "static/mobile-find-work-nav.js",
"static/today-completion.js", "static/card-planning.js", "static/work-detail-position.js", "static/work-route.js", "static/commands.js", "static/saved-searches.js", "static/task-overlay-history.js", "static/search-preview.js", "static/mobile-search-preview-nav.js", "static/search-reply-draft-store.js", "static/conversation-reply-draft-store.js", "static/conversation-photo-drafts.js", "static/search-defer.js", "static/mobile-search-viewport.js", "static/my-work.js", "static/protect-today.js", "static/mobile-task-dock.js", "static/mobile-queue-launcher.js", "static/mobile-start-day.js", "static/update-triage-session.js", "static/update-review-handoff.js", "static/update-triage-launcher.js", "static/update-triage-gesture.js", "static/notification-undo.js", "static/today-timer.js", "static/today-recap.js", "static/today-wrap-up.js", "static/today-handoff.js",
"static/today-completion.js", "static/card-planning.js", "static/work-detail-position.js", "static/work-route.js", "static/commands.js", "static/saved-searches.js", "static/task-overlay-history.js", "static/search-preview.js", "static/mobile-search-preview-nav.js", "static/search-reply-draft-store.js", "static/conversation-reply-draft-store.js", "static/conversation-photo-drafts.js", "static/search-defer.js", "static/mobile-search-viewport.js", "static/my-work.js", "static/protect-today.js", "static/mobile-task-dock.js", "static/mobile-work-entry.js", "static/mobile-queue-launcher.js", "static/mobile-start-day.js", "static/update-triage-session.js", "static/update-review-handoff.js", "static/update-triage-launcher.js", "static/update-triage-gesture.js", "static/notification-undo.js", "static/today-timer.js", "static/today-recap.js", "static/today-wrap-up.js", "static/today-handoff.js",
"static/today-rollover.js", "static/later-work.js", "static/later-picker.js", "static/drafts.js", "static/unfiled-captures.js", "static/unfiled-draft-sync.js",
"static/assign-and-start.js", "static/filed-claim.js", "static/queue-today.js", "static/create-and-start.js",
"static/draft-filing-session.js", "static/draft-capacity-dialog.js", "static/work-selection.js",

View File

@ -270,9 +270,8 @@ def test_release_artifact_files_one_mobile_issue_exactly_once_after_offline_relo
context.set_offline(False)
page.evaluate("window.dispatchEvent(new Event('online'))")
# Containerized Actions runners can take longer than a local browser to wake the
# service worker, finish optional workspace activation, and complete its identity
# handshake after reconnect.
for _ in range(240):
# service worker and complete its identity handshake after reconnect.
for _ in range(160):
if fake.created_issues:
break
page.wait_for_timeout(250)

View File

@ -53,4 +53,26 @@ console.log(JSON.stringify({message,status:status.textContent}));
assert result == {
"message": "network failed",
"status": "Workspace could not load. Check your connection, then reload to retry.",
}
}
def test_workspace_bootstrap_replays_online_event_after_dashboard_registers_handlers():
result = run_bootstrap("""
const status={textContent:''}; const listeners={};
const document={querySelector(selector) {
if (selector.startsWith('meta[')) return {content:'feature-workspace-abc.js'};
if (selector === '#my-work-action-status') return status;
return null;
}};
const window={
addEventListener(name,callback) { listeners[name]=callback; },
removeEventListener(name,callback) { if (listeners[name] === callback) delete listeners[name]; },
};
const createLoader=()=>({load:async()=>{listeners.online();}});
const lifecycle=await loadWorkspace({document,window,createLoader});
let reconnects=0;
lifecycle.replayOnline(()=>{reconnects++;});
lifecycle.replayOnline(()=>{reconnects++;});
console.log(JSON.stringify({reconnects,listening:Boolean(listeners.online)}));
""")
assert result == {"reconnects": 1, "listening": False}