import { useCallback, useEffect, useState } from 'react'; import { RoomJoinRulesEventContent } from 'matrix-js-sdk/lib/types'; import { ClientEvent, MatrixEvent, Room, RoomEvent, RoomEventHandlerMap } from 'matrix-js-sdk'; import { StateEvent } from '../../types/matrix/room'; import { useStateEvent } from './useStateEvent'; import { useMatrixClient } from './useMatrixClient'; export const useRoomAvatar = (room: Room, dm?: boolean): string | undefined => { const avatarEvent = useStateEvent(room, StateEvent.RoomAvatar); if (dm) { return room.getAvatarFallbackMember()?.getMxcAvatarUrl(); } const content = avatarEvent?.getContent(); const avatarMxc = content && typeof content.url === 'string' ? content.url : undefined; return avatarMxc; }; export const useRoomName = (room: Room): string => { const [name, setName] = useState(room.name); useEffect(() => { setName(room.name); const handleRoomNameChange: RoomEventHandlerMap[RoomEvent.Name] = () => { setName(room.name); }; room.on(RoomEvent.Name, handleRoomNameChange); return () => { room.removeListener(RoomEvent.Name, handleRoomNameChange); }; }, [room]); return name; }; export const LOCAL_ROOM_NAMES_KEY = 'io.lotus.room_names'; export type LocalRoomNamesContent = { rooms: Record }; export function getLocalRoomNamesContent(mx: ReturnType): LocalRoomNamesContent { // Use any-cast because LOCAL_ROOM_NAMES_KEY is not in matrix-js-sdk AccountDataEvents // eslint-disable-next-line @typescript-eslint/no-explicit-any const raw: unknown = (mx as any).getAccountData(LOCAL_ROOM_NAMES_KEY)?.getContent(); if ( raw && typeof raw === 'object' && 'rooms' in raw && // eslint-disable-next-line @typescript-eslint/no-explicit-any typeof (raw as any).rooms === 'object' ) { return raw as LocalRoomNamesContent; } return { rooms: {} }; } export const useLocalRoomName = (room: Room): string => { const mx = useMatrixClient(); const getLocalName = useCallback((): string => { const content = getLocalRoomNamesContent(mx); return content.rooms[room.roomId] ?? room.name; }, [mx, room]); const [name, setName] = useState(getLocalName); useEffect(() => { setName(getLocalName()); const handleAccountData = (event: MatrixEvent) => { if (event.getType() !== LOCAL_ROOM_NAMES_KEY) return; setName(getLocalName()); }; mx.on(ClientEvent.AccountData, handleAccountData); const handleRoomNameChange: RoomEventHandlerMap[RoomEvent.Name] = () => { setName(getLocalName()); }; room.on(RoomEvent.Name, handleRoomNameChange); return () => { mx.removeListener(ClientEvent.AccountData, handleAccountData); room.removeListener(RoomEvent.Name, handleRoomNameChange); }; }, [mx, room, getLocalName]); return name; }; export const useHasLocalRoomName = (roomId: string): boolean => { const mx = useMatrixClient(); const check = useCallback((): boolean => { const content = getLocalRoomNamesContent(mx); return !!content.rooms[roomId]; }, [mx, roomId]); const [hasLocal, setHasLocal] = useState(check); useEffect(() => { setHasLocal(check()); const handleAccountData = (event: MatrixEvent) => { if (event.getType() !== LOCAL_ROOM_NAMES_KEY) return; setHasLocal(check()); }; mx.on(ClientEvent.AccountData, handleAccountData); return () => { mx.removeListener(ClientEvent.AccountData, handleAccountData); }; }, [mx, check]); return hasLocal; }; export type RoomTopicContent = { topic: string; formatted_body?: string; format?: string; }; export const useRoomTopic = (room: Room): RoomTopicContent | undefined => { const topicEvent = useStateEvent(room, StateEvent.RoomTopic); const content = topicEvent?.getContent(); if (!content || typeof content.topic !== 'string') return undefined; return { topic: content.topic, formatted_body: typeof content.formatted_body === 'string' ? content.formatted_body : undefined, format: typeof content.format === 'string' ? content.format : undefined, }; }; export const useRoomJoinRule = (room: Room): RoomJoinRulesEventContent | undefined => { const mEvent = useStateEvent(room, StateEvent.RoomJoinRules); const joinRuleContent = mEvent?.getContent(); return joinRuleContent; };