/* Copyright 2024 New Vector Ltd. Copyright 2019 The Matrix.org Foundation C.I.C. Copyright 2018 New Vector Ltd Copyright 2015, 2016 OpenMarket Ltd SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only Please see LICENSE files in the repository root for full details. */ import { logger } from "matrix-js-sdk/src/logger"; import { SetPresence } from "matrix-js-sdk/src/matrix"; import { MatrixClientPeg } from "./MatrixClientPeg"; import dis from "./dispatcher/dispatcher"; import Timer from "./utils/Timer"; import { ActionPayload } from "./dispatcher/payloads"; // Time in ms after that a user is considered as unavailable/away const UNAVAILABLE_TIME_MS = 3 * 60 * 1000; // 3 mins class Presence { private unavailableTimer: Timer | null = null; private dispatcherRef: string | null = null; private state: SetPresence | null = null; /** * Start listening the user activity to evaluate his presence state. * Any state change will be sent to the homeserver. */ public async start(): Promise { this.unavailableTimer = new Timer(UNAVAILABLE_TIME_MS); // the user_activity_start action starts the timer this.dispatcherRef = dis.register(this.onAction); while (this.unavailableTimer) { try { await this.unavailableTimer.finished(); this.setState(SetPresence.Unavailable); } catch (e) { /* aborted, stop got called */ } } } /** * Stop tracking user activity */ public stop(): void { if (this.dispatcherRef) { dis.unregister(this.dispatcherRef); this.dispatcherRef = null; } if (this.unavailableTimer) { this.unavailableTimer.abort(); this.unavailableTimer = null; } } /** * Get the current presence state. * @returns {string} the presence state (see PRESENCE enum) */ public getState(): SetPresence | null { return this.state; } private onAction = (payload: ActionPayload): void => { if (payload.action === "user_activity") { this.setState(SetPresence.Online); this.unavailableTimer?.restart(); } }; /** * Set the presence state. * If the state has changed, the homeserver will be notified. * @param {string} newState the new presence state (see PRESENCE enum) */ private async setState(newState: SetPresence): Promise { if (newState === this.state) { return; } const oldState = this.state; this.state = newState; if (MatrixClientPeg.safeGet().isGuest()) { return; // don't try to set presence when a guest; it won't work. } try { await MatrixClientPeg.safeGet().setSyncPresence(this.state); logger.debug("Presence:", newState); } catch (err) { logger.error("Failed to set presence:", err); this.state = oldState; } } } export default new Presence();