From 62cd0f1beb31097505c82e6b61b5cb46211b813a Mon Sep 17 00:00:00 2001 From: David Baker Date: Mon, 27 Feb 2023 09:34:02 +0000 Subject: [PATCH] Use the room avatar as a placeholder in calls (#10231) * Use the room avatar as a placeholder in calls Rather than the image for the user we're in a call with. This makes it work correctly with virtual rooms easily since we'll get the avatar for the correct room. * Prettier * TS strict errors * More TS strict fixes * More strict TS * Prettier * Even more TS strict * more stricter --- src/components/views/voip/LegacyCallView.tsx | 32 +++++++-- src/components/views/voip/VideoFeed.tsx | 9 ++- test/components/views/voip/VideoFeed-test.tsx | 69 +++++++++++++++++++ 3 files changed, 102 insertions(+), 8 deletions(-) create mode 100644 test/components/views/voip/VideoFeed-test.tsx diff --git a/src/components/views/voip/LegacyCallView.tsx b/src/components/views/voip/LegacyCallView.tsx index 1e8b4621f9..2d989d296a 100644 --- a/src/components/views/voip/LegacyCallView.tsx +++ b/src/components/views/voip/LegacyCallView.tsx @@ -430,7 +430,8 @@ export default class LegacyCallView extends React.Component { const { pipMode, call, onResize } = this.props; const { isLocalOnHold, isRemoteOnHold, sidebarShown, primaryFeed, secondaryFeed, sidebarFeeds } = this.state; - const callRoom = MatrixClientPeg.get().getRoom(call.roomId) ?? undefined; + const callRoomId = LegacyCallHandler.instance.roomIdForCall(call); + const callRoom = (callRoomId ? MatrixClientPeg.get().getRoom(callRoomId) : undefined) ?? undefined; const avatarSize = pipMode ? 76 : 160; const transfereeCall = LegacyCallHandler.instance.getTransfereeForCallId(call.callId); const isOnHold = isLocalOnHold || isRemoteOnHold; @@ -527,23 +528,44 @@ export default class LegacyCallView extends React.Component { ); } else if (pipMode) { + // We've already checked that we have feeds so we cast away the optional when passing the feed return (
- +
); } else if (secondaryFeed) { return (
- + {secondaryFeedElement}
); } else { return (
- - {sidebarShown && } + + {sidebarShown && ( + + )}
); } diff --git a/src/components/views/voip/VideoFeed.tsx b/src/components/views/voip/VideoFeed.tsx index 35fb851f4a..503c53ec66 100644 --- a/src/components/views/voip/VideoFeed.tsx +++ b/src/components/views/voip/VideoFeed.tsx @@ -23,7 +23,9 @@ import { logger } from "matrix-js-sdk/src/logger"; import { SDPStreamMetadataPurpose } from "matrix-js-sdk/src/webrtc/callEventTypes"; import SettingsStore from "../../../settings/SettingsStore"; -import MemberAvatar from "../avatars/MemberAvatar"; +import LegacyCallHandler from "../../../LegacyCallHandler"; +import { MatrixClientPeg } from "../../../MatrixClientPeg"; +import RoomAvatar from "../avatars/RoomAvatar"; interface IProps { call: MatrixCall; @@ -197,7 +199,8 @@ export default class VideoFeed extends React.PureComponent { let content; if (this.state.videoMuted) { - const member = this.props.feed.getMember(); + const callRoomId = LegacyCallHandler.instance.roomIdForCall(this.props.call); + const callRoom = (callRoomId ? MatrixClientPeg.get().getRoom(callRoomId) : undefined) ?? undefined; let avatarSize; if (pipMode && primary) avatarSize = 76; @@ -205,7 +208,7 @@ export default class VideoFeed extends React.PureComponent { else if (!pipMode && primary) avatarSize = 160; else; // TBD - content = ; + content = ; } else { const videoClasses = classnames("mx_VideoFeed_video", { mx_VideoFeed_video_mirror: diff --git a/test/components/views/voip/VideoFeed-test.tsx b/test/components/views/voip/VideoFeed-test.tsx new file mode 100644 index 0000000000..85152437e0 --- /dev/null +++ b/test/components/views/voip/VideoFeed-test.tsx @@ -0,0 +1,69 @@ +/* +Copyright 2023 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 React from "react"; +import { render, screen } from "@testing-library/react"; +import { CallFeed } from "matrix-js-sdk/src/webrtc/callFeed"; +import { MatrixCall } from "matrix-js-sdk/src/webrtc/call"; +import { MatrixClient } from "matrix-js-sdk/src/client"; +import { Room } from "matrix-js-sdk/src/models/room"; + +import * as AvatarModule from "../../../../src/Avatar"; +import VideoFeed from "../../../../src/components/views/voip/VideoFeed"; +import { stubClient, useMockedCalls } from "../../../test-utils"; +import LegacyCallHandler from "../../../../src/LegacyCallHandler"; +import DMRoomMap from "../../../../src/utils/DMRoomMap"; + +const FAKE_AVATAR_URL = "http://fakeurl.dummy/fake.png"; + +describe("VideoFeed", () => { + useMockedCalls(); + + let client: MatrixClient; + + beforeAll(() => { + client = stubClient(); + (AvatarModule as any).avatarUrlForRoom = jest.fn().mockReturnValue(FAKE_AVATAR_URL); + + const dmRoomMap = new DMRoomMap(client); + jest.spyOn(dmRoomMap, "getUserIdForRoomId"); + jest.spyOn(DMRoomMap, "shared").mockReturnValue(dmRoomMap); + }); + + afterAll(() => { + jest.restoreAllMocks(); + }); + + it("Displays the room avatar when no video is available", () => { + window.mxLegacyCallHandler = { + roomIdForCall: jest.fn().mockReturnValue("!this:room.here"), + } as unknown as LegacyCallHandler; + + const mockCall = { + room: new Room("!room:example.com", client, client.getSafeUserId()), + }; + + const feed = { + isAudioMuted: jest.fn().mockReturnValue(false), + isVideoMuted: jest.fn().mockReturnValue(true), + addListener: jest.fn(), + removeListener: jest.fn(), + }; + render(); + const avatarImg = screen.getByRole("img"); + expect(avatarImg).toHaveAttribute("src", FAKE_AVATAR_URL); + }); +});