#8 root cause: listByTenant omitted last_seen/status → contacts/conversations sent lastSeen:null
repos.listByTenant selected id,email,name,role,active,avatar_url,created_at but NOT last_seen or status. So /api/messages/contacts and /api/messages/conversations always sent lastSeen:null (and status:'active'). Last-seen only ever appeared via LIVE presence events (broadcastPresence reads the full row) — which is why it "worked on desktop" (caught live), not on a fresh iOS load, and why round-2's loadSidebar-on-focus then clobbered the live value → "Offline for all". Verified locally: contacts now returns the real lastSeen timestamp; db-smoke 22/22. Also lightened refreshPresenceOnResume: reconnect the socket if it's dead (its onopen already resyncs the sidebar) but no longer force an unconditional loadSidebar on every focus — that churn caused the #8 regression and could momentarily reset an unread badge (#3). Session sliding (touchSession) stays. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -4537,8 +4537,13 @@ let _chatReconnectT=null;
|
|||||||
let _lastPresenceRefresh=0;
|
let _lastPresenceRefresh=0;
|
||||||
function refreshPresenceOnResume(){
|
function refreshPresenceOnResume(){
|
||||||
try{
|
try{
|
||||||
if(!chatWs || chatWs.readyState>1){ connectChatWs(); } // dead/closed → force reconnect (skip if OPEN/CONNECTING)
|
// If the socket is dead/closed, reconnect — its onopen runs resyncChat → loadSidebar, which refreshes
|
||||||
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
|
// 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(_){}
|
}catch(_){}
|
||||||
}
|
}
|
||||||
function connectChatWs(){
|
function connectChatWs(){
|
||||||
|
|||||||
+4
-1
@@ -38,7 +38,10 @@ const users = {
|
|||||||
byBizgazeId: (bizId) => (bizId ? db.prepare('SELECT * FROM users WHERE bizgaze_user_id=?').get(String(bizId)) : undefined),
|
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),
|
setBizgazeId: (id, bizId) => db.prepare('UPDATE users SET bizgaze_user_id=? WHERE id=?').run(bizId != null ? String(bizId) : null, id),
|
||||||
listByTenant: (tenantId) =>
|
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) =>
|
inTenant: (id, tenantId) =>
|
||||||
db.prepare('SELECT * FROM users WHERE id=? AND team_id=?').get(id, tenantId),
|
db.prepare('SELECT * FROM users WHERE id=? AND team_id=?').get(id, tenantId),
|
||||||
create: async ({ tenantId, email, hash, salt, role, name, mfaSecret }) => {
|
create: async ({ tenantId, email, hash, salt, role, name, mfaSecret }) => {
|
||||||
|
|||||||
Reference in New Issue
Block a user