diff --git a/src/components/views/avatars/MemberAvatar.tsx b/src/components/views/avatars/MemberAvatar.tsx index 48664394731..c0f36c22665 100644 --- a/src/components/views/avatars/MemberAvatar.tsx +++ b/src/components/views/avatars/MemberAvatar.tsx @@ -15,10 +15,9 @@ See the License for the specific language governing permissions and limitations under the License. */ -import React from 'react'; +import React, { useContext } from 'react'; import { RoomMember } from "matrix-js-sdk/src/models/room-member"; import { ResizeMethod } from 'matrix-js-sdk/src/@types/partials'; -import { logger } from "matrix-js-sdk/src/logger"; import dis from "../../../dispatcher/dispatcher"; import { Action } from "../../../dispatcher/actions"; @@ -26,8 +25,7 @@ import BaseAvatar from "./BaseAvatar"; import { mediaFromMxc } from "../../../customisations/Media"; import { CardContext } from '../right_panel/context'; import UserIdentifierCustomisations from '../../../customisations/UserIdentifier'; -import SettingsStore from "../../../settings/SettingsStore"; -import { MatrixClientPeg } from "../../../MatrixClientPeg"; +import { useRoomMemberProfile } from '../../../hooks/room/useRoomMemberProfile'; interface IProps extends Omit, "name" | "idName" | "url"> { member: RoomMember | null; @@ -46,100 +44,58 @@ interface IProps extends Omit, "name" | hideTitle?: boolean; } -interface IState { - name: string; - title: string; - imageUrl?: string; -} - -export default class MemberAvatar extends React.PureComponent { - public static defaultProps = { - width: 40, - height: 40, - resizeMethod: 'crop', - viewUserOnClick: false, - }; - - constructor(props: IProps) { - super(props); - - this.state = MemberAvatar.getState(props); - } - - public static getDerivedStateFromProps(nextProps: IProps): IState { - return MemberAvatar.getState(nextProps); - } - - private static getState(props: IProps): IState { - let member = props.member; - if (member && !props.forceHistorical && SettingsStore.getValue("useOnlyCurrentProfiles")) { - const room = MatrixClientPeg.get().getRoom(member.roomId); - if (room) { - member = room.getMember(member.userId); - } - } - if (member?.name) { - let imageUrl = null; - const userTitle = UserIdentifierCustomisations.getDisplayUserIdentifier( - member.userId, { roomId: member?.roomId }, +export default function MemberAvatar({ + width, + height, + resizeMethod = 'crop', + viewUserOnClick, + ...props +}: IProps) { + const card = useContext(CardContext); + + const member = useRoomMemberProfile({ + userId: props.member?.userId, + member: props.member, + forceHistorical: props.forceHistorical, + }); + + const name = member?.name ?? props.fallbackUserId; + let title: string | undefined = props.title; + let imageUrl: string | undefined; + if (member?.name) { + if (member.getMxcAvatarUrl()) { + imageUrl = mediaFromMxc(member.getMxcAvatarUrl() ?? "").getThumbnailOfSourceHttp( + width, + height, + resizeMethod, ); - if (member.getMxcAvatarUrl()) { - imageUrl = mediaFromMxc(member.getMxcAvatarUrl()).getThumbnailOfSourceHttp( - props.width, - props.height, - props.resizeMethod, - ); - } - return { - name: member.name, - title: props.title || userTitle, - imageUrl: imageUrl, - }; - } else if (props.fallbackUserId) { - return { - name: props.fallbackUserId, - title: props.fallbackUserId, - }; - } else { - logger.error("MemberAvatar called somehow with null member or fallbackUserId"); - return {} as IState; // prevent an explosion } - } - - render() { - let { - member, - fallbackUserId, - onClick, - viewUserOnClick, - // eslint-disable-next-line @typescript-eslint/no-unused-vars - forceHistorical, - hideTitle, - ...otherProps - } = this.props; - const userId = member ? member.userId : fallbackUserId; - if (viewUserOnClick) { - onClick = () => { + if (!title) { + title = UserIdentifierCustomisations.getDisplayUserIdentifier( + member?.userId ?? "", { roomId: member?.roomId ?? "" }, + ) ?? props.fallbackUserId; + } + } + const userId = member?.userId ?? props.fallbackUserId; + + return ( + { dis.dispatch({ action: Action.ViewUser, - member: this.props.member, - push: this.context.isCard, + member: props.member, + push: card.isCard, }); - }; - } - - return ( - - ); - } + } : props.onClick} + /> + ); } - -MemberAvatar.contextType = CardContext; diff --git a/src/components/views/messages/DisambiguatedProfile.tsx b/src/components/views/messages/DisambiguatedProfile.tsx index 36850e916ea..30053b3fbd8 100644 --- a/src/components/views/messages/DisambiguatedProfile.tsx +++ b/src/components/views/messages/DisambiguatedProfile.tsx @@ -23,7 +23,7 @@ import { getUserNameColorClass } from '../../../utils/FormattingUtils'; import UserIdentifier from "../../../customisations/UserIdentifier"; interface IProps { - member?: RoomMember; + member?: RoomMember | null; fallbackName: string; onClick?(): void; colored?: boolean; diff --git a/src/components/views/messages/SenderProfile.tsx b/src/components/views/messages/SenderProfile.tsx index db44cfeb04d..240e047c4cb 100644 --- a/src/components/views/messages/SenderProfile.tsx +++ b/src/components/views/messages/SenderProfile.tsx @@ -18,51 +18,27 @@ import React from 'react'; import { MatrixEvent } from "matrix-js-sdk/src/models/event"; import { MsgType } from "matrix-js-sdk/src/@types/event"; -import MatrixClientContext from "../../../contexts/MatrixClientContext"; import DisambiguatedProfile from "./DisambiguatedProfile"; -import RoomContext, { TimelineRenderingType } from '../../../contexts/RoomContext'; -import SettingsStore from "../../../settings/SettingsStore"; -import { MatrixClientPeg } from "../../../MatrixClientPeg"; +import { useRoomMemberProfile } from '../../../hooks/room/useRoomMemberProfile'; interface IProps { mxEvent: MatrixEvent; onClick?(): void; } -export default class SenderProfile extends React.PureComponent { - public static contextType = MatrixClientContext; - public context!: React.ContextType; - - render() { - const { mxEvent, onClick } = this.props; - const msgtype = mxEvent.getContent().msgtype; - - let member = mxEvent.sender; - if (SettingsStore.getValue("useOnlyCurrentProfiles")) { - const room = MatrixClientPeg.get().getRoom(mxEvent.getRoomId()); - if (room) { - member = room.getMember(mxEvent.getSender()); - } - } - - return - { roomContext => { - if (msgtype === MsgType.Emote && - roomContext.timelineRenderingType !== TimelineRenderingType.ThreadsList - ) { - return null; // emote message must include the name so don't duplicate it - } - - return ( - - ); - } } - ; - } +export default function SenderProfile({ mxEvent, onClick }: IProps) { + const member = useRoomMemberProfile({ + userId: mxEvent.getSender(), + member: mxEvent.sender, + }); + + return mxEvent.getContent().msgtype !== MsgType.Emote + ? + : null; } diff --git a/src/components/views/rooms/EventTile.tsx b/src/components/views/rooms/EventTile.tsx index f6b249b2913..4a3b1ebf8d6 100644 --- a/src/components/views/rooms/EventTile.tsx +++ b/src/components/views/rooms/EventTile.tsx @@ -1311,7 +1311,6 @@ export class UnwrappedEventTile extends React.Component ]); } case TimelineRenderingType.Thread: { - const room = MatrixClientPeg.get().getRoom(this.props.mxEvent.getRoomId()); return React.createElement(this.props.as || "li", { "ref": this.ref, "className": classes, @@ -1325,12 +1324,6 @@ export class UnwrappedEventTile extends React.Component "onMouseEnter": () => this.setState({ hover: true }), "onMouseLeave": () => this.setState({ hover: false }), }, [ - ,
{ avatar } { sender } diff --git a/src/hooks/room/useRoomMemberProfile.ts b/src/hooks/room/useRoomMemberProfile.ts new file mode 100644 index 00000000000..8afab490505 --- /dev/null +++ b/src/hooks/room/useRoomMemberProfile.ts @@ -0,0 +1,46 @@ +/* +Copyright 2022 The Matrix.org Foundation C.I.C. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +import { RoomMember } from "matrix-js-sdk/src/models/room-member"; +import { useContext, useEffect, useState } from "react"; + +import RoomContext, { TimelineRenderingType } from "../../contexts/RoomContext"; +import { useSettingValue } from "../useSettings"; + +export function useRoomMemberProfile({ + userId = "", + member: propMember, + forceHistorical = false, +}: { + userId: string | undefined; + member?: RoomMember | null; + forceHistorical?: boolean; +}): RoomMember | undefined | null { + const [member, setMember] = useState(propMember); + + const context = useContext(RoomContext); + const useOnlyCurrentProfiles = useSettingValue("useOnlyCurrentProfiles"); + + useEffect(() => { + const threadContexts = [TimelineRenderingType.ThreadsList, TimelineRenderingType.Thread]; + if ((propMember && !forceHistorical && useOnlyCurrentProfiles) + || threadContexts.includes(context?.timelineRenderingType)) { + setMember(context?.room?.getMember(userId)); + } + }, [forceHistorical, propMember, context.room, context?.timelineRenderingType, useOnlyCurrentProfiles, userId]); + + return member; +} diff --git a/test/components/views/beacon/__snapshots__/BeaconMarker-test.tsx.snap b/test/components/views/beacon/__snapshots__/BeaconMarker-test.tsx.snap index f9f67e8a363..21e471ec427 100644 --- a/test/components/views/beacon/__snapshots__/BeaconMarker-test.tsx.snap +++ b/test/components/views/beacon/__snapshots__/BeaconMarker-test.tsx.snap @@ -184,21 +184,65 @@ exports[` renders marker when beacon has location 1`] = ` Symbol(kCapture): false, } } - resizeMethod="crop" viewUserOnClick={false} width={36} > renders formatted m.text correctly pills do not appear " `; -exports[` renders formatted m.text correctly pills get injected correctly into the DOM 1`] = `"Hey Member"`; +exports[` renders formatted m.text correctly pills get injected correctly into the DOM 1`] = `"Hey Member"`;