diff --git a/src/components/views/avatars/RoomAvatar.tsx b/src/components/views/avatars/RoomAvatar.tsx index 541d0fc2ff..f74c061c68 100644 --- a/src/components/views/avatars/RoomAvatar.tsx +++ b/src/components/views/avatars/RoomAvatar.tsx @@ -6,19 +6,11 @@ SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Com Please see LICENSE files in the repository root for full details. */ -import React, { type ComponentProps } from "react"; -import { - type Room, - RoomStateEvent, - type MatrixEvent, - EventType, - RoomType, - KnownMembership, -} from "matrix-js-sdk/src/matrix"; +import React, { useCallback, useMemo, type ComponentProps } from "react"; +import { type Room, RoomType, KnownMembership } from "matrix-js-sdk/src/matrix"; import BaseAvatar from "./BaseAvatar"; import ImageView from "../elements/ImageView"; -import { MatrixClientPeg } from "../../../MatrixClientPeg"; import Modal from "../../../Modal"; import * as Avatar from "../../../Avatar"; import DMRoomMap from "../../../utils/DMRoomMap"; @@ -26,8 +18,9 @@ import { mediaFromMxc } from "../../../customisations/Media"; import { type IOOBData } from "../../../stores/ThreepidInviteStore"; import { LocalRoom } from "../../../models/LocalRoom"; import { filterBoolean } from "../../../utils/arrays"; -import SettingsStore from "../../../settings/SettingsStore"; import { MediaPreviewValue } from "../../../@types/media_preview"; +import { useRoomAvatar } from "../../../hooks/room/useRoomAvatar"; +import { useSettingValue } from "../../../hooks/useSettings"; interface IProps extends Omit, "name" | "idName" | "url" | "onClick"> { // Room may be left unset here, but if it is, @@ -41,10 +34,6 @@ interface IProps extends Omit, "name" | "idNam onClick?(): void; } -interface IState { - urls: string[]; -} - export function idNameForRoom(room: Room): string { const dmMapUserId = DMRoomMap.shared().getUserIdForRoomId(room.roomId); // If the room is a DM, we use the other user's ID for the color hash @@ -58,107 +47,73 @@ export function idNameForRoom(room: Room): string { return room.roomId; } -export default class RoomAvatar extends React.Component { - public static defaultProps = { - size: "36px", - oobData: {}, - }; +export const RoomAvatar: React.FC = ({ + room, + oobData, + viewAvatarOnClick, + onClick, + className, + size, + ...otherProps +}) => { + (size = size ?? "36px"), (oobData = oobData ?? {}); - public constructor(props: IProps) { - super(props); + const roomName = room?.name ?? oobData.name ?? "?"; + const roomAvatarMxc = useRoomAvatar(room); + const roomIdName = useMemo(() => { + if (room) { + return idNameForRoom(room); + } else { + return oobData?.roomId; + } + }, [oobData, room]); - this.state = { - urls: RoomAvatar.getImageUrls(this.props), + const mediaPreviewEnabled = + useSettingValue("mediaPreviewConfig", room?.roomId).invite_avatars === MediaPreviewValue.On; + + const onRoomAvatarClick = useCallback(() => { + const avatarUrl = Avatar.avatarUrlForRoom(room ?? null, undefined, undefined, undefined); + if (!avatarUrl) return; + const params = { + src: avatarUrl, + name: room?.name, }; - } - public componentDidMount(): void { - MatrixClientPeg.safeGet().on(RoomStateEvent.Events, this.onRoomStateEvents); - } + Modal.createDialog(ImageView, params, "mx_Dialog_lightbox", undefined, true); + }, [room]); - public componentWillUnmount(): void { - MatrixClientPeg.get()?.removeListener(RoomStateEvent.Events, this.onRoomStateEvents); - } - - public static getDerivedStateFromProps(nextProps: IProps): IState { - return { - urls: RoomAvatar.getImageUrls(nextProps), - }; - } - - private onRoomStateEvents = (ev: MatrixEvent): void => { - if (ev.getRoomId() !== this.props.room?.roomId || ev.getType() !== EventType.RoomAvatar) return; - - this.setState({ - urls: RoomAvatar.getImageUrls(this.props), - }); - }; - - private static getImageUrls(props: IProps): string[] { - const myMembership = props.room?.getMyMembership(); + const urls = useMemo(() => { + // Apparently parseInt ignores suffixes. + const sizeInt = parseInt(size, 10); + const myMembership = room?.getMyMembership(); if (myMembership === KnownMembership.Invite || !myMembership) { - if ( - SettingsStore.getValue("mediaPreviewConfig", props.room?.roomId).invite_avatars !== MediaPreviewValue.On - ) { + if (!mediaPreviewEnabled) { // The user has opted out of showing avatars, so return no urls here. return []; } } let oobAvatar: string | null = null; - if (props.oobData.avatarUrl) { - oobAvatar = mediaFromMxc(props.oobData.avatarUrl).getThumbnailOfSourceHttp( - parseInt(props.size, 10), - parseInt(props.size, 10), - "crop", - ); + if (oobData.avatarUrl) { + oobAvatar = mediaFromMxc(oobData.avatarUrl).getThumbnailOfSourceHttp(sizeInt, sizeInt, "crop"); } return filterBoolean([ oobAvatar, // highest priority - RoomAvatar.getRoomAvatarUrl(props), + roomAvatarMxc && Avatar.avatarUrlForRoom(room ?? null, sizeInt, sizeInt, "crop"), ]); - } + }, [mediaPreviewEnabled, room, size, roomAvatarMxc]); - private static getRoomAvatarUrl(props: IProps): string | null { - if (!props.room) return null; + return ( + + ); +}; - return Avatar.avatarUrlForRoom(props.room, parseInt(props.size, 10), parseInt(props.size, 10), "crop"); - } - - private onRoomAvatarClick = (): void => { - const avatarUrl = Avatar.avatarUrlForRoom(this.props.room ?? null, undefined, undefined, undefined); - if (!avatarUrl) return; - const params = { - src: avatarUrl, - name: this.props.room?.name, - }; - - Modal.createDialog(ImageView, params, "mx_Dialog_lightbox", undefined, true); - }; - - private get roomIdName(): string | undefined { - const room = this.props.room; - - if (room) { - return idNameForRoom(room); - } else { - return this.props.oobData?.roomId; - } - } - - public render(): React.ReactNode { - const { room, oobData, viewAvatarOnClick, onClick, className, ...otherProps } = this.props; - const roomName = room?.name ?? oobData.name ?? "?"; - - return ( - - ); - } -} +export default RoomAvatar;