fix: recover Tomorrow promotion after missed rollover (Closes #1154)
This commit is contained in:
1 parent
559fd2dc89
commit
7deb1c1de9
6 files changed
+188
-22
No files matched your search
@@ -377,6 +377,7 @@
|
||||
const outboxCoordinator = createOutboxCoordinator({ storage: localStorage });
|
||||
const todayRollover = createTodayRollover();
|
||||
let planningTomorrow = false;
|
||||
let latestTodayPlan = null;
|
||||
const tomorrowPlan = createTomorrowPlan({
|
||||
fetchJson:fetchReviewJson,
|
||||
localDate:todayRollover.localDate,
|
||||
@@ -419,6 +420,7 @@
|
||||
},
|
||||
onRemotePlan: plan => {
|
||||
if (!planningOwnerLogin) return;
|
||||
latestTodayPlan = plan;
|
||||
promoteTomorrowIfDue(plan);
|
||||
todayWork.replacePlanning({
|
||||
capacity_minutes: plan.capacity_minutes ?? null,
|
||||
@@ -453,6 +455,11 @@
|
||||
},
|
||||
});
|
||||
todaySync.startLifecycle({ window, document });
|
||||
tomorrowPlan.startLifecycle({
|
||||
windowObject:window,
|
||||
documentObject:document,
|
||||
check:() => latestTodayPlan ? promoteTomorrowIfDue(latestTodayPlan) : false,
|
||||
});
|
||||
const todayHandoff = createTodayHandoff({
|
||||
storage:localStorage,
|
||||
getLogin:() => planningOwnerLogin,
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
function createTomorrowPlan({fetchJson,localDate,timeZone,createId}={}) {
|
||||
function createTomorrowPlan({fetchJson,localDate,timeZone}={}) {
|
||||
let plan={revision:0,ids:[],capacity_minutes:null,estimates:{}};
|
||||
const state=()=>({...plan,ids:[...plan.ids],estimates:{...plan.estimates}});
|
||||
function adopt(value) {
|
||||
@@ -19,11 +19,45 @@ function createTomorrowPlan({fetchJson,localDate,timeZone,createId}={}) {
|
||||
return adopt(await fetchJson('api/v1/tomorrow',{method:'PUT',headers:{'Content-Type':'application/json'},body:JSON.stringify(body)}));
|
||||
}
|
||||
async function promote(today_revision) {
|
||||
if (!plan.plan_date||plan.plan_date!==localDate()||!plan.ids.length) return false;
|
||||
const promotion_id=createId?.()||globalThis.crypto?.randomUUID?.()||`${plan.plan_date}-${plan.revision}`;
|
||||
if (!plan.plan_date||plan.plan_date>localDate()||!plan.ids.length) return false;
|
||||
const promotion_id=`tomorrow-${plan.plan_date}-r${plan.revision}`;
|
||||
return fetchJson('api/v1/tomorrow/promote',{method:'POST',headers:{'Content-Type':'application/json'},
|
||||
body:JSON.stringify({promotion_id,tomorrow_revision:plan.revision,today_revision})});
|
||||
}
|
||||
return {adopt,load,save,promote,state,nextLocalDate};
|
||||
function millisecondsUntilNextDay() {
|
||||
const now=Date.now();
|
||||
const zone=timeZone();
|
||||
const formatter=new Intl.DateTimeFormat('en-CA',{timeZone:zone,year:'numeric',month:'2-digit',day:'2-digit',
|
||||
hour:'2-digit',minute:'2-digit',second:'2-digit',hourCycle:'h23'});
|
||||
const zoned=value=>Object.fromEntries(formatter.formatToParts(new Date(value))
|
||||
.filter(part=>part.type!=='literal').map(part=>[part.type,Number(part.value)]));
|
||||
const current=zoned(now);
|
||||
const targetWall=Date.UTC(current.year,current.month-1,current.day+1);
|
||||
let candidate=targetWall;
|
||||
for(let attempt=0;attempt<2;attempt+=1){
|
||||
const parts=zoned(candidate);
|
||||
const offset=Date.UTC(parts.year,parts.month-1,parts.day,parts.hour,parts.minute,parts.second)-candidate;
|
||||
candidate=targetWall-offset;
|
||||
}
|
||||
return Math.max(1,candidate-now+250);
|
||||
}
|
||||
function startLifecycle({windowObject,documentObject,check,setTimer=setTimeout,clearTimer=clearTimeout,
|
||||
nextDelay=millisecondsUntilNextDay}={}) {
|
||||
let flight=null;
|
||||
let timer=null;
|
||||
const schedule=()=>{
|
||||
if(timer!==null) clearTimer(timer);
|
||||
timer=setTimer(run,nextDelay());
|
||||
};
|
||||
function run(){
|
||||
if(!flight) flight=Promise.resolve().then(check).finally(()=>{flight=null;schedule();});
|
||||
return flight;
|
||||
}
|
||||
windowObject?.addEventListener?.('online',run);
|
||||
documentObject?.addEventListener?.('visibilitychange',()=>documentObject.hidden?false:run());
|
||||
run();
|
||||
return {run,stop(){if(timer!==null)clearTimer(timer);timer=null;}};
|
||||
}
|
||||
return {adopt,load,save,promote,state,nextLocalDate,startLifecycle};
|
||||
}
|
||||
if(typeof module!=='undefined'&&module.exports)module.exports=createTomorrowPlan;
|
||||
Reference in new issue
Block a user