diff --git a/server/public/home.html b/server/public/home.html index b5e2f7b..fb0fe19 100644 --- a/server/public/home.html +++ b/server/public/home.html @@ -4537,8 +4537,13 @@ let _chatReconnectT=null; let _lastPresenceRefresh=0; function refreshPresenceOnResume(){ try{ - if(!chatWs || chatWs.readyState>1){ connectChatWs(); } // dead/closed → force reconnect (skip if OPEN/CONNECTING) - const t=Date.now(); if(t-_lastPresenceRefresh>4000){ _lastPresenceRefresh=t; try{ loadSidebar(); }catch(_){} try{ touchSession(); }catch(_){} } // throttle so rapid tab switches don't spam; also slide the session forward + // If the socket is dead/closed, reconnect — its onopen runs resyncChat → loadSidebar, which refreshes + // presence AND unread from the server. We deliberately do NOT force a loadSidebar when the socket is fine: + // the initial payload now carries last-seen (repos.listByTenant fix) and live presence events keep it + // current, so an unconditional focus-refresh only risked clobbering fresh state (it caused the #8 "Offline + // for all" regression and could momentarily reset an unread badge). + if(!chatWs || chatWs.readyState>1){ connectChatWs(); } + const t=Date.now(); if(t-_lastPresenceRefresh>4000){ _lastPresenceRefresh=t; try{ touchSession(); }catch(_){} } // slide the session forward (throttled) }catch(_){} } function connectChatWs(){ diff --git a/server/repos.js b/server/repos.js index 0c2d89e..da8ea37 100644 --- a/server/repos.js +++ b/server/repos.js @@ -38,7 +38,10 @@ const users = { byBizgazeId: (bizId) => (bizId ? db.prepare('SELECT * FROM users WHERE bizgaze_user_id=?').get(String(bizId)) : undefined), setBizgazeId: (id, bizId) => db.prepare('UPDATE users SET bizgaze_user_id=? WHERE id=?').run(bizId != null ? String(bizId) : null, id), listByTenant: (tenantId) => - db.prepare('SELECT id,email,name,role,active,avatar_url,created_at FROM users WHERE team_id=?').all(tenantId), + // last_seen + status MUST be selected: the contacts/conversations DTOs read x.last_seen / x.status. Omitting + // them made every list payload carry lastSeen:null (and status:'active'), so a fresh load showed a bare + // "Offline" with no time — last-seen only appeared via live presence events (which read the full row). + db.prepare('SELECT id,email,name,role,active,avatar_url,created_at,last_seen,status FROM users WHERE team_id=?').all(tenantId), inTenant: (id, tenantId) => db.prepare('SELECT * FROM users WHERE id=? AND team_id=?').get(id, tenantId), create: async ({ tenantId, email, hash, salt, role, name, mfaSecret }) => {