feat: queue offline Week-to-Today pulls (Closes #1249)
This commit is contained in:
1 parent
317fc7371a
commit
7c4e798776
2 files changed
+129
-24
No files matched your search
+55
-24
@@ -39,20 +39,33 @@ function createWeekPlan({fetchJson,localDate,timeZone,storage,getLogin,now=()=>D
|
||||
const login=String(getLogin?.()||'').trim().toLowerCase();
|
||||
return login?pullPrefix+encodeURIComponent(login):'';
|
||||
}
|
||||
function pendingPull() {
|
||||
function pullQueue() {
|
||||
const key=pullKey();
|
||||
if(!key||!storage)return null;
|
||||
if(!key||!storage)return [];
|
||||
try {
|
||||
const value=JSON.parse(storage.getItem(key)||'null');
|
||||
return typeof value?.body?.operation_id==='string'&&Array.isArray(value?.today?.ids)&&
|
||||
Number.isInteger(value?.week?.revision)&&Array.isArray(value?.week?.days)?value:null;
|
||||
} catch(_error){return null;}
|
||||
const entries=Array.isArray(value?.entries)?value.entries:(value?[value]:[]);
|
||||
return entries.filter(entry=>typeof entry?.body?.operation_id==='string'&&Array.isArray(entry?.today?.ids)&&
|
||||
Number.isInteger(entry?.week?.revision)&&Array.isArray(entry?.week?.days));
|
||||
} catch(_error){return [];}
|
||||
}
|
||||
function writePullQueue(entries) {
|
||||
const key=pullKey();if(!key||!storage)return false;
|
||||
try {
|
||||
if(entries.length)storage.setItem(key,JSON.stringify({version:2,entries}));
|
||||
else storage.removeItem(key);
|
||||
return true;
|
||||
} catch(_error){return false;}
|
||||
}
|
||||
function pendingPull() {
|
||||
return pullQueue()[0]||null;
|
||||
}
|
||||
function resumePull() {
|
||||
const value=pendingPull();
|
||||
const queue=pullQueue(),conflicted=queue.find(entry=>entry.conflict&&entry.current?.week);
|
||||
const value=conflicted||queue[queue.length-1];
|
||||
if(!value)return null;
|
||||
adopt(value.week);
|
||||
return {...value,sync_pending:true};
|
||||
adopt(conflicted?conflicted.current.week:value.week);
|
||||
return {...value,...(conflicted?.current||{}),sync_pending:true};
|
||||
}
|
||||
function readConfirmed() {
|
||||
const key=confirmedKey();
|
||||
@@ -502,8 +515,8 @@ function createWeekPlan({fetchJson,localDate,timeZone,storage,getLogin,now=()=>D
|
||||
return {...cloneDay(day),ids:(day.ids||[]).filter(id=>id!==identity),estimates};
|
||||
})};
|
||||
const record={body,today:optimisticToday,week:optimisticWeek,queued_at:now()};
|
||||
try {storage.setItem(key,JSON.stringify(record));}
|
||||
catch(_error){throw new Error('Could not save this pull on this device. Nothing changed.');}
|
||||
const queue=pullQueue();
|
||||
if(!writePullQueue([...queue,record]))throw new Error('Could not save this pull on this device. Nothing changed.');
|
||||
adopt(optimisticWeek);
|
||||
try{return await flushPull();}
|
||||
catch(error){
|
||||
@@ -519,21 +532,39 @@ function createWeekPlan({fetchJson,localDate,timeZone,storage,getLogin,now=()=>D
|
||||
const record=pendingPull(),key=pullKey();
|
||||
if(!record||!key)return Promise.resolve(false);
|
||||
pulling=(async()=>{
|
||||
let result;
|
||||
try {
|
||||
result=await fetchJson('api/v1/week/pull-item',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify(record.body)});
|
||||
} catch(error) {
|
||||
if(error?.status!==409)throw error;
|
||||
const [today,currentWeek]=await Promise.all([fetchJson('api/v1/today'),fetchJson('api/v1/week')]);
|
||||
const conflicted={...record,conflict:true,current:{today,week:currentWeek}};
|
||||
storage.setItem(key,JSON.stringify(conflicted));
|
||||
adopt(currentWeek);
|
||||
return {today,week:currentWeek,conflict:true,sync_pending:true};
|
||||
let result,lastResult=false;
|
||||
while(pendingPull()){
|
||||
const current=pendingPull();
|
||||
try {
|
||||
result=await fetchJson('api/v1/week/pull-item',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify(current.body)});
|
||||
} catch(error) {
|
||||
if(error?.status!==409)throw error;
|
||||
const [today,currentWeek]=await Promise.all([fetchJson('api/v1/today'),fetchJson('api/v1/week')]);
|
||||
const queue=pullQueue(),conflicted={...current,conflict:true,current:{today,week:currentWeek}};
|
||||
if(!writePullQueue([conflicted,...queue.slice(1)]))throw new Error('Could not preserve queued Week Ahead work on this device.');
|
||||
adopt(currentWeek);
|
||||
return {today,week:currentWeek,conflict:true,sync_pending:true};
|
||||
}
|
||||
const queue=pullQueue();
|
||||
if(queue[0]?.body?.operation_id!==current.body.operation_id)continue;
|
||||
const remaining=queue.slice(1).map(entry=>{
|
||||
const identity=entry.body.identity;
|
||||
const source=entry.week.days.find(day=>(day.ids||[]).includes(identity));
|
||||
const estimate=Number(entry.today?.estimates?.[identity]??source?.estimates?.[identity]);
|
||||
const today={...result.today,ids:(result.today.ids||[]).includes(identity)?[...result.today.ids]:[...result.today.ids,identity],
|
||||
estimates:{...(result.today.estimates||{}),...(Number.isFinite(estimate)?{[identity]:estimate}:{})}};
|
||||
const nextWeek={revision:result.week.revision,timezone:result.week.timezone||null,days:result.week.days.map(day=>{
|
||||
const estimates={...(day.estimates||{})};delete estimates[identity];
|
||||
return {...cloneDay(day),ids:(day.ids||[]).filter(id=>id!==identity),estimates};
|
||||
})};
|
||||
result={today,week:nextWeek};
|
||||
return {...entry,body:{...entry.body,today_revision:today.revision,week_revision:nextWeek.revision},today,week:nextWeek};
|
||||
});
|
||||
if(!writePullQueue(remaining))throw new Error('Could not update queued Week Ahead work on this device.');
|
||||
lastResult=result;
|
||||
}
|
||||
const current=pendingPull();
|
||||
if(current?.body?.operation_id===record.body.operation_id)storage.removeItem(key);
|
||||
adoptConfirmed(result.week,{...confirmedItems,...pendingItems});
|
||||
return {...result,sync_pending:Boolean(pendingPull())};
|
||||
adoptConfirmed(lastResult.week,{...confirmedItems,...pendingItems});
|
||||
return {...lastResult,sync_pending:false};
|
||||
})().finally(()=>{pulling=null;});
|
||||
return pulling;
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user