fix ui view
This commit is contained in:
1 parent
863ca89485
commit
83f4d7c728
9 files changed
+182
-17
No files matched your search
@@ -11,6 +11,7 @@ document.querySelectorAll('[data-conversation-workspace]').forEach(workspace=>{
|
||||
|
||||
const atBottom=()=>scroller.scrollHeight-scroller.scrollTop-scroller.clientHeight<80;
|
||||
const scrollLatest=(smooth=false)=>{const top=scroller.scrollHeight;if(smooth&&typeof scroller.scrollTo==='function')scroller.scrollTo({top,behavior:'smooth'});else scroller.scrollTop=top};
|
||||
let followLatest=true;
|
||||
let initialPin=true;
|
||||
const scheduleLatest=()=>{[0,40,120,300,700,1200].forEach(delay=>setTimeout(()=>{if(initialPin)scrollLatest(false)},delay))};
|
||||
scheduleLatest();
|
||||
@@ -22,8 +23,12 @@ document.querySelectorAll('[data-conversation-workspace]').forEach(workspace=>{
|
||||
sizeObserver?.observe(scroller);if(conversationEnd?.parentElement)sizeObserver?.observe(conversationEnd.parentElement);
|
||||
setTimeout(()=>{initialPin=false;sizeObserver?.disconnect()},1800);
|
||||
['wheel','touchstart','pointerdown','keydown'].forEach(eventName=>scroller.addEventListener(eventName,()=>{initialPin=false;sizeObserver?.disconnect()},{once:true,passive:true}));
|
||||
scroller.addEventListener('scroll',()=>{jump.hidden=atBottom()});
|
||||
scroller.addEventListener('scroll',()=>{followLatest=atBottom();jump.hidden=followLatest});
|
||||
jump?.addEventListener('click',()=>{scrollLatest(true);jump.hidden=true});
|
||||
const layoutObserver='ResizeObserver' in window?new ResizeObserver(()=>{if(followLatest)requestAnimationFrame(()=>scrollLatest(false))}):null;
|
||||
layoutObserver?.observe(scroller);
|
||||
if(composer)layoutObserver?.observe(composer);
|
||||
window.addEventListener('appviewportchange',()=>{if(followLatest)requestAnimationFrame(()=>scrollLatest(false))});
|
||||
|
||||
const groupDates=()=>{scroller.querySelectorAll('.date-divider').forEach(item=>item.remove());let previousDate='';scroller.querySelectorAll('[data-message]').forEach(message=>{if(message.dataset.date!==previousDate){const divider=document.createElement('div');divider.className='date-divider';divider.innerHTML=`<span>${message.dataset.dayLabel}</span>`;message.before(divider);previousDate=message.dataset.date}})};
|
||||
groupDates();
|
||||
@@ -36,7 +41,7 @@ document.querySelectorAll('[data-conversation-workspace]').forEach(workspace=>{
|
||||
const replyResult=workspace.dataset.replyResult||new URLSearchParams(location.search).get('reply_result')||'';
|
||||
const submissionSucceeded=replyResult==='reply'||replyResult==='note';
|
||||
if(submissionSucceeded){sessionStorage.removeItem(`${baseKey}-reply`);sessionStorage.removeItem(`${baseKey}-note`)}
|
||||
const setComposerOpen=(open,focus=true)=>{const keepLatestVisible=atBottom();composer?.classList.toggle('is-collapsed',!open);composerToggle?.setAttribute('aria-expanded',String(open));requestAnimationFrame(()=>{if(keepLatestVisible)scrollLatest(false)});if(open&&focus)setTimeout(()=>input?.focus(),80)};
|
||||
const setComposerOpen=(open,focus=true)=>{const keepLatestVisible=followLatest||atBottom();composer?.classList.toggle('is-collapsed',!open);composerToggle?.setAttribute('aria-expanded',String(open));[0,80,240].forEach(delay=>setTimeout(()=>{if(keepLatestVisible)scrollLatest(false)},delay));if(open&&focus)setTimeout(()=>input?.focus({preventScroll:true}),80)};
|
||||
const saveDraft=()=>{if(!input)return;sessionStorage.setItem(draftKey(),input.value);if(status){status.textContent=input.value?'پیشنویس ذخیره شد':'';clearTimeout(saveDraft.timer);saveDraft.timer=setTimeout(()=>status.textContent='',1800)}};
|
||||
const loadDraft=()=>{if(!input)return;input.value=sessionStorage.getItem(draftKey())||'';resize();input.dispatchEvent(new Event('input',{bubbles:true}))};
|
||||
if(input){input.setAttribute('rows','5');input.setAttribute('placeholder','شرح کامل پاسخ، اطلاعات تکمیلی و نتیجه مورد انتظار خود را بنویسید…');input.addEventListener('input',()=>{resize();saveDraft()});loadDraft()}
|
||||
@@ -117,7 +122,7 @@ document.querySelectorAll('[data-conversation-workspace]').forEach(workspace=>{
|
||||
// cancel a recursively scheduled timeout. pollRunning prevents overlap and
|
||||
// nextPollAt preserves the normal interval and network-error backoff.
|
||||
const pollHeartbeat=setInterval(startPolling,1000);
|
||||
window.addEventListener('beforeunload',()=>{clearInterval(pollHeartbeat);stopPolling()},{once:true});
|
||||
window.addEventListener('beforeunload',()=>{clearInterval(pollHeartbeat);stopPolling();layoutObserver?.disconnect()},{once:true});
|
||||
startPolling();
|
||||
|
||||
const resetMessageMenu=menu=>{if(!menu)return;const editor=menu.querySelector('[data-message-editor]');const choices=menu.querySelector('[data-message-menu-choices]');const input=menu.querySelector('[data-message-edit-input]');if(editor)editor.hidden=true;if(choices)choices.hidden=false;if(input){input.value=input.dataset.originalValue||'';input.dispatchEvent(new Event('input',{bubbles:true}))}};
|
||||
|
||||
+1
-1
@@ -6,7 +6,7 @@ const isiOS=/iphone|ipad|ipod/i.test(navigator.userAgent);
|
||||
const dismissed=()=>Date.now()-Number(localStorage.getItem('pwa-install-dismissed')||0)<24*60*60*1000;
|
||||
const show=()=>{if(banner&&mobileDevice&&!standalone&&!dismissed())banner.hidden=false};
|
||||
const setGuide=(text,label='راهنمای نصب')=>{if(help)help.textContent=text;if(accept)accept.textContent=label;banner?.classList.add('pwa-install-guidance')};
|
||||
if('serviceWorker' in navigator&&window.isSecureContext){let workerRegistration=null,workerReloading=false;const assetVersion=document.documentElement.dataset.staticVersion||new URL(document.currentScript?.src||location.href).searchParams.get('v')||'';const serviceWorkerUrl=`/service-worker.js?v=${encodeURIComponent(assetVersion)}`;const updateWorker=()=>{if(workerRegistration&&document.visibilityState==='visible')workerRegistration.update().catch(()=>{})};navigator.serviceWorker.addEventListener('controllerchange',()=>{if(workerReloading)return;workerReloading=true;location.reload()});window.addEventListener('load',async()=>{try{workerRegistration=await navigator.serviceWorker.register(serviceWorkerUrl,{updateViaCache:'none'});await workerRegistration.update()}catch(error){setGuide('فعالسازی نصب برنامه با خطا روبهرو شد. صفحه را تازهسازی کنید.');show()}});document.addEventListener('visibilitychange',updateWorker);setInterval(updateWorker,60000)}
|
||||
if('serviceWorker' in navigator&&window.isSecureContext){let workerRegistration=null,workerReloading=false,hasWorkerController=Boolean(navigator.serviceWorker.controller);const assetVersion=document.documentElement.dataset.staticVersion||new URL(document.currentScript?.src||location.href).searchParams.get('v')||'';const serviceWorkerUrl=`/service-worker.js?v=${encodeURIComponent(assetVersion)}`;const updateWorker=()=>{if(workerRegistration&&document.visibilityState==='visible')workerRegistration.update().catch(()=>{})};navigator.serviceWorker.addEventListener('controllerchange',()=>{if(!hasWorkerController){hasWorkerController=true;return}if(workerReloading)return;workerReloading=true;location.reload()});window.addEventListener('load',async()=>{try{workerRegistration=await navigator.serviceWorker.register(serviceWorkerUrl,{updateViaCache:'none'});await workerRegistration.update()}catch(error){setGuide('فعالسازی نصب برنامه با خطا روبهرو شد. صفحه را تازهسازی کنید.');show()}});document.addEventListener('visibilitychange',updateWorker);setInterval(updateWorker,60000)}
|
||||
window.addEventListener('beforeinstallprompt',event=>{event.preventDefault();deferredInstallPrompt=event;if(accept){accept.textContent='نصب برنامه';accept.disabled=false}banner?.classList.remove('pwa-install-guidance');if(help)help.textContent='برنامه آماده نصب است؛ برای ادامه روی «نصب برنامه» بزنید.';show()});
|
||||
if(mobileDevice&&isiOS&&!standalone){setGuide('در Safari روی دکمه اشتراکگذاری (□↑) بزنید و «Add to Home Screen» را انتخاب کنید.','نمایش راهنما');setTimeout(show,700)}
|
||||
if(mobileDevice&&!isiOS&&!standalone)setTimeout(()=>{if(!deferredInstallPrompt){setGuide(window.isSecureContext?'از منوی مرورگر گزینه «Install app» یا «افزودن به صفحه اصلی» را انتخاب کنید.':'نصب مستقیم روی آدرس HTTP شبکه ممکن نیست. سامانه را با HTTPS باز کنید؛ سپس دکمه نصب فعال میشود.','راهنمای نصب');show()}},1800);
|
||||
|
||||
@@ -1,3 +1,27 @@
|
||||
// Keep a live measurement of the visual viewport for mobile browsers whose
|
||||
// address bar or software keyboard does not resize the layout viewport. CSS
|
||||
// combines this value with 100svh, so address-bar animation cannot make the
|
||||
// ticket shell jump while a genuinely smaller keyboard viewport still fits.
|
||||
(()=>{
|
||||
let frame=0;
|
||||
const syncViewport=()=>{
|
||||
frame=0;
|
||||
const viewport=window.visualViewport;
|
||||
const height=Math.max(1,Math.round(viewport?.height||window.innerHeight));
|
||||
const offsetTop=Math.max(0,Math.round(viewport?.offsetTop||0));
|
||||
const root=document.documentElement;
|
||||
root.style.setProperty('--visual-viewport-height',`${height}px`);
|
||||
root.style.setProperty('--visual-viewport-offset-top',`${offsetTop}px`);
|
||||
window.dispatchEvent(new CustomEvent('appviewportchange',{detail:{height,offsetTop}}));
|
||||
};
|
||||
const scheduleViewportSync=()=>{if(!frame)frame=requestAnimationFrame(syncViewport)};
|
||||
syncViewport();
|
||||
window.visualViewport?.addEventListener('resize',scheduleViewportSync,{passive:true});
|
||||
window.visualViewport?.addEventListener('scroll',scheduleViewportSync,{passive:true});
|
||||
window.addEventListener('resize',scheduleViewportSync,{passive:true});
|
||||
window.addEventListener('orientationchange',()=>setTimeout(scheduleViewportSync,120),{passive:true});
|
||||
})();
|
||||
|
||||
document.addEventListener('submit',event=>{
|
||||
const form=event.target;if(!form.matches('form'))return;
|
||||
requestAnimationFrame(()=>{if(!form.checkValidity())return;form.querySelectorAll('button[type="submit"],button:not([type])').forEach(button=>{button.disabled=true;button.setAttribute('aria-busy','true');if(!button.dataset.label)button.dataset.label=button.textContent;button.textContent='در حال ثبت…'})});
|
||||
|
||||
Reference in new issue
Block a user