Fix #14 (edit leaves a draft) and #8 (last-seen flapping)

#14: after editing a message the edited text reappeared in the composer as a
draft and re-sent as a new message. The input listener saved a draft while
editing, but saveEdit/cancelEdit cleared only the input value, not the stored
draft — so openConvo restored it. Now editing never writes a draft, and
save/cancel clear it.

#8: a contact's subtitle flapped between "last seen …" and "Offline". A
loadSidebar rebuild replaced the row with the server's lastSeen, which is
sometimes null (the disconnect touchSeen is fire-and-forget and can lag the
presence broadcast). Now the client keeps last-seen sticky (never overwrites a
known value with null) and the server never broadcasts a null last-seen for an
offline user.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-08-11 22:37:06 +05:30
parent e359271157
commit ecc8be3dba
2 changed files with 15 additions and 4 deletions
+6 -1
View File
@@ -70,9 +70,14 @@ async function broadcastPresence(userId) {
if (!userId) return;
// Carry last_seen too, so a contact going offline immediately reads "Last seen just now" instead of a
// bare "Offline" until the next sidebar reload (#2).
const online = isOnline(userId);
let lastSeen = null;
try { const u = await repos().users.byId(userId); lastSeen = (u && u.last_seen) || null; } catch (_) {}
const payload = JSON.stringify({ type: 'presence', userId, online: isOnline(userId), status: await effectiveStatus(userId), lastSeen });
// #8: never broadcast a NULL last-seen for someone who's offline — touchSeen() is fire-and-forget on
// disconnect, so the DB write can lag this broadcast and the client would flap to a bare "Offline". They're
// leaving now, so "just now" is accurate.
if (!lastSeen && !online) lastSeen = Date.now();
const payload = JSON.stringify({ type: 'presence', userId, online, status: await effectiveStatus(userId), lastSeen });
deliverPresenceLocal(userId, payload); // this instance
pubsub.publish('presence', { userId, payload }); // other instances (no-op on memory)
}