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') {