4bee845010
* Use other branch * All the changes that got lost * Fix merge * Ensure emoji can only be one character long * Fixup labs feature * Remove redundant check * Update snapshot * update snapshot * add snapshot * unpin * fix pnpm lock * undo pn[m lockfile changes altogether as we shouldn't actually need any afaik * update snpahot for changed IDs * Snapshot update * Snapshot update * There is now another section * more snapshots * more snapshot * More snapshots * oh come on snapshots * actual snapshot update * Fix sonar issues * just update the thing manually * [screams internally] * Update snapshot * test for useUserStatus * Make useUserStatus actually truncate * Split out slash command to its own file & add test * Remove irrelevant comment * doc * Comment on non-obvious error message --------- Co-authored-by: David Baker <dbkr@users.noreply.github.com>
99 lines
3.5 KiB
TypeScript
99 lines
3.5 KiB
TypeScript
/**
|
|
Copyright 2026 Element Creations Ltd.
|
|
|
|
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
|
|
Please see LICENSE files in the repository root for full details.
|
|
*/
|
|
|
|
import { useEffect, useState } from "react";
|
|
import { ClientEvent, MatrixError } from "matrix-js-sdk/src/matrix";
|
|
import { logger as rootLogger } from "matrix-js-sdk/src/logger";
|
|
|
|
import { useMatrixClientContext } from "../contexts/MatrixClientContext";
|
|
import { useTypedEventEmitter } from "./useEventEmitter";
|
|
import { useFeatureEnabled } from "./useSettings";
|
|
|
|
const logger = rootLogger.getChild("useUserStatus");
|
|
|
|
export interface UserStatus {
|
|
emoji: string;
|
|
text: string;
|
|
}
|
|
|
|
const MAX_STATUS_TEXT_BYTES = 256;
|
|
|
|
export function userStatusTextWithinMaxLength(text: string): boolean {
|
|
const textEncoder = new TextEncoder();
|
|
return textEncoder.encode(text).length <= MAX_STATUS_TEXT_BYTES;
|
|
}
|
|
|
|
/**
|
|
* Hook to get the MSC4426 user status for a given user ID. Returns undefined if the feature is disabled,
|
|
* the user does not have a status, or if there was an error fetching the status.
|
|
*
|
|
* @param userId The ID of the user whose status is being fetched.
|
|
* @returns The user's status, or undefined if not available.
|
|
*/
|
|
export function useUserStatus(userId: string | undefined): UserStatus | undefined {
|
|
const isEnabled = useFeatureEnabled("feature_user_status");
|
|
const matrixClient = useMatrixClientContext();
|
|
const [rawUserStatus, setRawUserStatus] = useState<unknown>();
|
|
|
|
useTypedEventEmitter(matrixClient, ClientEvent.UserProfileUpdate, (syncedUserId, syncProfile) => {
|
|
if (syncedUserId !== userId) {
|
|
return;
|
|
}
|
|
if (syncProfile["org.matrix.msc4426.status"]) {
|
|
setRawUserStatus(syncProfile["org.matrix.msc4426.status"]);
|
|
}
|
|
});
|
|
useEffect(() => {
|
|
(async () => {
|
|
if (!isEnabled) {
|
|
return;
|
|
}
|
|
if (!userId) {
|
|
setRawUserStatus(undefined);
|
|
return;
|
|
}
|
|
if ((await matrixClient.doesServerSupportExtendedProfiles()) === false) {
|
|
setRawUserStatus(undefined);
|
|
return;
|
|
}
|
|
try {
|
|
const result = await matrixClient.getExtendedProfileProperty(userId, "org.matrix.msc4426.status");
|
|
setRawUserStatus(result);
|
|
} catch (ex) {
|
|
if (ex instanceof MatrixError && ex.errcode === "M_NOT_FOUND") {
|
|
setRawUserStatus(undefined);
|
|
} else {
|
|
logger.warn(`Failed to get userStatus for ${userId}`, ex);
|
|
}
|
|
}
|
|
})();
|
|
}, [isEnabled, userId, matrixClient]);
|
|
if (!isEnabled) {
|
|
return;
|
|
}
|
|
|
|
if (typeof rawUserStatus !== "object" || rawUserStatus === null) {
|
|
logger.warn(`value of "org.matrix.msc4426.status" was not an object for ${userId}`);
|
|
return;
|
|
}
|
|
if ("emoji" in rawUserStatus === false || typeof rawUserStatus.emoji !== "string" || !rawUserStatus.emoji) {
|
|
logger.warn(`"emoji" property was not a valid string for ${userId}`);
|
|
return;
|
|
}
|
|
if ("text" in rawUserStatus === false || typeof rawUserStatus.text !== "string" || !rawUserStatus.text) {
|
|
logger.warn(`"text" property was not a valid string for ${userId}`);
|
|
return;
|
|
}
|
|
|
|
return {
|
|
emoji: rawUserStatus.emoji,
|
|
text: userStatusTextWithinMaxLength(rawUserStatus.text)
|
|
? rawUserStatus.text
|
|
: `${rawUserStatus.text.slice(0, MAX_STATUS_TEXT_BYTES)}…`,
|
|
};
|
|
}
|