From 9b2ec91dd603183c4e7fe0db19f6631cbee10df7 Mon Sep 17 00:00:00 2001 From: Lotus CI Date: Mon, 12 Oct 2026 12:35:26 -0400 Subject: [PATCH] fix(presence): Invisible, Hide Online Status, Idle and DND stick (#282) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit usePresenceUpdater sent a one-off PUT /presence, but the SDK kept long-polling /sync with the default set_presence=online and Synapse treats every sync as "online", so Invisible / Hide Online Status / Idle / DND flipped back within seconds — a privacy setting that silently didn't work. The presence helpers now also call mx.setSyncPresence(): offline for Invisible / Hide Online Status, unavailable for Idle / DND (and auto-mode idle / hidden tab), online otherwise. The PUT stays for the immediate change. Verified on the local Synapse, another account polling every 5 s for 60 s after a reload in each mode: invisible → offline ×12, hidePresence → offline ×12, idle → unavailable ×12, dnd → unavailable ×12, online → online (currently_active) ×12; /sync carries set_presence=offline / unavailable / online accordingly (before: online* throughout). Fixes #282 Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_017PCb59uY3UKqDqt7B6hzd5 --- src/app/hooks/usePresenceUpdater.ts | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/src/app/hooks/usePresenceUpdater.ts b/src/app/hooks/usePresenceUpdater.ts index 74a57644e..74a1ada06 100644 --- a/src/app/hooks/usePresenceUpdater.ts +++ b/src/app/hooks/usePresenceUpdater.ts @@ -1,4 +1,5 @@ import { useEffect, useRef } from 'react'; +import { SetPresence } from 'matrix-js-sdk'; import { PresenceDeduper, PresenceWrite } from '../utils/presenceWrite'; import { useMatrixClient } from './useMatrixClient'; import { getLastLocalStatusWrite, pickHeartbeatStatus } from '../utils/ownStatusSync'; @@ -90,11 +91,21 @@ export function usePresenceUpdater() { return status; }; + // [Gitea #282] A one-off PUT /presence doesn't stick: the SDK long-polls + // /sync with set_presence=online by default and Synapse treats every sync + // as "online", so Invisible / Idle / DND flipped back within seconds. Tell + // the sync loop which presence to report too (as Element does). + const syncPresence = (presence: SetPresence) => { + mx.setSyncPresence(presence).catch(() => undefined); + }; + const setOnline = async () => { + syncPresence(SetPresence.Online); const status = await resolveStatus(); return send({ presence: 'online', ...(status ? { status_msg: status } : {}) }, 'online'); }; const setUnavailable = async (statusMsg?: string) => { + syncPresence(SetPresence.Unavailable); const status = await resolveStatus(); return send( { @@ -104,7 +115,10 @@ export function usePresenceUpdater() { 'unavailable', ); }; - const setOffline = () => send({ presence: 'offline', status_msg: '' }, 'offline'); + const setOffline = () => { + syncPresence(SetPresence.Offline); + return send({ presence: 'offline', status_msg: '' }, 'offline'); + }; // Manual presence overrides — no activity tracking needed. if (hidePresence || presenceStatus === 'invisible') {