dc1915bb43
Live presence (fixes stale in-call/status until refresh — impossible in apps): - server broadcasts a user's status over the chat socket on connect/disconnect, call join/leave, and status change (chat.js broadcastPresence; signaling + routes hooks). - client onPresence() updates the contact dot + open-chat header live. Chat delivery ticks (#6): chat-list row now mirrors the thread (delivered→double grey, read→blue) via a new 'with' field on the delivered relay + onChatRead/onChatDelivered. Call fixes: no bogus 'host handed over' when a 1:1 call ends (leaveMeeting forced); branded call-connecting + chat-thread loaders; header subtitle tracks live call state. Notifications: web notify + sw.js use sender/group DP + brand icon (not old wordmark); desktop shell drops Web Push so only the single native toast fires (#5). Brand: master icon/splash/loaders wired everywhere (PWA/favicon/apple-touch/.ico), branded login (blue + gold CTA), branded toasts (BZToast) on all pages, Electron splash. Desktop: dev auto-targets localhost (packaged→prod); version 0.1.3 with new multi-size icon; dropped unused node-notifier; removed home-mockup.html. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
30 lines
2.4 KiB
JavaScript
30 lines
2.4 KiB
JavaScript
/* Biz Connect toast API. Requires bizconnect-toast.css.
|
|
BZToast.success('Saved'); BZToast.error('Connection lost'); BZToast.message('Hi', {title:'Ravi'}); */
|
|
window.BZToast=(function(){
|
|
var wrap=null;
|
|
var ICON={
|
|
message:'<svg viewBox="0 0 24 24" fill="none" stroke="#fff" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z"/></svg>',
|
|
info:'<svg viewBox="0 0 24 24" fill="none"><circle cx="12" cy="12" r="9" stroke="#fff" stroke-width="2.2"/><path d="M12 11v5M12 8h.01" stroke="#fff" stroke-width="2.4" stroke-linecap="round"/></svg>',
|
|
success:'<svg viewBox="0 0 24 24" fill="none" stroke="#fff" stroke-width="2.6" stroke-linecap="round" stroke-linejoin="round"><path d="M20 6 9 17l-5-5"/></svg>',
|
|
error:'<svg viewBox="0 0 24 24" fill="none" stroke="#fff" stroke-width="2.6" stroke-linecap="round"><path d="M18 6 6 18M6 6l12 12"/></svg>'
|
|
};
|
|
function esc(s){return String(s==null?'':s).replace(/[&<>"]/g,function(c){return{'&':'&','<':'<','>':'>','"':'"'}[c];});}
|
|
function ensure(){ if(wrap) return wrap; wrap=document.createElement('div'); wrap.className='bzt-wrap'; document.body.appendChild(wrap); return wrap; }
|
|
function show(message,opts){
|
|
opts=opts||{}; var type=opts.type||'message'; var w=ensure();
|
|
var t=document.createElement('div'); t.className='bzt '+type;
|
|
t.innerHTML='<div class="bzt-badge">'+(ICON[type]||ICON.message)+'</div><div class="bzt-body">'
|
|
+(opts.title?'<div class="bzt-title">'+esc(opts.title)+'</div>':'')
|
|
+'<div class="bzt-msg">'+esc(message)+'</div></div><button class="bzt-x" aria-label="Dismiss">×</button>';
|
|
w.appendChild(t); requestAnimationFrame(function(){ t.classList.add('bzt-in'); });
|
|
var dur=(opts.duration==null?4000:opts.duration), timer;
|
|
function close(){ t.classList.remove('bzt-in'); setTimeout(function(){ if(t.parentNode) t.parentNode.removeChild(t); },320); clearTimeout(timer); }
|
|
t.querySelector('.bzt-x').onclick=close; if(dur>0) timer=setTimeout(close,dur); return close;
|
|
}
|
|
return { show:show,
|
|
message:function(m,o){o=o||{};o.type='message';return show(m,o);},
|
|
success:function(m,o){o=o||{};o.type='success';return show(m,o);},
|
|
error:function(m,o){o=o||{};o.type='error';return show(m,o);},
|
|
info:function(m,o){o=o||{};o.type='info';return show(m,o);} };
|
|
})();
|