Display room avatars
This commit is contained in:
		
					parent
					
						
							
								183eea9f24
							
						
					
				
			
			
				commit
				
					
						24f721e414
					
				
			
		
					 12 changed files with 112 additions and 63 deletions
				
			
		| 
						 | 
				
			
			@ -1,6 +1,9 @@
 | 
			
		|||
import React, { useMemo } from "react";
 | 
			
		||||
import React, { useMemo, CSSProperties } from "react";
 | 
			
		||||
import classNames from "classnames";
 | 
			
		||||
import { MatrixClient } from "matrix-js-sdk/src/client";
 | 
			
		||||
 | 
			
		||||
import { getAvatarUrl } from "./matrix-utils";
 | 
			
		||||
import { useClient } from "./ClientContext";
 | 
			
		||||
import styles from "./Avatar.module.css";
 | 
			
		||||
 | 
			
		||||
const backgroundColors = [
 | 
			
		||||
| 
						 | 
				
			
			@ -14,6 +17,22 @@ const backgroundColors = [
 | 
			
		|||
  "#74D12C",
 | 
			
		||||
];
 | 
			
		||||
 | 
			
		||||
export enum Size {
 | 
			
		||||
  XS = "xs",
 | 
			
		||||
  SM = "sm",
 | 
			
		||||
  MD = "md",
 | 
			
		||||
  LG = "lg",
 | 
			
		||||
  XL = "xl",
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export const sizes = new Map([
 | 
			
		||||
  [Size.XS, 22],
 | 
			
		||||
  [Size.SM, 32],
 | 
			
		||||
  [Size.MD, 36],
 | 
			
		||||
  [Size.LG, 42],
 | 
			
		||||
  [Size.XL, 90],
 | 
			
		||||
]);
 | 
			
		||||
 | 
			
		||||
function hashStringToArrIndex(str: string, arrLength: number) {
 | 
			
		||||
  let sum = 0;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -24,24 +43,51 @@ function hashStringToArrIndex(str: string, arrLength: number) {
 | 
			
		|||
  return sum % arrLength;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const resolveAvatarSrc = (client: MatrixClient, src: string, size: number) =>
 | 
			
		||||
  src?.startsWith("mxc://") ? client && getAvatarUrl(client, src, size) : src;
 | 
			
		||||
 | 
			
		||||
interface Props extends React.HTMLAttributes<HTMLDivElement> {
 | 
			
		||||
  bgKey?: string;
 | 
			
		||||
  src: string;
 | 
			
		||||
  fallback: string;
 | 
			
		||||
  size?: number;
 | 
			
		||||
  size?: Size | number;
 | 
			
		||||
  className: string;
 | 
			
		||||
  style: React.CSSProperties;
 | 
			
		||||
  style?: CSSProperties;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export const Avatar: React.FC<Props> = ({
 | 
			
		||||
  bgKey,
 | 
			
		||||
  src,
 | 
			
		||||
  fallback,
 | 
			
		||||
  size,
 | 
			
		||||
  size = Size.MD,
 | 
			
		||||
  className,
 | 
			
		||||
  style,
 | 
			
		||||
  style = {},
 | 
			
		||||
  ...rest
 | 
			
		||||
}) => {
 | 
			
		||||
  const { client } = useClient();
 | 
			
		||||
 | 
			
		||||
  const [sizeClass, sizePx, sizeStyle] = useMemo(
 | 
			
		||||
    () =>
 | 
			
		||||
      Object.values(Size).includes(size as Size)
 | 
			
		||||
        ? [styles[size as string], sizes.get(size as Size), {}]
 | 
			
		||||
        : [
 | 
			
		||||
            null,
 | 
			
		||||
            size as number,
 | 
			
		||||
            {
 | 
			
		||||
              width: size,
 | 
			
		||||
              height: size,
 | 
			
		||||
              borderRadius: size,
 | 
			
		||||
              fontSize: Math.round((size as number) / 2),
 | 
			
		||||
            },
 | 
			
		||||
          ],
 | 
			
		||||
    [size]
 | 
			
		||||
  );
 | 
			
		||||
 | 
			
		||||
  const resolvedSrc = useMemo(
 | 
			
		||||
    () => resolveAvatarSrc(client, src, sizePx),
 | 
			
		||||
    [client, src, sizePx]
 | 
			
		||||
  );
 | 
			
		||||
 | 
			
		||||
  const backgroundColor = useMemo(() => {
 | 
			
		||||
    const index = hashStringToArrIndex(
 | 
			
		||||
      bgKey || fallback || src || "",
 | 
			
		||||
| 
						 | 
				
			
			@ -53,12 +99,12 @@ export const Avatar: React.FC<Props> = ({
 | 
			
		|||
  /* eslint-disable jsx-a11y/alt-text */
 | 
			
		||||
  return (
 | 
			
		||||
    <div
 | 
			
		||||
      className={classNames(styles.avatar, styles[size || "md"], className)}
 | 
			
		||||
      style={{ backgroundColor, ...style }}
 | 
			
		||||
      className={classNames(styles.avatar, sizeClass, className)}
 | 
			
		||||
      style={{ backgroundColor, ...sizeStyle, ...style }}
 | 
			
		||||
      {...rest}
 | 
			
		||||
    >
 | 
			
		||||
      {src ? (
 | 
			
		||||
        <img src={src} />
 | 
			
		||||
      {resolvedSrc ? (
 | 
			
		||||
        <img src={resolvedSrc} />
 | 
			
		||||
      ) : typeof fallback === "string" ? (
 | 
			
		||||
        <span>{fallback}</span>
 | 
			
		||||
      ) : (
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,8 +1,7 @@
 | 
			
		|||
import React from "react";
 | 
			
		||||
import styles from "./Facepile.module.css";
 | 
			
		||||
import classNames from "classnames";
 | 
			
		||||
import { Avatar } from "./Avatar";
 | 
			
		||||
import { getAvatarUrl } from "./matrix-utils";
 | 
			
		||||
import { Avatar, sizes } from "./Avatar";
 | 
			
		||||
 | 
			
		||||
const overlapMap = {
 | 
			
		||||
  xs: 2,
 | 
			
		||||
| 
						 | 
				
			
			@ -10,12 +9,6 @@ const overlapMap = {
 | 
			
		|||
  md: 8,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const sizeMap = {
 | 
			
		||||
  xs: 24,
 | 
			
		||||
  sm: 32,
 | 
			
		||||
  md: 36,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export function Facepile({
 | 
			
		||||
  className,
 | 
			
		||||
  client,
 | 
			
		||||
| 
						 | 
				
			
			@ -24,7 +17,7 @@ export function Facepile({
 | 
			
		|||
  size,
 | 
			
		||||
  ...rest
 | 
			
		||||
}) {
 | 
			
		||||
  const _size = sizeMap[size];
 | 
			
		||||
  const _size = sizes.get(size);
 | 
			
		||||
  const _overlap = overlapMap[size];
 | 
			
		||||
 | 
			
		||||
  return (
 | 
			
		||||
| 
						 | 
				
			
			@ -40,7 +33,7 @@ export function Facepile({
 | 
			
		|||
          <Avatar
 | 
			
		||||
            key={member.userId}
 | 
			
		||||
            size={size}
 | 
			
		||||
            src={avatarUrl && getAvatarUrl(client, avatarUrl, _size)}
 | 
			
		||||
            src={avatarUrl}
 | 
			
		||||
            fallback={member.name.slice(0, 1).toUpperCase()}
 | 
			
		||||
            className={styles.avatar}
 | 
			
		||||
            style={{ left: i * (_size - _overlap) }}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -57,12 +57,13 @@ export function HeaderLogo({ className }) {
 | 
			
		|||
  );
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export function RoomHeaderInfo({ roomName }) {
 | 
			
		||||
export function RoomHeaderInfo({ roomName, avatarUrl }) {
 | 
			
		||||
  return (
 | 
			
		||||
    <>
 | 
			
		||||
      <div className={styles.roomAvatar}>
 | 
			
		||||
        <Avatar
 | 
			
		||||
          size="md"
 | 
			
		||||
          src={avatarUrl}
 | 
			
		||||
          bgKey={roomName}
 | 
			
		||||
          fallback={roomName.slice(0, 1).toUpperCase()}
 | 
			
		||||
        />
 | 
			
		||||
| 
						 | 
				
			
			@ -73,13 +74,13 @@ export function RoomHeaderInfo({ roomName }) {
 | 
			
		|||
  );
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export function RoomSetupHeaderInfo({ roomName, ...rest }) {
 | 
			
		||||
export function RoomSetupHeaderInfo({ roomName, avatarUrl, ...rest }) {
 | 
			
		||||
  const ref = useRef();
 | 
			
		||||
  const { buttonProps } = useButton(rest, ref);
 | 
			
		||||
  return (
 | 
			
		||||
    <button className={styles.backButton} ref={ref} {...buttonProps}>
 | 
			
		||||
      <ArrowLeftIcon width={16} height={16} />
 | 
			
		||||
      <RoomHeaderInfo roomName={roomName} />
 | 
			
		||||
      <RoomHeaderInfo roomName={roomName} avatarUrl={avatarUrl} />
 | 
			
		||||
    </button>
 | 
			
		||||
  );
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -79,7 +79,7 @@ export function useGroupCallRooms(client) {
 | 
			
		|||
        return {
 | 
			
		||||
          roomId: room.getCanonicalAlias() || room.roomId,
 | 
			
		||||
          roomName: room.name,
 | 
			
		||||
          avatarUrl: null,
 | 
			
		||||
          avatarUrl: room.getMxcAvatarUrl(),
 | 
			
		||||
          room,
 | 
			
		||||
          groupCall,
 | 
			
		||||
          participants: [...groupCall.participants],
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -15,7 +15,6 @@ limitations under the License.
 | 
			
		|||
*/
 | 
			
		||||
 | 
			
		||||
import { useState, useCallback, useEffect } from "react";
 | 
			
		||||
import { getAvatarUrl } from "../matrix-utils";
 | 
			
		||||
 | 
			
		||||
export function useProfile(client) {
 | 
			
		||||
  const [{ loading, displayName, avatarUrl, error, success }, setState] =
 | 
			
		||||
| 
						 | 
				
			
			@ -26,7 +25,7 @@ export function useProfile(client) {
 | 
			
		|||
        success: false,
 | 
			
		||||
        loading: false,
 | 
			
		||||
        displayName: user?.rawDisplayName,
 | 
			
		||||
        avatarUrl: user && client && getAvatarUrl(client, user.avatarUrl),
 | 
			
		||||
        avatarUrl: user?.avatarUrl,
 | 
			
		||||
        error: null,
 | 
			
		||||
      };
 | 
			
		||||
    });
 | 
			
		||||
| 
						 | 
				
			
			@ -37,7 +36,7 @@ export function useProfile(client) {
 | 
			
		|||
        success: false,
 | 
			
		||||
        loading: false,
 | 
			
		||||
        displayName,
 | 
			
		||||
        avatarUrl: getAvatarUrl(client, avatarUrl),
 | 
			
		||||
        avatarUrl,
 | 
			
		||||
        error: null,
 | 
			
		||||
      });
 | 
			
		||||
    };
 | 
			
		||||
| 
						 | 
				
			
			@ -84,11 +83,7 @@ export function useProfile(client) {
 | 
			
		|||
          setState((prev) => ({
 | 
			
		||||
            ...prev,
 | 
			
		||||
            displayName,
 | 
			
		||||
            avatarUrl: removeAvatar
 | 
			
		||||
              ? null
 | 
			
		||||
              : mxcAvatarUrl
 | 
			
		||||
              ? getAvatarUrl(client, mxcAvatarUrl)
 | 
			
		||||
              : prev.avatarUrl,
 | 
			
		||||
            avatarUrl: removeAvatar ? null : mxcAvatarUrl ?? prev.avatarUrl,
 | 
			
		||||
            loading: false,
 | 
			
		||||
            success: true,
 | 
			
		||||
          }));
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -23,6 +23,7 @@ import { LobbyView } from "./LobbyView";
 | 
			
		|||
import { InCallView } from "./InCallView";
 | 
			
		||||
import { PTTCallView } from "./PTTCallView";
 | 
			
		||||
import { CallEndedView } from "./CallEndedView";
 | 
			
		||||
import { useRoomAvatar } from "./useRoomAvatar";
 | 
			
		||||
import { useSentryGroupCallHandler } from "./useSentryGroupCallHandler";
 | 
			
		||||
import { useLocationNavigation } from "../useLocationNavigation";
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -67,6 +68,8 @@ export function GroupCallView({
 | 
			
		|||
    participants,
 | 
			
		||||
  } = useGroupCall(groupCall);
 | 
			
		||||
 | 
			
		||||
  const avatarUrl = useRoomAvatar(groupCall.room);
 | 
			
		||||
 | 
			
		||||
  useEffect(() => {
 | 
			
		||||
    window.groupCall = groupCall;
 | 
			
		||||
  }, [groupCall]);
 | 
			
		||||
| 
						 | 
				
			
			@ -96,6 +99,7 @@ export function GroupCallView({
 | 
			
		|||
          client={client}
 | 
			
		||||
          roomId={roomId}
 | 
			
		||||
          roomName={groupCall.room.name}
 | 
			
		||||
          avatarUrl={avatarUrl}
 | 
			
		||||
          groupCall={groupCall}
 | 
			
		||||
          participants={participants}
 | 
			
		||||
          userMediaFeeds={userMediaFeeds}
 | 
			
		||||
| 
						 | 
				
			
			@ -110,6 +114,7 @@ export function GroupCallView({
 | 
			
		|||
          groupCall={groupCall}
 | 
			
		||||
          client={client}
 | 
			
		||||
          roomName={groupCall.room.name}
 | 
			
		||||
          avatarUrl={avatarUrl}
 | 
			
		||||
          microphoneMuted={microphoneMuted}
 | 
			
		||||
          localVideoMuted={localVideoMuted}
 | 
			
		||||
          toggleLocalVideoMuted={toggleLocalVideoMuted}
 | 
			
		||||
| 
						 | 
				
			
			@ -142,6 +147,7 @@ export function GroupCallView({
 | 
			
		|||
        groupCall={groupCall}
 | 
			
		||||
        hasLocalParticipant={hasLocalParticipant}
 | 
			
		||||
        roomName={groupCall.room.name}
 | 
			
		||||
        avatarUrl={avatarUrl}
 | 
			
		||||
        state={state}
 | 
			
		||||
        onInitLocalCallFeed={initLocalCallFeed}
 | 
			
		||||
        localCallFeed={localCallFeed}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -25,7 +25,6 @@ import {
 | 
			
		|||
import { Header, LeftNav, RightNav, RoomHeaderInfo } from "../Header";
 | 
			
		||||
import { VideoGrid, useVideoGridLayout } from "../video-grid/VideoGrid";
 | 
			
		||||
import { VideoTileContainer } from "../video-grid/VideoTileContainer";
 | 
			
		||||
import { getAvatarUrl } from "../matrix-utils";
 | 
			
		||||
import { GroupCallInspector } from "./GroupCallInspector";
 | 
			
		||||
import { OverflowMenu } from "./OverflowMenu";
 | 
			
		||||
import { GridLayoutMenu } from "./GridLayoutMenu";
 | 
			
		||||
| 
						 | 
				
			
			@ -46,6 +45,7 @@ export function InCallView({
 | 
			
		|||
  client,
 | 
			
		||||
  groupCall,
 | 
			
		||||
  roomName,
 | 
			
		||||
  avatarUrl,
 | 
			
		||||
  microphoneMuted,
 | 
			
		||||
  localVideoMuted,
 | 
			
		||||
  toggleLocalVideoMuted,
 | 
			
		||||
| 
						 | 
				
			
			@ -125,13 +125,8 @@ export function InCallView({
 | 
			
		|||
      return (
 | 
			
		||||
        <Avatar
 | 
			
		||||
          key={roomMember.userId}
 | 
			
		||||
          style={{
 | 
			
		||||
            width: size,
 | 
			
		||||
            height: size,
 | 
			
		||||
            borderRadius: size,
 | 
			
		||||
            fontSize: Math.round(size / 2),
 | 
			
		||||
          }}
 | 
			
		||||
          src={avatarUrl && getAvatarUrl(client, avatarUrl, 96)}
 | 
			
		||||
          size={size}
 | 
			
		||||
          src={avatarUrl}
 | 
			
		||||
          fallback={roomMember.name.slice(0, 1).toUpperCase()}
 | 
			
		||||
          className={styles.avatar}
 | 
			
		||||
        />
 | 
			
		||||
| 
						 | 
				
			
			@ -149,7 +144,7 @@ export function InCallView({
 | 
			
		|||
    <div className={styles.inRoom}>
 | 
			
		||||
      <Header>
 | 
			
		||||
        <LeftNav>
 | 
			
		||||
          <RoomHeaderInfo roomName={roomName} />
 | 
			
		||||
          <RoomHeaderInfo roomName={roomName} avatarUrl={avatarUrl} />
 | 
			
		||||
        </LeftNav>
 | 
			
		||||
        <RightNav>
 | 
			
		||||
          <GridLayoutMenu layout={layout} setLayout={setLayout} />
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -32,6 +32,7 @@ export function LobbyView({
 | 
			
		|||
  client,
 | 
			
		||||
  groupCall,
 | 
			
		||||
  roomName,
 | 
			
		||||
  avatarUrl,
 | 
			
		||||
  state,
 | 
			
		||||
  onInitLocalCallFeed,
 | 
			
		||||
  onEnter,
 | 
			
		||||
| 
						 | 
				
			
			@ -72,7 +73,7 @@ export function LobbyView({
 | 
			
		|||
    <div className={styles.room}>
 | 
			
		||||
      <Header>
 | 
			
		||||
        <LeftNav>
 | 
			
		||||
          <RoomHeaderInfo roomName={roomName} />
 | 
			
		||||
          <RoomHeaderInfo roomName={roomName} avatarUrl={avatarUrl} />
 | 
			
		||||
        </LeftNav>
 | 
			
		||||
        <RightNav>
 | 
			
		||||
          <UserMenuContainer />
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -148,12 +148,7 @@ export const PTTButton: React.FC<Props> = ({
 | 
			
		|||
      ) : (
 | 
			
		||||
        <Avatar
 | 
			
		||||
          key={activeSpeakerUserId}
 | 
			
		||||
          style={{
 | 
			
		||||
            width: size - 12,
 | 
			
		||||
            height: size - 12,
 | 
			
		||||
            borderRadius: size - 12,
 | 
			
		||||
            fontSize: Math.round((size - 12) / 2),
 | 
			
		||||
          }}
 | 
			
		||||
          size={size - 12}
 | 
			
		||||
          src={activeSpeakerAvatarUrl}
 | 
			
		||||
          fallback={activeSpeakerDisplayName.slice(0, 1).toUpperCase()}
 | 
			
		||||
          className={styles.avatar}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -33,7 +33,6 @@ import { useMediaHandler } from "../settings/useMediaHandler";
 | 
			
		|||
import { usePTT } from "./usePTT";
 | 
			
		||||
import { Timer } from "./Timer";
 | 
			
		||||
import { Toggle } from "../input/Toggle";
 | 
			
		||||
import { getAvatarUrl } from "../matrix-utils";
 | 
			
		||||
import { ReactComponent as AudioIcon } from "../icons/Audio.svg";
 | 
			
		||||
import { usePTTSounds } from "../sound/usePttSounds";
 | 
			
		||||
import { PTTClips } from "../sound/PTTClips";
 | 
			
		||||
| 
						 | 
				
			
			@ -79,6 +78,7 @@ interface Props {
 | 
			
		|||
  client: MatrixClient;
 | 
			
		||||
  roomId: string;
 | 
			
		||||
  roomName: string;
 | 
			
		||||
  avatarUrl: string;
 | 
			
		||||
  groupCall: GroupCall;
 | 
			
		||||
  participants: RoomMember[];
 | 
			
		||||
  userMediaFeeds: CallFeed[];
 | 
			
		||||
| 
						 | 
				
			
			@ -91,6 +91,7 @@ export const PTTCallView: React.FC<Props> = ({
 | 
			
		|||
  client,
 | 
			
		||||
  roomId,
 | 
			
		||||
  roomName,
 | 
			
		||||
  avatarUrl,
 | 
			
		||||
  groupCall,
 | 
			
		||||
  participants,
 | 
			
		||||
  userMediaFeeds,
 | 
			
		||||
| 
						 | 
				
			
			@ -105,7 +106,6 @@ export const PTTCallView: React.FC<Props> = ({
 | 
			
		|||
  const [containerRef, bounds] = useMeasure({ polyfill: ResizeObserver });
 | 
			
		||||
  const facepileSize = bounds.width < 800 ? "sm" : "md";
 | 
			
		||||
  const pttButtonSize = 232;
 | 
			
		||||
  const pttBorderWidth = 6;
 | 
			
		||||
 | 
			
		||||
  const { audioOutput } = useMediaHandler();
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -135,13 +135,7 @@ export const PTTCallView: React.FC<Props> = ({
 | 
			
		|||
  const activeSpeakerUser = activeSpeakerUserId
 | 
			
		||||
    ? client.getUser(activeSpeakerUserId)
 | 
			
		||||
    : null;
 | 
			
		||||
  const activeSpeakerAvatarUrl = activeSpeakerUser
 | 
			
		||||
    ? getAvatarUrl(
 | 
			
		||||
        client,
 | 
			
		||||
        activeSpeakerUser.avatarUrl,
 | 
			
		||||
        pttButtonSize - pttBorderWidth * 2
 | 
			
		||||
      )
 | 
			
		||||
    : null;
 | 
			
		||||
  const activeSpeakerAvatarUrl = activeSpeakerUser?.avatarUrl;
 | 
			
		||||
  const activeSpeakerDisplayName = activeSpeakerUser
 | 
			
		||||
    ? activeSpeakerUser.displayName
 | 
			
		||||
    : "";
 | 
			
		||||
| 
						 | 
				
			
			@ -156,7 +150,11 @@ export const PTTCallView: React.FC<Props> = ({
 | 
			
		|||
      />
 | 
			
		||||
      <Header className={styles.header}>
 | 
			
		||||
        <LeftNav>
 | 
			
		||||
          <RoomSetupHeaderInfo roomName={roomName} onPress={onLeave} />
 | 
			
		||||
          <RoomSetupHeaderInfo
 | 
			
		||||
            roomName={roomName}
 | 
			
		||||
            avatarUrl={avatarUrl}
 | 
			
		||||
            onPress={onLeave}
 | 
			
		||||
          />
 | 
			
		||||
        </LeftNav>
 | 
			
		||||
        <RightNav />
 | 
			
		||||
      </Header>
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -62,12 +62,7 @@ export function VideoPreview({
 | 
			
		|||
          {localVideoMuted && (
 | 
			
		||||
            <div className={styles.avatarContainer}>
 | 
			
		||||
              <Avatar
 | 
			
		||||
                style={{
 | 
			
		||||
                  width: avatarSize,
 | 
			
		||||
                  height: avatarSize,
 | 
			
		||||
                  borderRadius: avatarSize,
 | 
			
		||||
                  fontSize: Math.round(avatarSize / 2),
 | 
			
		||||
                }}
 | 
			
		||||
                size={avatarSize}
 | 
			
		||||
                src={avatarUrl}
 | 
			
		||||
                fallback={displayName.slice(0, 1).toUpperCase()}
 | 
			
		||||
              />
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										24
									
								
								src/room/useRoomAvatar.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										24
									
								
								src/room/useRoomAvatar.ts
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,24 @@
 | 
			
		|||
import { useState, useEffect } from "react";
 | 
			
		||||
import { MatrixEvent } from "matrix-js-sdk/src/models/event";
 | 
			
		||||
import { Room } from "matrix-js-sdk/src/models/room";
 | 
			
		||||
import { RoomStateEvent } from "matrix-js-sdk/src/models/room-state";
 | 
			
		||||
import { EventType } from "matrix-js-sdk/src/@types/event";
 | 
			
		||||
 | 
			
		||||
export const useRoomAvatar = (room: Room) => {
 | 
			
		||||
  const [avatarUrl, setAvatarUrl] = useState(room.getMxcAvatarUrl());
 | 
			
		||||
 | 
			
		||||
  useEffect(() => {
 | 
			
		||||
    const update = (ev: MatrixEvent) => {
 | 
			
		||||
      if (ev.getType() === EventType.RoomAvatar) {
 | 
			
		||||
        setAvatarUrl(room.getMxcAvatarUrl());
 | 
			
		||||
      }
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    room.currentState.on(RoomStateEvent.Events, update);
 | 
			
		||||
    return () => {
 | 
			
		||||
      room.currentState.off(RoomStateEvent.Events, update);
 | 
			
		||||
    };
 | 
			
		||||
  }, [room]);
 | 
			
		||||
 | 
			
		||||
  return avatarUrl;
 | 
			
		||||
};
 | 
			
		||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue