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:
parent
4f391645e7
commit
bd793a0970
16 changed files with 74 additions and 193 deletions
|
@ -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") {
|
||||||
|
|
|
@ -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">
|
||||||
|
|
|
@ -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)) {
|
||||||
|
|
|
@ -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;
|
||||||
|
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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,53 +289,13 @@ 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) {
|
return makeVideoCallButton("element");
|
||||||
return makeVideoCallButton("element");
|
|
||||||
} else {
|
|
||||||
return makeVideoCallButton(new DisabledWithReason(_t("voip|disabled_no_perms_start_video_call")));
|
|
||||||
}
|
|
||||||
} 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) {
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
{makeVoiceCallButton("legacy_or_jitsi")}
|
|
||||||
{makeVideoCallButton("legacy_or_element")}
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
} else if (mayEditWidgets) {
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
{makeVoiceCallButton("legacy_or_jitsi")}
|
|
||||||
{makeVideoCallButton(mayCreateElementCalls ? "jitsi_or_element" : "legacy_or_jitsi")}
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
} else {
|
} else {
|
||||||
const videoCallBehavior = mayCreateElementCalls
|
return makeVideoCallButton(new DisabledWithReason(_t("voip|disabled_no_perms_start_video_call")));
|
||||||
? "element"
|
|
||||||
: new DisabledWithReason(_t("voip|disabled_no_perms_start_video_call"));
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
{makeVoiceCallButton(new DisabledWithReason(_t("voip|disabled_no_perms_start_voice_call")))}
|
|
||||||
{makeVideoCallButton(videoCallBehavior)}
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
} else if (hasLegacyCall || hasJitsiWidget) {
|
} else if (hasLegacyCall || hasJitsiWidget) {
|
||||||
return (
|
return (
|
||||||
|
@ -352,18 +311,31 @@ const CallButtons: FC<CallButtonsProps> = ({ room }) => {
|
||||||
{makeVideoCallButton(new DisabledWithReason(_t("voip|disabled_no_one_here")))}
|
{makeVideoCallButton(new DisabledWithReason(_t("voip|disabled_no_one_here")))}
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
} else if (functionalMembers.length === 2 || mayEditWidgets) {
|
} else if (functionalMembers.length === 2) {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{makeVoiceCallButton("legacy_or_jitsi")}
|
{makeVoiceCallButton("legacy_or_jitsi")}
|
||||||
{makeVideoCallButton("legacy_or_jitsi")}
|
{makeVideoCallButton(groupCallsEnabled ? "legacy_or_element" : "legacy_or_jitsi")}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
} else if (mayEditWidgets) {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{makeVoiceCallButton("legacy_or_jitsi")}
|
||||||
|
{makeVideoCallButton(
|
||||||
|
groupCallsEnabled && mayCreateElementCalls ? "jitsi_or_element" : "legacy_or_jitsi",
|
||||||
|
)}
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
|
const videoCallBehavior =
|
||||||
|
groupCallsEnabled && mayCreateElementCalls
|
||||||
|
? "element"
|
||||||
|
: new DisabledWithReason(_t("voip|disabled_no_perms_start_video_call"));
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{makeVoiceCallButton(new DisabledWithReason(_t("voip|disabled_no_perms_start_voice_call")))}
|
{makeVoiceCallButton(new DisabledWithReason(_t("voip|disabled_no_perms_start_voice_call")))}
|
||||||
{makeVideoCallButton(new DisabledWithReason(_t("voip|disabled_no_perms_start_video_call")))}
|
{makeVideoCallButton(videoCallBehavior)}
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -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) {
|
||||||
|
|
|
@ -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 ||
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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>
|
||||||
);
|
);
|
||||||
|
|
|
@ -133,10 +133,10 @@ export const useRoomCall = (
|
||||||
if (useElementCallExclusively && !hasJitsiWidget) {
|
if (useElementCallExclusively && !hasJitsiWidget) {
|
||||||
return [PlatformCallType.ElementCall];
|
return [PlatformCallType.ElementCall];
|
||||||
}
|
}
|
||||||
if (hasGroupCall && WidgetType.CALL.matches(groupCall.widget.type)) {
|
}
|
||||||
// only allow joining the ongoing Element call if there is one.
|
if (hasGroupCall && WidgetType.CALL.matches(groupCall.widget.type)) {
|
||||||
return [PlatformCallType.ElementCall];
|
// only allow joining the ongoing Element call if there is one.
|
||||||
}
|
return [PlatformCallType.ElementCall];
|
||||||
}
|
}
|
||||||
return options;
|
return options;
|
||||||
}, [
|
}, [
|
||||||
|
|
|
@ -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"
|
||||||
},
|
},
|
||||||
|
|
|
@ -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,33 +805,24 @@ 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.
|
const apps = WidgetStore.instance.getApps(room.roomId);
|
||||||
|
const hasEcWidget = apps.some((app) => WidgetType.CALL.matches(app.type));
|
||||||
|
const session = room.client.matrixRTC.getRoomSession(room);
|
||||||
|
|
||||||
if (
|
// A call is present if we
|
||||||
SettingsStore.getValue("feature_group_calls") ||
|
// - have a widget: This means the create function was called.
|
||||||
(SettingsStore.getValue("feature_video_rooms") &&
|
// - or there is a running session where we have not yet created a widget for.
|
||||||
SettingsStore.getValue("feature_element_call_video_rooms") &&
|
// - or this is a call room. Then we also always want to show a call.
|
||||||
room.isCallRoom())
|
if (hasEcWidget || session.memberships.length !== 0 || room.isCallRoom()) {
|
||||||
) {
|
// create a widget for the case we are joining a running call and don't have on yet.
|
||||||
const apps = WidgetStore.instance.getApps(room.roomId);
|
const availableOrCreatedWidget = ElementCall.createOrGetCallWidget(
|
||||||
const hasEcWidget = apps.some((app) => WidgetType.CALL.matches(app.type));
|
room.roomId,
|
||||||
const session = room.client.matrixRTC.getRoomSession(room);
|
room.client,
|
||||||
|
undefined,
|
||||||
// A call is present if we
|
undefined,
|
||||||
// - have a widget: This means the create function was called.
|
isVideoRoom(room),
|
||||||
// - or there is a running session where we have not yet created a widget for.
|
);
|
||||||
// - or this is a call room. Then we also always want to show a call.
|
return new ElementCall(session, availableOrCreatedWidget, room.client);
|
||||||
if (hasEcWidget || session.memberships.length !== 0 || room.isCallRoom()) {
|
|
||||||
// create a widget for the case we are joining a running call and don't have on yet.
|
|
||||||
const availableOrCreatedWidget = ElementCall.createOrGetCallWidget(
|
|
||||||
room.roomId,
|
|
||||||
room.client,
|
|
||||||
undefined,
|
|
||||||
undefined,
|
|
||||||
isVideoRoom(room),
|
|
||||||
);
|
|
||||||
return new ElementCall(session, availableOrCreatedWidget, room.client);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
|
|
|
@ -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);
|
|
||||||
};
|
|
||||||
|
|
|
@ -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();
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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);
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue