Fix iPhone offer template flicker (#33)

Avoid rebuilding the offer template picker during the five-second maintenance pass unless the selected template or template list actually changed. Includes a regression test for repeated maintenance cycles.
This commit is contained in:
pavlov346346-source 2026-09-19 19:28:57 +03:00 committed by GitHub
parent b99bb30aa1
commit ee70c66ed8
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 52 additions and 1 deletions

View File

@ -176,7 +176,18 @@
if(!box){box=document.createElement('section');box.id='sunOfferTemplateV1764';box.className='sun-v1764-offer-template';const head=dialog.querySelector('.sun-offer-modal-head');if(head)head.insertAdjacentElement('afterend',box);else dialog.prepend(box);box.addEventListener('click',e=>{const b=e.target.closest('[data-v1764-offer-template]');if(!b)return;const id=b.dataset.v1764OfferTemplate;if(!persistOfferTemplate(activeOfferOrderId,id))return;renderOfferPicker();toast(`Оформление «${templateList().find(x=>x.id===id)?.name||id}» сохранено для этого предложения.`,'success',3200)});} if(!box){box=document.createElement('section');box.id='sunOfferTemplateV1764';box.className='sun-v1764-offer-template';const head=dialog.querySelector('.sun-offer-modal-head');if(head)head.insertAdjacentElement('afterend',box);else dialog.prepend(box);box.addEventListener('click',e=>{const b=e.target.closest('[data-v1764-offer-template]');if(!b)return;const id=b.dataset.v1764OfferTemplate;if(!persistOfferTemplate(activeOfferOrderId,id))return;renderOfferPicker();toast(`Оформление «${templateList().find(x=>x.id===id)?.name||id}» сохранено для этого предложения.`,'success',3200)});}
renderOfferPicker();return true; renderOfferPicker();return true;
} }
function renderOfferPicker(){const box=$('sunOfferTemplateV1764');if(box)box.innerHTML=pickerHtml()} function offerPickerSignature(){
const list=templateList(),current=activeTemplateId();
return JSON.stringify({current,templates:list.map(t=>[String(t?.id||''),String(t?.name||'')])});
}
function renderOfferPicker({force=false}={}){
const box=$('sunOfferTemplateV1764');if(!box)return false;
const signature=offerPickerSignature();
if(!force&&box.dataset.sunV1764Signature===signature)return false;
box.innerHTML=pickerHtml();
box.dataset.sunV1764Signature=signature;
return true;
}
function setActiveOffer(id){if(id==null||id==='')return;activeOfferOrderId=String(id);setTimeout(ensureOfferPicker,0)} function setActiveOffer(id){if(id==null||id==='')return;activeOfferOrderId=String(id);setTimeout(ensureOfferPicker,0)}
function clearActiveOffer(){activeOfferOrderId=null} function clearActiveOffer(){activeOfferOrderId=null}
function patchOfferOpenExports(){ function patchOfferOpenExports(){

View File

@ -118,6 +118,46 @@ test('a delayed menu editor cannot move back into a section that was already lef
await expect(page.locator('#editor')).not.toHaveClass(/\bon\b/); await expect(page.locator('#editor')).not.toHaveClass(/\bon\b/);
}); });
test('offer template picker is not rebuilt by the five-second maintenance timer',async({page})=>{
await fixture(page,'<button id="offerOpen" data-sun-offer-id="one">Предложение</button><div id="sunClientOfferModal" class="modal on"><div class="dialog"><div class="sun-offer-modal-head"></div></div></div>');
await page.evaluate(()=>{
localStorage.setItem('sunOrders',JSON.stringify([{id:'one',clientOfferTemplateId:'light'}]));
window.SunOfferTemplate={
get:()=>({id:'light',template:{id:'light',name:'Светлый'}}),
list:()=>[{id:'light',name:'Светлый'},{id:'editorial-grid',name:'Редакционный'}]
};
window.CateriumProposalPDF={resolveTemplate:id=>id,IDS:[]};
window.sunOpenClientOffer=()=>{};
window.sunOpenCurrentClientOffer=()=>{};
});
await page.addScriptTag({url:'/core/ux-fixes-v1764.js'});
await page.locator('#offerOpen').click();
const picker=page.locator('#sunOfferTemplateV1764');
await expect(picker).toBeVisible();
await expect(picker.locator('[data-v1764-offer-template]')).toHaveCount(2);
const before=await picker.evaluate(el=>({
first:el.querySelector('[data-v1764-offer-template]'),
html:el.innerHTML,
signature:el.dataset.sunV1764Signature
}));
await page.evaluate(()=>{
const picker=document.getElementById('sunOfferTemplateV1764');
window.offerPickerMutations=0;
new MutationObserver(records=>{window.offerPickerMutations+=records.filter(r=>r.type==='childList').length}).observe(picker,{childList:true,subtree:true});
});
await page.waitForTimeout(5300);
expect(await page.evaluate(()=>window.offerPickerMutations)).toBe(0);
expect(await picker.evaluate((el,before)=>el.innerHTML===before.html&&el.dataset.sunV1764Signature===before.signature,before)).toBe(true);
await picker.locator('[data-v1764-offer-template="editorial-grid"]').click();
await expect(picker.locator('[data-v1764-offer-template="editorial-grid"]')).toHaveClass(/\bon\b/);
const afterSelection=await page.evaluate(()=>window.offerPickerMutations);
expect(afterSelection).toBeGreaterThan(0);
await page.evaluate(()=>window.offerPickerMutations=0);
await page.waitForTimeout(5300);
expect(await page.evaluate(()=>window.offerPickerMutations)).toBe(0);
});
test('full app stays stable across settings, menu, calendar and delayed background refreshes',async({page})=>{ test('full app stays stable across settings, menu, calendar and delayed background refreshes',async({page})=>{
const errors=[];page.on('pageerror',e=>errors.push(String(e))); const errors=[];page.on('pageerror',e=>errors.push(String(e)));
const workspaceId='22222222-2222-4222-8222-222222222222',servedRpcs=new Set(); const workspaceId='22222222-2222-4222-8222-222222222222',servedRpcs=new Set();