Allow joining calls and video rooms without enabling the labs flags (#95)

Since Element Call has now reached production on Element X, Element Web needs to be able to at least participate in group calls. Starting a group call or creating a video room will still require the labs flags, for now.

Note that Jitsi-based video rooms are also affected by this change. This is not because we intend to delabs them (rather, we intend to get rid of them in favor of Element Call video rooms), but because it's easiest to handle both video room variants consistently.
This commit is contained in:
Robin 2024-09-30 10:07:53 -04:00 committed by GitHub
parent 4f391645e7
commit bd793a0970
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
16 changed files with 74 additions and 193 deletions

View file

@ -513,12 +513,7 @@ class NotifierClass extends TypedEventEmitter<keyof EmittedEvents, EmittedEvents
const thisUserHasConnectedDevice = const thisUserHasConnectedDevice =
room && MatrixRTCSession.callMembershipsForRoom(room).some((m) => m.sender === cli.getUserId()); room && MatrixRTCSession.callMembershipsForRoom(room).some((m) => m.sender === cli.getUserId());
if ( if (EventType.CallNotify === ev.getType() && (ev.getAge() ?? 0) < 10000 && !thisUserHasConnectedDevice) {
EventType.CallNotify === ev.getType() &&
SettingsStore.getValue("feature_group_calls") &&
(ev.getAge() ?? 0) < 10000 &&
!thisUserHasConnectedDevice
) {
const content = ev.getContent(); const content = ev.getContent();
const roomId = ev.getRoomId(); const roomId = ev.getRoomId();
if (typeof content.call_id !== "string") { if (typeof content.call_id !== "string") {

View file

@ -614,10 +614,7 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
}; };
private getMainSplitContentType = (room: Room): MainSplitContentType => { private getMainSplitContentType = (room: Room): MainSplitContentType => {
if ( if (this.context.roomViewStore.isViewingCall() || isVideoRoom(room)) {
(SettingsStore.getValue("feature_group_calls") && this.context.roomViewStore.isViewingCall()) ||
isVideoRoom(room)
) {
return MainSplitContentType.Call; return MainSplitContentType.Call;
} }
if (this.context.widgetLayoutStore.hasMaximisedWidget(room)) { if (this.context.widgetLayoutStore.hasMaximisedWidget(room)) {
@ -2183,10 +2180,7 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
} }
const myMembership = this.state.room.getMyMembership(); const myMembership = this.state.room.getMyMembership();
if ( if (isVideoRoom(this.state.room) && myMembership !== KnownMembership.Join) {
isVideoRoom(this.state.room) &&
!(SettingsStore.getValue("feature_video_rooms") && myMembership === KnownMembership.Join)
) {
return ( return (
<ErrorBoundary> <ErrorBoundary>
<div className="mx_MainSplit"> <div className="mx_MainSplit">

View file

@ -42,7 +42,7 @@ import { shouldShowComponent } from "../../../customisations/helpers/UIComponent
import { UIComponent } from "../../../settings/UIFeature"; import { UIComponent } from "../../../settings/UIFeature";
import { DeveloperToolsOption } from "./DeveloperToolsOption"; import { DeveloperToolsOption } from "./DeveloperToolsOption";
import { tagRoom } from "../../../utils/room/tagRoom"; import { tagRoom } from "../../../utils/room/tagRoom";
import { useIsVideoRoom } from "../../../utils/video-rooms"; import { isVideoRoom as calcIsVideoRoom } from "../../../utils/video-rooms";
import { usePinnedEvents } from "../../../hooks/usePinnedEvents"; import { usePinnedEvents } from "../../../hooks/usePinnedEvents";
interface IProps extends IContextMenuProps { interface IProps extends IContextMenuProps {
@ -105,7 +105,7 @@ const RoomContextMenu: React.FC<IProps> = ({ room, onFinished, ...props }) => {
} }
const isDm = DMRoomMap.shared().getUserIdForRoomId(room.roomId); const isDm = DMRoomMap.shared().getUserIdForRoomId(room.roomId);
const isVideoRoom = useIsVideoRoom(room); const isVideoRoom = calcIsVideoRoom(room);
const canInvite = useEventEmitterState(cli, RoomMemberEvent.PowerLevel, () => room.canInvite(cli.getUserId()!)); const canInvite = useEventEmitterState(cli, RoomMemberEvent.PowerLevel, () => room.canInvite(cli.getUserId()!));
let inviteOption: JSX.Element | undefined; let inviteOption: JSX.Element | undefined;
if (canInvite && !isDm && shouldShowComponent(UIComponent.InviteUsers)) { if (canInvite && !isDm && shouldShowComponent(UIComponent.InviteUsers)) {

View file

@ -20,7 +20,7 @@ import { Action } from "../../../dispatcher/actions";
import SettingsStore from "../../../settings/SettingsStore"; import SettingsStore from "../../../settings/SettingsStore";
import { UIComponent, UIFeature } from "../../../settings/UIFeature"; import { UIComponent, UIFeature } from "../../../settings/UIFeature";
import { shouldShowComponent } from "../../../customisations/helpers/UIComponents"; import { shouldShowComponent } from "../../../customisations/helpers/UIComponents";
import { useIsVideoRoom } from "../../../utils/video-rooms"; import { isVideoRoom as calcIsVideoRoom } from "../../../utils/video-rooms";
function shouldShowTabsForPhase(phase?: RightPanelPhases): boolean { function shouldShowTabsForPhase(phase?: RightPanelPhases): boolean {
const tabs = [ const tabs = [
@ -48,7 +48,7 @@ export const RightPanelTabs: React.FC<Props> = ({ phase, room }): JSX.Element |
} }
}); });
const isVideoRoom = useIsVideoRoom(room); const isVideoRoom = room !== undefined && calcIsVideoRoom(room);
if (!shouldShowTabsForPhase(phase)) return null; if (!shouldShowTabsForPhase(phase)) return null;

View file

@ -70,7 +70,7 @@ import { useDispatcher } from "../../../hooks/useDispatcher";
import { Action } from "../../../dispatcher/actions"; import { Action } from "../../../dispatcher/actions";
import { Key } from "../../../Keyboard"; import { Key } from "../../../Keyboard";
import { useTransition } from "../../../hooks/useTransition"; import { useTransition } from "../../../hooks/useTransition";
import { useIsVideoRoom } from "../../../utils/video-rooms"; import { isVideoRoom as calcIsVideoRoom } from "../../../utils/video-rooms";
import { usePinnedEvents } from "../../../hooks/usePinnedEvents"; import { usePinnedEvents } from "../../../hooks/usePinnedEvents";
import { ReleaseAnnouncement } from "../../structures/ReleaseAnnouncement.tsx"; import { ReleaseAnnouncement } from "../../structures/ReleaseAnnouncement.tsx";
@ -219,7 +219,7 @@ const RoomSummaryCard: React.FC<IProps> = ({
const isRoomEncrypted = useIsEncrypted(cli, room); const isRoomEncrypted = useIsEncrypted(cli, room);
const roomContext = useContext(RoomContext); const roomContext = useContext(RoomContext);
const e2eStatus = roomContext.e2eStatus; const e2eStatus = roomContext.e2eStatus;
const isVideoRoom = useIsVideoRoom(room); const isVideoRoom = calcIsVideoRoom(room);
const roomState = useRoomState(room); const roomState = useRoomState(room);
const directRoomsList = useAccountData<Record<string, string[]>>(room.client, EventType.Direct); const directRoomsList = useAccountData<Record<string, string[]>>(room.client, EventType.Direct);

View file

@ -251,8 +251,7 @@ const CallButtons: FC<CallButtonsProps> = ({ room }) => {
const [busy, setBusy] = useState(false); const [busy, setBusy] = useState(false);
const showButtons = useSettingValue<boolean>("showCallButtonsInComposer"); const showButtons = useSettingValue<boolean>("showCallButtonsInComposer");
const groupCallsEnabled = useFeatureEnabled("feature_group_calls"); const groupCallsEnabled = useFeatureEnabled("feature_group_calls");
const videoRoomsEnabled = useFeatureEnabled("feature_video_rooms"); const isVideoRoom = useMemo(() => calcIsVideoRoom(room), [room]);
const isVideoRoom = useMemo(() => videoRoomsEnabled && calcIsVideoRoom(room), [videoRoomsEnabled, room]);
const useElementCallExclusively = useMemo(() => { const useElementCallExclusively = useMemo(() => {
return SdkConfig.get("element_call").use_exclusively; return SdkConfig.get("element_call").use_exclusively;
}, []); }, []);
@ -290,8 +289,7 @@ const CallButtons: FC<CallButtonsProps> = ({ room }) => {
if (isVideoRoom || !showButtons) { if (isVideoRoom || !showButtons) {
return null; return null;
} else if (groupCallsEnabled) { } else if (groupCallsEnabled && useElementCallExclusively) {
if (useElementCallExclusively) {
if (hasGroupCall) { if (hasGroupCall) {
return makeVideoCallButton(new DisabledWithReason(_t("voip|disabled_ongoing_call"))); return makeVideoCallButton(new DisabledWithReason(_t("voip|disabled_ongoing_call")));
} else if (mayCreateElementCalls) { } else if (mayCreateElementCalls) {
@ -317,18 +315,21 @@ const CallButtons: FC<CallButtonsProps> = ({ room }) => {
return ( return (
<> <>
{makeVoiceCallButton("legacy_or_jitsi")} {makeVoiceCallButton("legacy_or_jitsi")}
{makeVideoCallButton("legacy_or_element")} {makeVideoCallButton(groupCallsEnabled ? "legacy_or_element" : "legacy_or_jitsi")}
</> </>
); );
} else if (mayEditWidgets) { } else if (mayEditWidgets) {
return ( return (
<> <>
{makeVoiceCallButton("legacy_or_jitsi")} {makeVoiceCallButton("legacy_or_jitsi")}
{makeVideoCallButton(mayCreateElementCalls ? "jitsi_or_element" : "legacy_or_jitsi")} {makeVideoCallButton(
groupCallsEnabled && mayCreateElementCalls ? "jitsi_or_element" : "legacy_or_jitsi",
)}
</> </>
); );
} else { } else {
const videoCallBehavior = mayCreateElementCalls const videoCallBehavior =
groupCallsEnabled && mayCreateElementCalls
? "element" ? "element"
: new DisabledWithReason(_t("voip|disabled_no_perms_start_video_call")); : new DisabledWithReason(_t("voip|disabled_no_perms_start_video_call"));
return ( return (
@ -338,35 +339,6 @@ const CallButtons: FC<CallButtonsProps> = ({ room }) => {
</> </>
); );
} }
} else if (hasLegacyCall || hasJitsiWidget) {
return (
<>
{makeVoiceCallButton(new DisabledWithReason(_t("voip|disabled_ongoing_call")))}
{makeVideoCallButton(new DisabledWithReason(_t("voip|disabled_ongoing_call")))}
</>
);
} else if (functionalMembers.length <= 1) {
return (
<>
{makeVoiceCallButton(new DisabledWithReason(_t("voip|disabled_no_one_here")))}
{makeVideoCallButton(new DisabledWithReason(_t("voip|disabled_no_one_here")))}
</>
);
} else if (functionalMembers.length === 2 || mayEditWidgets) {
return (
<>
{makeVoiceCallButton("legacy_or_jitsi")}
{makeVideoCallButton("legacy_or_jitsi")}
</>
);
} else {
return (
<>
{makeVoiceCallButton(new DisabledWithReason(_t("voip|disabled_no_perms_start_voice_call")))}
{makeVideoCallButton(new DisabledWithReason(_t("voip|disabled_no_perms_start_video_call")))}
</>
);
}
}; };
interface CallLayoutSelectorProps { interface CallLayoutSelectorProps {
@ -745,7 +717,7 @@ export default class RoomHeader extends React.Component<IProps, IState> {
} }
public render(): React.ReactNode { public render(): React.ReactNode {
const isVideoRoom = SettingsStore.getValue("feature_video_rooms") && calcIsVideoRoom(this.props.room); const isVideoRoom = calcIsVideoRoom(this.props.room);
let roomAvatar: JSX.Element | null = null; let roomAvatar: JSX.Element | null = null;
if (this.props.room) { if (this.props.room) {

View file

@ -42,7 +42,7 @@ import RightPanelStore from "../../../stores/right-panel/RightPanelStore";
import PosthogTrackers from "../../../PosthogTrackers"; import PosthogTrackers from "../../../PosthogTrackers";
import { VideoRoomChatButton } from "./RoomHeader/VideoRoomChatButton"; import { VideoRoomChatButton } from "./RoomHeader/VideoRoomChatButton";
import { RoomKnocksBar } from "./RoomKnocksBar"; import { RoomKnocksBar } from "./RoomKnocksBar";
import { useIsVideoRoom } from "../../../utils/video-rooms"; import { isVideoRoom as calcIsVideoRoom } from "../../../utils/video-rooms";
import { notificationLevelToIndicator } from "../../../utils/notifications"; import { notificationLevelToIndicator } from "../../../utils/notifications";
import { CallGuestLinkButton } from "./RoomHeader/CallGuestLinkButton"; import { CallGuestLinkButton } from "./RoomHeader/CallGuestLinkButton";
import { ButtonEvent } from "../elements/AccessibleButton"; import { ButtonEvent } from "../elements/AccessibleButton";
@ -225,7 +225,7 @@ export default function RoomHeader({
} }
const roomContext = useContext(RoomContext); const roomContext = useContext(RoomContext);
const isVideoRoom = useIsVideoRoom(room); const isVideoRoom = calcIsVideoRoom(room);
const showChatButton = const showChatButton =
isVideoRoom || isVideoRoom ||
roomContext.mainSplitContentType === MainSplitContentType.MaximisedWidget || roomContext.mainSplitContentType === MainSplitContentType.MaximisedWidget ||

View file

@ -17,7 +17,7 @@ import { useAsyncMemo } from "../../../hooks/useAsyncMemo";
import { useRoomState } from "../../../hooks/useRoomState"; import { useRoomState } from "../../../hooks/useRoomState";
import { useRoomMemberCount, useMyRoomMembership } from "../../../hooks/useRoomMembers"; import { useRoomMemberCount, useMyRoomMembership } from "../../../hooks/useRoomMembers";
import AccessibleButton from "../elements/AccessibleButton"; import AccessibleButton from "../elements/AccessibleButton";
import { useIsVideoRoom } from "../../../utils/video-rooms"; import { isVideoRoom as calcIsVideoRoom } from "../../../utils/video-rooms";
interface IProps { interface IProps {
room: Room; room: Room;
@ -37,7 +37,7 @@ const RoomInfoLine: FC<IProps> = ({ room }) => {
const membership = useMyRoomMembership(room); const membership = useMyRoomMembership(room);
const memberCount = useRoomMemberCount(room); const memberCount = useRoomMemberCount(room);
const isVideoRoom = useIsVideoRoom(room, true); const isVideoRoom = calcIsVideoRoom(room);
let iconClass: string; let iconClass: string;
let roomType: string; let roomType: string;

View file

@ -17,7 +17,6 @@ import { UserTab } from "../dialogs/UserTab";
import { EffectiveMembership, getEffectiveMembership } from "../../../utils/membership"; import { EffectiveMembership, getEffectiveMembership } from "../../../utils/membership";
import MatrixClientContext from "../../../contexts/MatrixClientContext"; import MatrixClientContext from "../../../contexts/MatrixClientContext";
import { useDispatcher } from "../../../hooks/useDispatcher"; import { useDispatcher } from "../../../hooks/useDispatcher";
import { useFeatureEnabled } from "../../../hooks/useSettings";
import { useRoomState } from "../../../hooks/useRoomState"; import { useRoomState } from "../../../hooks/useRoomState";
import { useMyRoomMembership } from "../../../hooks/useRoomMembers"; import { useMyRoomMembership } from "../../../hooks/useRoomMembers";
import AccessibleButton from "../elements/AccessibleButton"; import AccessibleButton from "../elements/AccessibleButton";
@ -29,7 +28,7 @@ import RoomAvatar from "../avatars/RoomAvatar";
import MemberAvatar from "../avatars/MemberAvatar"; import MemberAvatar from "../avatars/MemberAvatar";
import { BetaPill } from "../beta/BetaCard"; import { BetaPill } from "../beta/BetaCard";
import RoomInfoLine from "./RoomInfoLine"; import RoomInfoLine from "./RoomInfoLine";
import { useIsVideoRoom } from "../../../utils/video-rooms"; import { isVideoRoom as calcIsVideoRoom } from "../../../utils/video-rooms";
interface IProps { interface IProps {
room: Room; room: Room;
@ -43,8 +42,7 @@ interface IProps {
// and viewing invite reasons to achieve parity with the default invite screen. // and viewing invite reasons to achieve parity with the default invite screen.
const RoomPreviewCard: FC<IProps> = ({ room, onJoinButtonClicked, onRejectButtonClicked }) => { const RoomPreviewCard: FC<IProps> = ({ room, onJoinButtonClicked, onRejectButtonClicked }) => {
const cli = useContext(MatrixClientContext); const cli = useContext(MatrixClientContext);
const videoRoomsEnabled = useFeatureEnabled("feature_video_rooms"); const isVideoRoom = calcIsVideoRoom(room);
const isVideoRoom = useIsVideoRoom(room, true);
const myMembership = useMyRoomMembership(room); const myMembership = useMyRoomMembership(room);
useDispatcher(defaultDispatcher, (payload) => { useDispatcher(defaultDispatcher, (payload) => {
if (payload.action === Action.JoinRoomError && payload.roomId === room.roomId) { if (payload.action === Action.JoinRoomError && payload.roomId === room.roomId) {
@ -164,24 +162,6 @@ const RoomPreviewCard: FC<IProps> = ({ room, onJoinButtonClicked, onRejectButton
avatarRow = <RoomAvatar room={room} size="50px" viewAvatarOnClick />; avatarRow = <RoomAvatar room={room} size="50px" viewAvatarOnClick />;
} }
let notice: string | null = null;
if (cannotJoin) {
notice = _t("room|join_failed_needs_invite", {
roomName: room.name,
});
} else if (isVideoRoom && !videoRoomsEnabled) {
notice =
myMembership === KnownMembership.Join
? _t("room|view_failed_enable_video_rooms")
: _t("room|join_failed_enable_video_rooms");
joinButtons = (
<AccessibleButton kind="primary" onClick={viewLabs}>
{_t("room|show_labs_settings")}
</AccessibleButton>
);
}
return ( return (
<div className="mx_RoomPreviewCard"> <div className="mx_RoomPreviewCard">
{inviterSection} {inviterSection}
@ -192,7 +172,11 @@ const RoomPreviewCard: FC<IProps> = ({ room, onJoinButtonClicked, onRejectButton
<RoomInfoLine room={room} /> <RoomInfoLine room={room} />
<RoomTopic room={room} className="mx_RoomPreviewCard_topic" /> <RoomTopic room={room} className="mx_RoomPreviewCard_topic" />
{room.getJoinRule() === "public" && <RoomFacePile room={room} />} {room.getJoinRule() === "public" && <RoomFacePile room={room} />}
{notice ? <div className="mx_RoomPreviewCard_notice">{notice}</div> : null} {cannotJoin ? (
<div className="mx_RoomPreviewCard_notice">
{_t("room|join_failed_needs_invite", { roomName: room.name })}
</div>
) : null}
<div className="mx_RoomPreviewCard_joinButtons">{joinButtons}</div> <div className="mx_RoomPreviewCard_joinButtons">{joinButtons}</div>
</div> </div>
); );

View file

@ -133,11 +133,11 @@ export const useRoomCall = (
if (useElementCallExclusively && !hasJitsiWidget) { if (useElementCallExclusively && !hasJitsiWidget) {
return [PlatformCallType.ElementCall]; return [PlatformCallType.ElementCall];
} }
}
if (hasGroupCall && WidgetType.CALL.matches(groupCall.widget.type)) { if (hasGroupCall && WidgetType.CALL.matches(groupCall.widget.type)) {
// only allow joining the ongoing Element call if there is one. // only allow joining the ongoing Element call if there is one.
return [PlatformCallType.ElementCall]; return [PlatformCallType.ElementCall];
} }
}
return options; return options;
}, [ }, [
memberCount, memberCount,

View file

@ -2019,7 +2019,6 @@
"inviter_unknown": "Unknown", "inviter_unknown": "Unknown",
"invites_you_text": "<inviter/> invites you", "invites_you_text": "<inviter/> invites you",
"join_button_account": "Sign Up", "join_button_account": "Sign Up",
"join_failed_enable_video_rooms": "To join, please enable video rooms in Labs first",
"join_failed_needs_invite": "To view %(roomName)s, you need an invite", "join_failed_needs_invite": "To view %(roomName)s, you need an invite",
"join_the_discussion": "Join the discussion", "join_the_discussion": "Join the discussion",
"join_title": "Join the room to participate", "join_title": "Join the room to participate",
@ -2088,7 +2087,6 @@
}, },
"this_room_button": "Search this room" "this_room_button": "Search this room"
}, },
"show_labs_settings": "Show Labs settings",
"status_bar": { "status_bar": {
"delete_all": "Delete all", "delete_all": "Delete all",
"exceeded_resource_limit": "Your message wasn't sent because this homeserver has exceeded a resource limit. Please <a>contact your service administrator</a> to continue using the service.", "exceeded_resource_limit": "Your message wasn't sent because this homeserver has exceeded a resource limit. Please <a>contact your service administrator</a> to continue using the service.",
@ -2119,7 +2117,6 @@
}, },
"uploading_single_file": "Uploading %(filename)s" "uploading_single_file": "Uploading %(filename)s"
}, },
"view_failed_enable_video_rooms": "To view, please enable video rooms in Labs first",
"waiting_for_join_subtitle": "Once invited users have joined %(brand)s, you will be able to chat and the room will be end-to-end encrypted", "waiting_for_join_subtitle": "Once invited users have joined %(brand)s, you will be able to chat and the room will be end-to-end encrypted",
"waiting_for_join_title": "Waiting for users to join %(brand)s" "waiting_for_join_title": "Waiting for users to join %(brand)s"
}, },

View file

@ -338,7 +338,7 @@ export class JitsiCall extends Call {
public static get(room: Room): JitsiCall | null { public static get(room: Room): JitsiCall | null {
// Only supported in video rooms // Only supported in video rooms
if (SettingsStore.getValue("feature_video_rooms") && room.isElementVideoRoom()) { if (room.isElementVideoRoom()) {
const apps = WidgetStore.instance.getApps(room.roomId); const apps = WidgetStore.instance.getApps(room.roomId);
// The isVideoChannel field differentiates rich Jitsi calls from bare Jitsi widgets // The isVideoChannel field differentiates rich Jitsi calls from bare Jitsi widgets
const jitsiWidget = apps.find((app) => WidgetType.JITSI.matches(app.type) && app.data?.isVideoChannel); const jitsiWidget = apps.find((app) => WidgetType.JITSI.matches(app.type) && app.data?.isVideoChannel);
@ -805,14 +805,6 @@ export class ElementCall extends Call {
} }
public static get(room: Room): ElementCall | null { public static get(room: Room): ElementCall | null {
// Only supported in the new group call experience or in video rooms.
if (
SettingsStore.getValue("feature_group_calls") ||
(SettingsStore.getValue("feature_video_rooms") &&
SettingsStore.getValue("feature_element_call_video_rooms") &&
room.isCallRoom())
) {
const apps = WidgetStore.instance.getApps(room.roomId); const apps = WidgetStore.instance.getApps(room.roomId);
const hasEcWidget = apps.some((app) => WidgetType.CALL.matches(app.type)); const hasEcWidget = apps.some((app) => WidgetType.CALL.matches(app.type));
const session = room.client.matrixRTC.getRoomSession(room); const session = room.client.matrixRTC.getRoomSession(room);
@ -832,7 +824,6 @@ export class ElementCall extends Call {
); );
return new ElementCall(session, availableOrCreatedWidget, room.client); return new ElementCall(session, availableOrCreatedWidget, room.client);
} }
}
return null; return null;
} }

View file

@ -7,27 +7,8 @@ Please see LICENSE files in the repository root for full details.
*/ */
import type { Room } from "matrix-js-sdk/src/matrix"; import type { Room } from "matrix-js-sdk/src/matrix";
import SettingsStore from "../settings/SettingsStore";
import { useFeatureEnabled } from "../hooks/useSettings";
function checkIsVideoRoom(room: Room, elementCallVideoRoomsEnabled: boolean): boolean {
return room.isElementVideoRoom() || (elementCallVideoRoomsEnabled && room.isCallRoom());
}
export const isVideoRoom = (room: Room): boolean =>
checkIsVideoRoom(room, SettingsStore.getValue("feature_element_call_video_rooms"));
/** /**
* Returns whether the given room is a video room based on the current feature flags. * Determines whether the given room is a video room.
* @param room The room to check.
* @param skipVideoRoomsEnabledCheck If true, the check for the video rooms feature flag is skipped,
* useful for suggesting to the user to enable the labs flag.
*/ */
export const useIsVideoRoom = (room?: Room, skipVideoRoomsEnabledCheck = false): boolean => { export const isVideoRoom = (room: Room): boolean => room.isElementVideoRoom() || room.isCallRoom();
const videoRoomsEnabled = useFeatureEnabled("feature_video_rooms");
const elementCallVideoRoomsEnabled = useFeatureEnabled("feature_element_call_video_rooms"); // react to updates as isVideoRoom reads the value itself
if (!room) return false;
if (!videoRoomsEnabled && !skipVideoRoomsEnabledCheck) return false;
return checkIsVideoRoom(room, elementCallVideoRoomsEnabled);
};

View file

@ -423,15 +423,7 @@ describe("Notifier", () => {
return callEvent; return callEvent;
}; };
const setGroupCallsEnabled = (val: boolean) => { it("shows group call toast", () => {
jest.spyOn(SettingsStore, "getValue").mockImplementation((name: string) => {
if (name === "feature_group_calls") return val;
});
};
it("should show toast when group calls are supported", () => {
setGroupCallsEnabled(true);
const notifyEvent = emitCallNotifyEvent(); const notifyEvent = emitCallNotifyEvent();
expect(ToastStore.sharedInstance().addOrReplaceToast).toHaveBeenCalledWith( expect(ToastStore.sharedInstance().addOrReplaceToast).toHaveBeenCalledWith(
@ -445,16 +437,7 @@ describe("Notifier", () => {
); );
}); });
it("should not show toast when group calls are not supported", () => {
setGroupCallsEnabled(false);
emitCallNotifyEvent();
expect(ToastStore.sharedInstance().addOrReplaceToast).not.toHaveBeenCalled();
});
it("should not show toast when group call is already connected", () => { it("should not show toast when group call is already connected", () => {
setGroupCallsEnabled(true);
const spyCallMemberships = jest.spyOn(MatrixRTCSession, "callMembershipsForRoom").mockReturnValue([ const spyCallMemberships = jest.spyOn(MatrixRTCSession, "callMembershipsForRoom").mockReturnValue([
new CallMembership( new CallMembership(
mkEvent({ mkEvent({
@ -483,8 +466,6 @@ describe("Notifier", () => {
}); });
it("should not show toast when calling with non-group call event", () => { it("should not show toast when calling with non-group call event", () => {
setGroupCallsEnabled(true);
emitCallNotifyEvent("event_type"); emitCallNotifyEvent("event_type");
expect(ToastStore.sharedInstance().addOrReplaceToast).not.toHaveBeenCalled(); expect(ToastStore.sharedInstance().addOrReplaceToast).not.toHaveBeenCalled();

View file

@ -1039,10 +1039,13 @@ describe("<MatrixChat />", () => {
}); });
describe("when encryption is force disabled", () => { describe("when encryption is force disabled", () => {
const unencryptedRoom = new Room("!unencrypted:server.org", loginClient, userId); let unencryptedRoom: Room;
const encryptedRoom = new Room("!encrypted:server.org", loginClient, userId); let encryptedRoom: Room;
beforeEach(() => { beforeEach(() => {
unencryptedRoom = new Room("!unencrypted:server.org", loginClient, userId);
encryptedRoom = new Room("!encrypted:server.org", loginClient, userId);
loginClient.getClientWellKnown.mockReturnValue({ loginClient.getClientWellKnown.mockReturnValue({
"io.element.e2ee": { "io.element.e2ee": {
force_disable: true, force_disable: true,

View file

@ -83,21 +83,4 @@ describe("RoomPreviewCard", () => {
await renderPreview(); await renderPreview();
expect(screen.queryByRole("button", { name: /beta/i })).toBeNull(); expect(screen.queryByRole("button", { name: /beta/i })).toBeNull();
}); });
it("shows instructions on Jitsi video rooms invites if video rooms are disabled", async () => {
jest.spyOn(room, "getType").mockReturnValue(RoomType.ElementVideo);
jest.spyOn(room, "getMyMembership").mockReturnValue(KnownMembership.Invite);
await renderPreview();
screen.getByText(/enable video rooms in labs/i);
});
it("shows instructions on Element video rooms invites if video rooms are disabled", async () => {
jest.spyOn(room, "getType").mockReturnValue(RoomType.UnstableCall);
jest.spyOn(room, "getMyMembership").mockReturnValue(KnownMembership.Invite);
enabledFeatures = ["feature_element_call_video_rooms"];
await renderPreview();
screen.getByText(/enable video rooms in labs/i);
});
}); });