Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Element Call video rooms #9267

Merged
merged 38 commits into from
Sep 16, 2022
Merged
Show file tree
Hide file tree
Changes from 29 commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
7e36216
Add an element_call_url config option
robintown Sep 9, 2022
b60868e
Add a labs flag for Element Call video rooms
robintown Sep 9, 2022
2d0df69
Add Element Call as another video rooms backend
robintown Sep 9, 2022
bc30a58
Consolidate event power level defaults
robintown Sep 9, 2022
d7a69ca
Remember to clean up participantsExpirationTimer
robintown Sep 9, 2022
0815aff
Fix a code smell
robintown Sep 9, 2022
95ee3a4
Test the clean method
robintown Sep 9, 2022
946d172
Fix some strict mode errors
robintown Sep 9, 2022
50188c4
Test that clean still works when there are no state events
robintown Sep 9, 2022
ecde812
Test auto-approval of Element Call widget capabilities
robintown Sep 9, 2022
fa04ec5
Deduplicate some code to placate SonarCloud
robintown Sep 9, 2022
691d027
Fix more strict mode errors
robintown Sep 9, 2022
2c3b5a4
Test that calls disconnect when leaving the room
robintown Sep 11, 2022
ef1ac39
Test the get methods of JitsiCall and ElementCall more
robintown Sep 11, 2022
43c25a1
Test Call.ts even more
robintown Sep 11, 2022
4a50f3c
Test creation of Element video rooms
robintown Sep 11, 2022
07edd1e
Test that createRoom works for non-video-rooms
robintown Sep 11, 2022
6b77fd6
Test Call's get method rather than the methods of derived classes
robintown Sep 11, 2022
6c7fdf1
Ensure that the clean method is able to preserve devices
robintown Sep 11, 2022
2e0ad3a
Remove duplicate clean method
robintown Sep 11, 2022
fa2e5dd
Fix lints
robintown Sep 11, 2022
e47b5c9
Fix some strict mode errors in RoomPreviewCard
robintown Sep 11, 2022
c52ed57
Test RoomPreviewCard changes
robintown Sep 11, 2022
4d85e57
Merge branch 'develop' into ec-video-rooms
robintown Sep 12, 2022
3705651
Quick and dirty hotfix for the community testing session
robintown Sep 12, 2022
75adaf1
Merge branch 'develop' into ec-video-rooms
robintown Sep 12, 2022
78388c8
Revert "Quick and dirty hotfix for the community testing session"
robintown Sep 12, 2022
fa1dcb6
Fix the event schema for org.matrix.msc3401.call.member devices
robintown Sep 12, 2022
a5a38dd
Remove org.matrix.call_duplicate_session from Element Call capabilities
robintown Sep 12, 2022
eae8657
Merge branch 'develop' into ec-video-rooms
robintown Sep 14, 2022
e7a5144
Replace element_call_url with a map
robintown Sep 14, 2022
b55d931
Merge branch 'develop' into ec-video-rooms
robintown Sep 14, 2022
1ee4147
Make PiPs work for virtual widgets
robintown Sep 14, 2022
456f93d
Auto-approve room timeline capability
robintown Sep 15, 2022
419322c
Merge branch 'develop' into ec-video-rooms
robintown Sep 15, 2022
d44486c
Merge branch 'develop' into ec-video-rooms
robintown Sep 15, 2022
54269e7
Merge branch 'develop' into ec-video-rooms
robintown Sep 16, 2022
f52e305
Create a reusable isVideoRoom util
robintown Sep 16, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
13 changes: 7 additions & 6 deletions res/css/views/rooms/_RoomPreviewCard.pcss
Original file line number Diff line number Diff line change
Expand Up @@ -64,12 +64,6 @@ limitations under the License.
color: $secondary-content;
}
}

/* XXX Remove this when video rooms leave beta */
.mx_BetaCard_betaPill {
margin-inline-start: auto;
align-self: start;
}
}

.mx_RoomPreviewCard_avatar {
Expand Down Expand Up @@ -104,6 +98,13 @@ limitations under the License.
mask-image: url('$(res)/img/element-icons/call/video-call.svg');
}
}

/* XXX Remove this when video rooms leave beta */
.mx_BetaCard_betaPill {
position: absolute;
inset-block-start: $spacing-32;
inset-inline-end: $spacing-24;
}
}

