diff --git a/src/room/InCallView.tsx b/src/room/InCallView.tsx index 6382cb8..f921ade 100644 --- a/src/room/InCallView.tsx +++ b/src/room/InCallView.tsx @@ -34,7 +34,6 @@ import { useTranslation } from "react-i18next"; import useMeasure from "react-use-measure"; import type { IWidgetApiRequest } from "matrix-widget-api"; -import { Avatar } from "../Avatar"; import { Header, LeftNav, @@ -236,24 +235,6 @@ export function InCallView({ const reducedControls = boundsValid && bounds.width <= 400; const noControls = reducedControls && bounds.height <= 400; - const renderAvatar = useCallback( - (roomMember: RoomMember, width: number, height: number) => { - const avatarUrl = roomMember.getMxcAvatarUrl(); - const size = Math.round(Math.min(width, height) / 2); - - return ( - - ); - }, - [] - ); - const prefersReducedMotion = usePrefersReducedMotion(); const items = useParticipantTiles(livekitRoom, participants); @@ -273,13 +254,7 @@ export function InCallView({ layout={layout} disableAnimations={prefersReducedMotion || isSafari} > - {(child) => ( - - )} + {(child) => } ); }; @@ -375,17 +350,21 @@ function useParticipantTiles( }); const items = useMemo(() => { - const tiles: TileDescriptor[] = []; + // The IDs of the participants who published membership event to the room (i.e. are present from Matrix perspective). + const matrixParticipants: Map = new Map( + [...participants.entries()].flatMap(([user, devicesMap]) => { + return [...devicesMap.keys()].map((deviceId) => [ + `${user.userId}:${deviceId}`, + user, + ]); + }) + ); - for (const [member, participantMap] of participants) { - for (const [deviceId] of participantMap) { - const id = `${member.userId}:${deviceId}`; - const sfuParticipant = sfuParticipants.find((p) => p.identity === id); - - // Skip rendering participants that did not connect to the SFU. - if (!sfuParticipant) { - continue; - } + // Iterate over SFU participants (those who actually are present from the SFU perspective) and create tiles for them. + const tiles: TileDescriptor[] = sfuParticipants.flatMap( + (sfuParticipant) => { + const id = sfuParticipant.identity; + const member = matrixParticipants.get(id); const userMediaTile = { id, @@ -398,12 +377,10 @@ function useParticipantTiles( }, }; - // Add a tile for user media. - tiles.push(userMediaTile); - // If there is a screen sharing enabled for this participant, create a tile for it as well. + let screenShareTile: TileDescriptor | undefined; if (sfuParticipant.isScreenShareEnabled) { - const screenShareTile = { + screenShareTile = { ...userMediaTile, id: `${id}:screen-share`, focused: true, @@ -412,10 +389,13 @@ function useParticipantTiles( content: TileContent.ScreenShare, }, }; - tiles.push(screenShareTile); } + + return screenShareTile + ? [userMediaTile, screenShareTile] + : [userMediaTile]; } - } + ); PosthogAnalytics.instance.eventCallEnded.cacheParticipantCountChanged( tiles.length diff --git a/src/video-grid/VideoTileContainer.tsx b/src/video-grid/VideoTileContainer.tsx index 4f2c10e..cee56da 100644 --- a/src/video-grid/VideoTileContainer.tsx +++ b/src/video-grid/VideoTileContainer.tsx @@ -15,14 +15,18 @@ limitations under the License. */ import React from "react"; -import { RoomMember } from "matrix-js-sdk/src/models/room-member"; +import { + RoomMember, + RoomMemberEvent, +} from "matrix-js-sdk/src/models/room-member"; import { LocalParticipant, RemoteParticipant } from "livekit-client"; -import { useRoomMemberName } from "./useRoomMemberName"; import { TileContent, VideoTile } from "./VideoTile"; +import { Avatar } from "../Avatar"; +import Styles from "../room/InCallView.module.css"; export interface ItemData { - member: RoomMember; + member?: RoomMember; sfuParticipant: LocalParticipant | RemoteParticipant; content: TileContent; } @@ -31,29 +35,45 @@ interface Props { item: ItemData; width?: number; height?: number; - getAvatar: ( - roomMember: RoomMember, - width: number, - height: number - ) => JSX.Element; } -export function VideoTileContainer({ - item, - width, - height, - getAvatar, - ...rest -}: Props) { - const { rawDisplayName } = useRoomMemberName(item.member); +export function VideoTileContainer({ item, width, height, ...rest }: Props) { + const [displayName, setDisplayName] = React.useState("[👻]"); + + React.useEffect(() => { + const member = item.member; + + if (member) { + setDisplayName(member.rawDisplayName); + + const updateName = () => { + setDisplayName(member.rawDisplayName); + }; + + member!.on(RoomMemberEvent.Name, updateName); + return () => { + member!.removeListener(RoomMemberEvent.Name, updateName); + }; + } + }, [item.member]); + + const avatar = ( + + ); return ( <> diff --git a/src/video-grid/useRoomMemberName.ts b/src/video-grid/useRoomMemberName.ts deleted file mode 100644 index 9c1d5cb..0000000 --- a/src/video-grid/useRoomMemberName.ts +++ /dev/null @@ -1,48 +0,0 @@ -/* -Copyright 2022 New Vector Ltd - -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, - RoomMemberEvent, -} from "matrix-js-sdk/src/models/room-member"; -import { useState, useEffect } from "react"; - -interface RoomMemberName { - name: string; - rawDisplayName: string; -} -export function useRoomMemberName(member: RoomMember): RoomMemberName { - const [state, setState] = useState({ - name: member.name, - rawDisplayName: member.rawDisplayName, - }); - - useEffect(() => { - function updateName() { - setState({ name: member.name, rawDisplayName: member.rawDisplayName }); - } - - updateName(); - - member.on(RoomMemberEvent.Name, updateName); - - return () => { - member.removeListener(RoomMemberEvent.Name, updateName); - }; - }, [member]); - - return state; -}