From b777a5b9d0d2d67ffc8bcead784221622d867a26 Mon Sep 17 00:00:00 2001 From: retoor Date: Thu, 6 Aug 2026 19:19:21 +0200 Subject: [PATCH] Make the presence roster the single source of truth for online status Online status had three server-side candidate populations and two client-side deciders, so the feed roster and the /messages indicators could legitimately disagree. PresenceRelayService built its online set from whichever topics happened to be subscribed on a given tick: the roster candidates on /feed, only the per-uid dot rows on /messages. Different populations meant a different hysteresis baseline, so the same user could be online in one place and offline in the other. On top of that, PresenceManager re-derived online status client-side from a frozen data-presence-last-seen with a strict timeout and no hysteresis, re-evaluated every 20s, so any element whose relay frame was missed drifted grey after the timeout and stayed there. AppChat carried a third renderer with its own PubSubClient that only ever wrote online/offline, plus hand-built dot markup duplicating _presence_dot.html. The relay now collapses to one set on one topic. Each tick it reads the online population in a single indexed query (online_candidates, capped by the new PRESENCE_TRACK_LIMIT), applies hysteresis once, and publishes {count, online, users} on public.presence.roster only when the uid set changes. online is the authority for every avatar dot; users is the same set trimmed to PRESENCE_ONLINE_LIMIT for the feed panel. The per-uid public.presence.{uid} topics are gone, which also removes one subscription per distinct author on a page. is_online(user) is now stays_online(seconds_since(last_seen), False), so the server-rendered initial state and the live set apply one formula. PresenceManager makes one subscription and renders every [data-presence-uid] element as membership of that set, with no clock and no expiry timer; before the first frame the server-rendered state stands. Relative "last seen" text is a