h1.mx_RoomPreviewCard_name {
Expand Down
1 change: 1 addition & 0 deletions src/IConfigOptions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,7 @@ export interface IConfigOptions {
voip?: {
obey_asserted_identity?: boolean; // MSC3086
};
element_call_url?: string;

logout_redirect_url?: string;

Expand Down
1 change: 1 addition & 0 deletions src/SdkConfig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ export const DEFAULTS: IConfigOptions = {
jitsi: {
preferred_domain: "meet.element.io",
},
element_call_url: "https://call.element.io",
robintown marked this conversation as resolved.
Show resolved Hide resolved

// @ts-ignore - we deliberately use the camelCase version here so we trigger
// the fallback behaviour. If we used the snake_case version then we'd break
Expand Down
17 changes: 14 additions & 3 deletions src/components/structures/RoomView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -514,7 +514,12 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
};

private getMainSplitContentType = (room: Room) => {
if (SettingsStore.getValue("feature_video_rooms") && room.isElementVideoRoom()) {
if (
SettingsStore.getValue("feature_video_rooms") && (
room.isElementVideoRoom()
|| (SettingsStore.getValue("feature_element_call_video_rooms") && room.isCallRoom())
)
) {
return MainSplitContentType.Video;
}
if (WidgetLayoutStore.instance.hasMaximisedWidget(room)) {
Expand Down Expand Up @@ -2014,8 +2019,14 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {

const myMembership = this.state.room.getMyMembership();
if (
this.state.room.isElementVideoRoom() &&
!(SettingsStore.getValue("feature_video_rooms") && myMembership === "join")
(
this.state.room.isElementVideoRoom()
|| (
SettingsStore.getValue("feature_element_call_video_rooms")
&& this.state.room.isCallRoom()
)
)
&& !(SettingsStore.getValue("feature_video_rooms") && myMembership === "join")
) {
return <ErrorBoundary>
<div className="mx_MainSplit">
Expand Down
10 changes: 8 additions & 2 deletions src/components/structures/SpaceRoomView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -108,8 +108,9 @@ const SpaceLandingAddButton = ({ space }) => {
const canCreateRoom = shouldShowComponent(UIComponent.CreateRooms);
const canCreateSpace = shouldShowComponent(UIComponent.CreateSpaces);
const videoRoomsEnabled = useFeatureEnabled("feature_video_rooms");
const elementCallVideoRoomsEnabled = useFeatureEnabled("feature_element_call_video_rooms");

let contextMenu;
let contextMenu: JSX.Element | null = null;
if (menuDisplayed) {
const rect = handle.current.getBoundingClientRect();
contextMenu = <IconizedContextMenu
Expand Down Expand Up @@ -145,7 +146,12 @@ const SpaceLandingAddButton = ({ space }) => {
e.stopPropagation();
closeMenu();

if (await showCreateNewRoom(space, RoomType.ElementVideo)) {
if (
await showCreateNewRoom(
space,
elementCallVideoRoomsEnabled ? RoomType.UnstableCall : RoomType.ElementVideo,
)
) {
defaultDispatcher.fire(Action.UpdateSpaceHierarchy);
}
}}
Expand Down
8 changes: 6 additions & 2 deletions src/components/views/context_menus/RoomContextMenu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -105,10 +105,14 @@ const RoomContextMenu = ({ room, onFinished, ...props }: IProps) => {
}

const isDm = DMRoomMap.shared().getUserIdForRoomId(room.roomId);
const isVideoRoom = useFeatureEnabled("feature_video_rooms") && room.isElementVideoRoom();
const videoRoomsEnabled = useFeatureEnabled("feature_video_rooms");
const elementCallVideoRoomsEnabled = useFeatureEnabled("feature_element_call_video_rooms");
const isVideoRoom = videoRoomsEnabled && (
room.isElementVideoRoom() || (elementCallVideoRoomsEnabled && room.isCallRoom())
);

let inviteOption: JSX.Element;
if (room.canInvite(cli.getUserId()) && !isDm) {
if (room.canInvite(cli.getUserId()!) && !isDm) {
const onInviteClick = (ev: ButtonEvent) => {
ev.preventDefault();
ev.stopPropagation();
Expand Down
21 changes: 12 additions & 9 deletions src/components/views/context_menus/SpaceContextMenu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ import { ButtonEvent } from "../elements/AccessibleButton";
import defaultDispatcher from "../../../dispatcher/dispatcher";
import { BetaPill } from "../beta/BetaCard";
import SettingsStore from "../../../settings/SettingsStore";
import { useFeatureEnabled } from "../../../hooks/useSettings";
import { Action } from "../../../dispatcher/actions";
import { shouldShowComponent } from "../../../customisations/helpers/UIComponents";
import { UIComponent } from "../../../settings/UIFeature";
Expand All @@ -48,9 +49,9 @@ interface IProps extends IContextMenuProps {

const SpaceContextMenu = ({ space, hideHeader, onFinished, ...props }: IProps) => {
const cli = useContext(MatrixClientContext);
const userId = cli.getUserId();
const userId = cli.getUserId()!;

let inviteOption;
let inviteOption: JSX.Element | null = null;
if (space.getJoinRule() === "public" || space.canInvite(userId)) {
const onInviteClick = (ev: ButtonEvent) => {
ev.preventDefault();
Expand All @@ -71,8 +72,8 @@ const SpaceContextMenu = ({ space, hideHeader, onFinished, ...props }: IProps) =
);
}

let settingsOption;
let leaveOption;
let settingsOption: JSX.Element | null = null;
let leaveOption: JSX.Element | null = null;
if (shouldShowSpaceSettings(space)) {
const onSettingsClick = (ev: ButtonEvent) => {
ev.preventDefault();
Expand Down Expand Up @@ -110,7 +111,7 @@ const SpaceContextMenu = ({ space, hideHeader, onFinished, ...props }: IProps) =
);
}

let devtoolsOption;
let devtoolsOption: JSX.Element | null = null;
if (SettingsStore.getValue("developerMode")) {
const onViewTimelineClick = (ev: ButtonEvent) => {
ev.preventDefault();
Expand All @@ -134,12 +135,15 @@ const SpaceContextMenu = ({ space, hideHeader, onFinished, ...props }: IProps) =
);
}

const videoRoomsEnabled = useFeatureEnabled("feature_video_rooms");
const elementCallVideoRoomsEnabled = useFeatureEnabled("feature_element_call_video_rooms");

const hasPermissionToAddSpaceChild = space.currentState.maySendStateEvent(EventType.SpaceChild, userId);
const canAddRooms = hasPermissionToAddSpaceChild && shouldShowComponent(UIComponent.CreateRooms);
const canAddVideoRooms = canAddRooms && SettingsStore.getValue("feature_video_rooms");
const canAddVideoRooms = canAddRooms && videoRoomsEnabled;
const canAddSubSpaces = hasPermissionToAddSpaceChild && shouldShowComponent(UIComponent.CreateSpaces);

let newRoomSection: JSX.Element;
let newRoomSection: JSX.Element | null = null;
if (canAddRooms || canAddSubSpaces) {
const onNewRoomClick = (ev: ButtonEvent) => {
ev.preventDefault();
Expand All @@ -154,7 +158,7 @@ const SpaceContextMenu = ({ space, hideHeader, onFinished, ...props }: IProps) =
ev.preventDefault();
ev.stopPropagation();

showCreateNewRoom(space, RoomType.ElementVideo);
showCreateNewRoom(space, elementCallVideoRoomsEnabled ? RoomType.UnstableCall : RoomType.ElementVideo);
onFinished();
};

Expand Down Expand Up @@ -266,4 +270,3 @@ const SpaceContextMenu = ({ space, hideHeader, onFinished, ...props }: IProps) =
};

export default SpaceContextMenu;

9 changes: 4 additions & 5 deletions src/components/views/context_menus/WidgetContextMenu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -146,18 +146,17 @@ const WidgetContextMenu: React.FC<IProps> = ({
/>;
}

let isAllowedWidget = SettingsStore.getValue("allowedWidgets", roomId)[app.eventId];
if (isAllowedWidget === undefined) {
isAllowedWidget = app.creatorUserId === cli.getUserId();
}
const isAllowedWidget =
(app.eventId !== undefined && (SettingsStore.getValue("allowedWidgets", roomId)[app.eventId] ?? false))
|| app.creatorUserId === cli.getUserId();

const isLocalWidget = WidgetType.JITSI.matches(app.type);
let revokeButton;
if (!userWidget && !isLocalWidget && isAllowedWidget) {
const onRevokeClick = () => {
logger.info("Revoking permission for widget to load: " + app.eventId);
const current = SettingsStore.getValue("allowedWidgets", roomId);
current[app.eventId] = false;
if (app.eventId !== undefined) current[app.eventId] = false;
const level = SettingsStore.firstSupportedLevel("allowedWidgets");
SettingsStore.setValue("allowedWidgets", roomId, level, current).catch(err => {
logger.error(err);
Expand Down
2 changes: 1 addition & 1 deletion src/components/views/dialogs/ModalWidgetDialog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ export default class ModalWidgetDialog extends React.PureComponent<IProps, IStat
}

public componentDidMount() {
const driver = new StopGapWidgetDriver([], this.widget, WidgetKind.Modal);
const driver = new StopGapWidgetDriver([], this.widget, WidgetKind.Modal, false);
const messaging = new ClientWidgetApi(this.widget, this.appFrame.current, driver);
this.setState({ messaging });
}
Expand Down
8 changes: 3 additions & 5 deletions src/components/views/elements/AppTile.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -165,10 +165,8 @@ export default class AppTile extends React.Component<IProps, IState> {
if (!props.room) return true; // user widgets always have permissions

const currentlyAllowedWidgets = SettingsStore.getValue("allowedWidgets", props.room.roomId);
if (currentlyAllowedWidgets[props.app.eventId] === undefined) {
return props.userId === props.creatorUserId;
}
return !!currentlyAllowedWidgets[props.app.eventId];
const allowed = props.app.eventId !== undefined && (currentlyAllowedWidgets[props.app.eventId] ?? false);
return allowed || props.userId === props.creatorUserId;
};

private onUserLeftRoom() {
Expand Down Expand Up @@ -442,7 +440,7 @@ export default class AppTile extends React.Component<IProps, IState> {
const roomId = this.props.room?.roomId;
logger.info("Granting permission for widget to load: " + this.props.app.eventId);
const current = SettingsStore.getValue("allowedWidgets", roomId);
current[this.props.app.eventId] = true;
if (this.props.app.eventId !== undefined) current[this.props.app.eventId] = true;
const level = SettingsStore.firstSupportedLevel("allowedWidgets");
SettingsStore.setValue("allowedWidgets", roomId, level, current).then(() => {
this.setState({ hasPermissionToLoad: true });
Expand Down
6 changes: 5 additions & 1 deletion src/components/views/right_panel/RoomSummaryCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -262,7 +262,11 @@ const RoomSummaryCard: React.FC<IProps> = ({ room, onClose }) => {
const isRoomEncrypted = useIsEncrypted(cli, room);
const roomContext = useContext(RoomContext);
const e2eStatus = roomContext.e2eStatus;
const isVideoRoom = useFeatureEnabled("feature_video_rooms") && room.isElementVideoRoom();
const videoRoomsEnabled = useFeatureEnabled("feature_video_rooms");
const elementCallVideoRoomsEnabled = useFeatureEnabled("feature_element_call_video_rooms");
const isVideoRoom = videoRoomsEnabled && (
room.isElementVideoRoom() || (elementCallVideoRoomsEnabled && room.isCallRoom())
);

const alias = room.getCanonicalAlias() || room.getAltAliases()[0] || "";
const header = <React.Fragment>
Expand Down
6 changes: 5 additions & 1 deletion src/components/views/rooms/RoomHeader.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -312,7 +312,11 @@ 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") && (
this.props.room.isElementVideoRoom() || (
SettingsStore.getValue("feature_element_call_video_rooms") && this.props.room.isCallRoom()
)
);
robintown marked this conversation as resolved.
Show resolved Hide resolved
const viewLabs = () => defaultDispatcher.dispatch({
action: Action.ViewUserSettings,
initialTabId: UserTab.Labs,
Expand Down
6 changes: 5 additions & 1 deletion src/components/views/rooms/RoomInfoLine.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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";

Expand All @@ -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) {
Expand Down
18 changes: 12 additions & 6 deletions src/components/views/rooms/RoomList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand Down Expand Up @@ -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());
Expand Down Expand Up @@ -239,15 +242,18 @@ 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"
onClick={(e) => {
e.preventDefault();
e.stopPropagation();
closeMenu();
showCreateNewRoom(activeSpace, RoomType.ElementVideo);
showCreateNewRoom(
activeSpace,
elementCallVideoRoomsEnabled ? RoomType.UnstableCall : RoomType.ElementVideo,
);
}}
disabled={!canAddRooms}
tooltip={canAddRooms ? undefined
Expand Down Expand Up @@ -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"
Expand All @@ -297,7 +303,7 @@ const UntaggedAuxButton = ({ tabIndex }: IAuxButtonProps) => {
closeMenu();
defaultDispatcher.dispatch({
action: "view_create_room",
type: RoomType.ElementVideo,
type: elementCallVideoRoomsEnabled ? RoomType.UnstableCall : RoomType.ElementVideo,
});
}}
>
Expand All @@ -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 }
Expand Down
8 changes: 6 additions & 2 deletions src/components/views/rooms/RoomListHeader.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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();
}}
>
Expand Down Expand Up @@ -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();
}}
Expand Down