fix: restore Today break after sync recovery (Closes #1204)
All checks were successful
CI / lint (pull_request) Successful in 3m57s
CI / build-release (pull_request) Successful in 8s
CI / browser-journey (pull_request) Successful in 5m17s
CI / release-candidate (pull_request) Has been skipped

This commit is contained in:
timmy 2026-08-21 01:56:04 +00:00
parent e3c7df25c8
commit e5ccfb0c5e
3 changed files with 84 additions and 6 deletions

View File

@ -5480,7 +5480,7 @@
activeFlushLogin = contextIdentityFresh ? String(snapshot.context.user?.login || '').trim() : '';
if (activeFlushLogin) {
confirmedOwnerLogin = activeFlushLogin;
timerView.render();
timerView.restore(todaySync.flush());
restoreReleaseReceipt();
updateDeliveryReceiptControls();
interruptionPrompt.restore();

View File

@ -542,6 +542,10 @@ function createTodayTimerView({ timer, isActive, queryAll, formatEstimate, getRu
},
finishCapture() { return captureView.finish(); },
transferCapture() { return captureView.transfer(); },
restore(recovery) {
render();
return Promise.resolve(recovery).catch(() => false).then(() => { render(); return !!timer.breakSnapshot?.(); });
},
render,
};
queryAll('#new-issue').forEach(button => button.addEventListener('click', () => {

View File

@ -185,12 +185,86 @@ process.stdout.write(JSON.stringify({{opened,cancelled,started,expired,final:{{c
}
def test_confirming_the_account_renders_a_persisted_break_immediately():
dashboard = (ROOT / "frontend" / "dashboard.js").read_text()
confirmation = dashboard.split("if (activeFlushLogin) {", 1)[1].split("}", 1)[0]
def test_identity_ready_restore_renders_break_again_after_sync_recovery_settles():
script = f"""
const createBreak = require({json.dumps(str(BREAK))});
global.createTodayBreak = createBreak;
global.setInterval = () => 1;
global.clearInterval = () => {{}};
Date.now = () => 100000;
const createTimer = require({json.dumps(str(TIMER))});
const createSync = require({json.dumps(str(ROOT / "frontend" / "today-sync.js"))});
const values = new Map();
const storage = {{
get length(){{return values.size;}},
key:index=>Array.from(values.keys())[index]??null,
getItem:key=>values.get(key)||null,
setItem:(key,value)=>values.set(key,value),
removeItem:key=>values.delete(key),
}};
let login = '';
values.set('stackchain.today-timer.v1.timmy', JSON.stringify({{
version:1, active_identity:'issue:r:42:',
entries:{{'issue:r:42:':{{elapsed_ms:12000,started_at:null,running:false}}}},
away_at:null,pending_interruption:null,attention_interruption:null,capture_interruption:null,
search_interruption:null,detour_interruption:null,
timed_break:{{identity:'issue:r:42:',deadline_at:400000}},
}}));
const status = {{hidden:false,textContent:''}}, resume = {{hidden:false,addEventListener(){{}}}};
const opener = {{hidden:false,addEventListener(){{}}}};
const elements = {{'#today-break-status':[status],'#resume-today-break':[resume],'[data-today-break-open]':[opener]}};
const timer = createTimer({{storage,getLogin:()=>login,now:()=>100000}});
const view = createTimer.createView({{
timer,isActive:()=>true,queryAll:selector=>elements[selector]||[],
formatEstimate:value=>String(value),getItem:()=>({{title:'Ship mobile capture'}}),
}});
login = 'timmy';
values.set('stackchain.today-sync.v1.timmy.operation.broken', '{{not-json');
const statuses = [];
const sync = createSync({{
storage,getLogin:()=>login,now:()=>100000,
fetchJson:async()=>({{revision:1,ids:['issue:r:42:'],capacity_minutes:null,estimates:{{}}}}),
onStatus:(state,detail)=>statuses.push([state,detail.discarded||0]),
}});
const recovery = sync.flush();
const restoring = view.restore(recovery);
const immediate = {{hidden:status.hidden,text:status.textContent,openerHidden:opener.hidden}};
status.hidden = true; status.textContent = ''; opener.hidden = false;
restoring.then(result => {{
const settled = {{hidden:status.hidden,text:status.textContent,openerHidden:opener.hidden}};
status.hidden = true; status.textContent = ''; opener.hidden = false;
return view.restore(Promise.reject(new Error('coordinator unavailable'))).then(rejected =>
process.stdout.write(JSON.stringify({{
immediate, settled, rejectedSettled:{{hidden:status.hidden,text:status.textContent,openerHidden:opener.hidden}},
result,rejected,statuses,
}}))
);
}});
"""
assert "confirmedOwnerLogin = activeFlushLogin;" in confirmation
assert "timerView.render();" in confirmation
assert run_node(script) == {
"immediate": {
"hidden": False,
"text": "On break · resume in 5:00",
"openerHidden": True,
},
"settled": {
"hidden": False,
"text": "On break · resume in 5:00",
"openerHidden": True,
},
"rejectedSettled": {
"hidden": False,
"text": "On break · resume in 5:00",
"openerHidden": True,
},
"result": True,
"rejected": True,
"statuses": [["recovered", 1]],
}
dashboard = (ROOT / "frontend" / "dashboard.js").read_text()
assert "timerView.restore(todaySync.flush());" in dashboard
def test_timed_break_is_packaged_as_an_accessible_mobile_flow():