Element Call video rooms (#9267)

* Add an element_call_url config option

* Add a labs flag for Element Call video rooms

* Add Element Call as another video rooms backend

* Consolidate event power level defaults

* Remember to clean up participantsExpirationTimer

* Fix a code smell

* Test the clean method

* Fix some strict mode errors

* Test that clean still works when there are no state events

* Test auto-approval of Element Call widget capabilities

* Deduplicate some code to placate SonarCloud

* Fix more strict mode errors

* Test that calls disconnect when leaving the room

* Test the get methods of JitsiCall and ElementCall more

* Test Call.ts even more

* Test creation of Element video rooms

* Test that createRoom works for non-video-rooms

* Test Call's get method rather than the methods of derived classes

* Ensure that the clean method is able to preserve devices

* Remove duplicate clean method

* Fix lints

* Fix some strict mode errors in RoomPreviewCard

* Test RoomPreviewCard changes

* Quick and dirty hotfix for the community testing session

* Revert "Quick and dirty hotfix for the community testing session"

This reverts commit 37056514fbc040aaf1bff2539da770a1c8ba72a2.

* Fix the event schema for org.matrix.msc3401.call.member devices

* Remove org.matrix.call_duplicate_session from Element Call capabilities

It's no longer used by Element Call when running as a widget.

* Replace element_call_url with a map

* Make PiPs work for virtual widgets

* Auto-approve room timeline capability

Because Element Call uses this now

* Create a reusable isVideoRoom util
This commit is contained in:
Robin 2022-09-16 11:12:27 -04:00 committed by GitHub
parent db5716b776
commit cb735c9439
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
37 changed files with 1699 additions and 1384 deletions

View file

@ -47,6 +47,7 @@ import RoomLiveShareWarning from '../beacon/RoomLiveShareWarning';
import { BetaPill } from "../beta/BetaCard";
import RightPanelStore from "../../../stores/right-panel/RightPanelStore";
import { UPDATE_EVENT } from "../../../stores/AsyncStore";
import { isVideoRoom as calcIsVideoRoom } from "../../../utils/video-rooms";
export interface ISearchInfo {
searchTerm: string;
@ -312,7 +313,7 @@ export default class RoomHeader extends React.Component<IProps, IState> {
const e2eIcon = this.props.e2eStatus ? <E2EIcon status={this.props.e2eStatus} /> : undefined;
const isVideoRoom = SettingsStore.getValue("feature_video_rooms") && this.props.room.isElementVideoRoom();
const isVideoRoom = SettingsStore.getValue("feature_video_rooms") && calcIsVideoRoom(this.props.room);
const viewLabs = () => defaultDispatcher.dispatch({
action: Action.ViewUserSettings,
initialTabId: UserTab.Labs,

View file

@ -23,6 +23,7 @@ import RightPanelStore from "../../../stores/right-panel/RightPanelStore";
import { RightPanelPhases } from "../../../stores/right-panel/RightPanelStorePhases";
import { useAsyncMemo } from "../../../hooks/useAsyncMemo";
import { useRoomState } from "../../../hooks/useRoomState";
import { useFeatureEnabled } from "../../../hooks/useSettings";
import { useRoomMemberCount, useMyRoomMembership } from "../../../hooks/useRoomMembers";
import AccessibleButton from "../elements/AccessibleButton";
@ -44,9 +45,12 @@ const RoomInfoLine: FC<IProps> = ({ room }) => {
const membership = useMyRoomMembership(room);
const memberCount = useRoomMemberCount(room);
const elementCallVideoRoomsEnabled = useFeatureEnabled("feature_element_call_video_rooms");
const isVideoRoom = room.isElementVideoRoom() || (elementCallVideoRoomsEnabled && room.isCallRoom());
let iconClass: string;
let roomType: string;
if (room.isElementVideoRoom()) {
if (isVideoRoom) {
iconClass = "mx_RoomInfoLine_video";
roomType = _t("Video room");
} else if (joinRule === JoinRule.Public) {

View file

@ -32,6 +32,7 @@ import { _t, _td } from "../../../languageHandler";
import { MatrixClientPeg } from "../../../MatrixClientPeg";
import PosthogTrackers from "../../../PosthogTrackers";
import SettingsStore from "../../../settings/SettingsStore";
import { useFeatureEnabled } from "../../../hooks/useSettings";
import { UIComponent } from "../../../settings/UIFeature";
import { RoomNotificationStateStore } from "../../../stores/notifications/RoomNotificationStateStore";
import { ITagMap } from "../../../stores/room-list/algorithms/models";
@ -200,8 +201,10 @@ const UntaggedAuxButton = ({ tabIndex }: IAuxButtonProps) => {
});
const showCreateRoom = shouldShowComponent(UIComponent.CreateRooms);
const videoRoomsEnabled = useFeatureEnabled("feature_video_rooms");
const elementCallVideoRoomsEnabled = useFeatureEnabled("feature_element_call_video_rooms");
let contextMenuContent: JSX.Element;
let contextMenuContent: JSX.Element | null = null;
if (menuDisplayed && activeSpace) {
const canAddRooms = activeSpace.currentState.maySendStateEvent(EventType.SpaceChild,
MatrixClientPeg.get().getUserId());
@ -239,7 +242,7 @@ const UntaggedAuxButton = ({ tabIndex }: IAuxButtonProps) => {
tooltip={canAddRooms ? undefined
: _t("You do not have permissions to create new rooms in this space")}
/>
{ SettingsStore.getValue("feature_video_rooms") && (
{ videoRoomsEnabled && (
<IconizedContextMenuOption
label={_t("New video room")}
iconClassName="mx_RoomList_iconNewVideoRoom"
@ -247,7 +250,10 @@ const UntaggedAuxButton = ({ tabIndex }: IAuxButtonProps) => {
e.preventDefault();
e.stopPropagation();
closeMenu();
showCreateNewRoom(activeSpace, RoomType.ElementVideo);
showCreateNewRoom(
activeSpace,
elementCallVideoRoomsEnabled ? RoomType.UnstableCall : RoomType.ElementVideo,
);
}}
disabled={!canAddRooms}
tooltip={canAddRooms ? undefined
@ -287,7 +293,7 @@ const UntaggedAuxButton = ({ tabIndex }: IAuxButtonProps) => {
PosthogTrackers.trackInteraction("WebRoomListRoomsSublistPlusMenuCreateRoomItem", e);
}}
/>
{ SettingsStore.getValue("feature_video_rooms") && (
{ videoRoomsEnabled && (
<IconizedContextMenuOption
label={_t("New video room")}
iconClassName="mx_RoomList_iconNewVideoRoom"
@ -297,7 +303,7 @@ const UntaggedAuxButton = ({ tabIndex }: IAuxButtonProps) => {
closeMenu();
defaultDispatcher.dispatch({
action: "view_create_room",
type: RoomType.ElementVideo,
type: elementCallVideoRoomsEnabled ? RoomType.UnstableCall : RoomType.ElementVideo,
});
}}
>
@ -319,7 +325,7 @@ const UntaggedAuxButton = ({ tabIndex }: IAuxButtonProps) => {
</IconizedContextMenuOptionList>;
}
let contextMenu: JSX.Element;
let contextMenu: JSX.Element | null = null;
if (menuDisplayed) {
contextMenu = <IconizedContextMenu {...auxButtonContextMenuPosition(handle)} onFinished={closeMenu} compact>
{ contextMenuContent }

View file

@ -127,6 +127,7 @@ const RoomListHeader = ({ onVisibilityChange }: IProps) => {
return SpaceStore.instance.allRoomsInHome;
});
const videoRoomsEnabled = useFeatureEnabled("feature_video_rooms");
const elementCallVideoRoomsEnabled = useFeatureEnabled("feature_element_call_video_rooms");
const pendingActions = usePendingActions();
const canShowMainMenu = activeSpace || spaceKey === MetaSpace.Home;
@ -211,7 +212,10 @@ const RoomListHeader = ({ onVisibilityChange }: IProps) => {
onClick={(e) => {
e.preventDefault();
e.stopPropagation();
showCreateNewRoom(activeSpace, RoomType.ElementVideo);
showCreateNewRoom(
activeSpace,
elementCallVideoRoomsEnabled ? RoomType.UnstableCall : RoomType.ElementVideo,
);
closePlusMenu();
}}
>
@ -310,7 +314,7 @@ const RoomListHeader = ({ onVisibilityChange }: IProps) => {
e.stopPropagation();
defaultDispatcher.dispatch({
action: "view_create_room",
type: RoomType.ElementVideo,
type: elementCallVideoRoomsEnabled ? RoomType.UnstableCall : RoomType.ElementVideo,
});
closePlusMenu();
}}

View file

@ -51,6 +51,8 @@ interface IProps {
const RoomPreviewCard: FC<IProps> = ({ room, onJoinButtonClicked, onRejectButtonClicked }) => {
const cli = useContext(MatrixClientContext);
const videoRoomsEnabled = useFeatureEnabled("feature_video_rooms");
const elementCallVideoRoomsEnabled = useFeatureEnabled("feature_element_call_video_rooms");
const isVideoRoom = room.isElementVideoRoom() || (elementCallVideoRoomsEnabled && room.isCallRoom());
const myMembership = useMyRoomMembership(room);
useDispatcher(defaultDispatcher, payload => {
if (payload.action === Action.JoinRoomError && payload.roomId === room.roomId) {
@ -69,7 +71,7 @@ const RoomPreviewCard: FC<IProps> = ({ room, onJoinButtonClicked, onRejectButton
initialTabId: UserTab.Labs,
});
let inviterSection: JSX.Element;
let inviterSection: JSX.Element | null = null;
let joinButtons: JSX.Element;
if (myMembership === "join") {
joinButtons = (
@ -86,10 +88,11 @@ const RoomPreviewCard: FC<IProps> = ({ room, onJoinButtonClicked, onRejectButton
</AccessibleButton>
);
} else if (myMembership === "invite") {
const inviteSender = room.getMember(cli.getUserId())?.events.member?.getSender();
const inviter = inviteSender && room.getMember(inviteSender);
const inviteSender = room.getMember(cli.getUserId()!)?.events.member?.getSender();
if (inviteSender) {
const inviter = room.getMember(inviteSender);
inviterSection = <div className="mx_RoomPreviewCard_inviter">
<MemberAvatar member={inviter} fallbackUserId={inviteSender} width={32} height={32} />
<div>
@ -102,10 +105,6 @@ const RoomPreviewCard: FC<IProps> = ({ room, onJoinButtonClicked, onRejectButton
{ inviteSender }
</div> : null }
</div>
{ room.isElementVideoRoom()
? <BetaPill onClick={viewLabs} tooltipTitle={_t("Video rooms are a beta feature")} />
: null
}
</div>;
}
@ -152,10 +151,11 @@ const RoomPreviewCard: FC<IProps> = ({ room, onJoinButtonClicked, onRejectButton
}
let avatarRow: JSX.Element;
if (room.isElementVideoRoom()) {
if (isVideoRoom) {
avatarRow = <>
<RoomAvatar room={room} height={50} width={50} viewAvatarOnClick />
<div className="mx_RoomPreviewCard_video" />
<BetaPill onClick={viewLabs} tooltipTitle={_t("Video rooms are a beta feature")} />
</>;
} else if (room.isSpaceRoom()) {
avatarRow = <RoomAvatar room={room} height={80} width={80} viewAvatarOnClick />;
@ -163,12 +163,12 @@ const RoomPreviewCard: FC<IProps> = ({ room, onJoinButtonClicked, onRejectButton
avatarRow = <RoomAvatar room={room} height={50} width={50} viewAvatarOnClick />;
}
let notice: string;
let notice: string | null = null;
if (cannotJoin) {
notice = _t("To view %(roomName)s, you need an invite", {
roomName: room.name,
});
} else if (room.isElementVideoRoom() && !videoRoomsEnabled) {
} else if (isVideoRoom && !videoRoomsEnabled) {
notice = myMembership === "join"
? _t("To view, please enable video rooms in Labs first")
: _t("To join, please enable video rooms in Labs first");