From 4f391645e734f67e4e68b632a6b71d401ae22eb2 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Mon, 30 Sep 2024 14:50:17 +0100 Subject: [PATCH 01/12] Ensure timeline search results are visible even in video rooms (#96) Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- src/components/structures/RoomView.tsx | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/components/structures/RoomView.tsx b/src/components/structures/RoomView.tsx index 87e8c3c307..37e45d6b8d 100644 --- a/src/components/structures/RoomView.tsx +++ b/src/components/structures/RoomView.tsx @@ -2521,9 +2521,15 @@ export class RoomView extends React.Component { mx_RoomView_timeline_rr_enabled: this.state.showReadReceipts, }); + let { mainSplitContentType } = this.state; + if (this.state.search) { + // When in the middle of a search force the main split content type to timeline + mainSplitContentType = MainSplitContentType.Timeline; + } + const mainClasses = classNames("mx_RoomView", { mx_RoomView_inCall: Boolean(activeCall), - mx_RoomView_immersive: this.state.mainSplitContentType !== MainSplitContentType.Timeline, + mx_RoomView_immersive: mainSplitContentType !== MainSplitContentType.Timeline, }); const showChatEffects = SettingsStore.getValue("showChatEffects"); @@ -2531,7 +2537,7 @@ export class RoomView extends React.Component { let mainSplitBody: JSX.Element | undefined; let mainSplitContentClassName: string | undefined; // Decide what to show in the main split - switch (this.state.mainSplitContentType) { + switch (mainSplitContentType) { case MainSplitContentType.Timeline: mainSplitContentClassName = "mx_MainSplit_timeline"; mainSplitBody = ( @@ -2595,7 +2601,7 @@ export class RoomView extends React.Component { let viewingCall = false; // Simplify the header for other main split types - switch (this.state.mainSplitContentType) { + switch (mainSplitContentType) { case MainSplitContentType.MaximisedWidget: excludedRightPanelPhaseButtons = []; onAppsClick = null; From bd793a0970744a637f733fd46455aa187879f416 Mon Sep 17 00:00:00 2001 From: Robin Date: Mon, 30 Sep 2024 10:07:53 -0400 Subject: [PATCH 02/12] 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. --- src/Notifier.ts | 7 +- src/components/structures/RoomView.tsx | 10 +-- .../views/context_menus/RoomContextMenu.tsx | 4 +- .../views/right_panel/RightPanelTabs.tsx | 4 +- .../views/right_panel/RoomSummaryCard.tsx | 4 +- .../views/rooms/LegacyRoomHeader.tsx | 76 ++++++------------- src/components/views/rooms/RoomHeader.tsx | 4 +- src/components/views/rooms/RoomInfoLine.tsx | 4 +- .../views/rooms/RoomPreviewCard.tsx | 30 ++------ src/hooks/room/useRoomCall.ts | 8 +- src/i18n/strings/en_EN.json | 3 - src/models/Call.ts | 47 +++++------- src/utils/video-rooms.ts | 23 +----- test/Notifier-test.ts | 21 +---- .../components/structures/MatrixChat-test.tsx | 7 +- .../views/rooms/RoomPreviewCard-test.tsx | 17 ----- 16 files changed, 75 insertions(+), 194 deletions(-) diff --git a/src/Notifier.ts b/src/Notifier.ts index 6323343871..23d239de0a 100644 --- a/src/Notifier.ts +++ b/src/Notifier.ts @@ -513,12 +513,7 @@ class NotifierClass extends TypedEventEmitter m.sender === cli.getUserId()); - if ( - EventType.CallNotify === ev.getType() && - SettingsStore.getValue("feature_group_calls") && - (ev.getAge() ?? 0) < 10000 && - !thisUserHasConnectedDevice - ) { + if (EventType.CallNotify === ev.getType() && (ev.getAge() ?? 0) < 10000 && !thisUserHasConnectedDevice) { const content = ev.getContent(); const roomId = ev.getRoomId(); if (typeof content.call_id !== "string") { diff --git a/src/components/structures/RoomView.tsx b/src/components/structures/RoomView.tsx index 37e45d6b8d..b56172be42 100644 --- a/src/components/structures/RoomView.tsx +++ b/src/components/structures/RoomView.tsx @@ -614,10 +614,7 @@ export class RoomView extends React.Component { }; private getMainSplitContentType = (room: Room): MainSplitContentType => { - if ( - (SettingsStore.getValue("feature_group_calls") && this.context.roomViewStore.isViewingCall()) || - isVideoRoom(room) - ) { + if (this.context.roomViewStore.isViewingCall() || isVideoRoom(room)) { return MainSplitContentType.Call; } if (this.context.widgetLayoutStore.hasMaximisedWidget(room)) { @@ -2183,10 +2180,7 @@ export class RoomView extends React.Component { } const myMembership = this.state.room.getMyMembership(); - if ( - isVideoRoom(this.state.room) && - !(SettingsStore.getValue("feature_video_rooms") && myMembership === KnownMembership.Join) - ) { + if (isVideoRoom(this.state.room) && myMembership !== KnownMembership.Join) { return (
diff --git a/src/components/views/context_menus/RoomContextMenu.tsx b/src/components/views/context_menus/RoomContextMenu.tsx index 694aba0ef5..f431218b06 100644 --- a/src/components/views/context_menus/RoomContextMenu.tsx +++ b/src/components/views/context_menus/RoomContextMenu.tsx @@ -42,7 +42,7 @@ import { shouldShowComponent } from "../../../customisations/helpers/UIComponent import { UIComponent } from "../../../settings/UIFeature"; import { DeveloperToolsOption } from "./DeveloperToolsOption"; 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"; interface IProps extends IContextMenuProps { @@ -105,7 +105,7 @@ const RoomContextMenu: React.FC = ({ room, onFinished, ...props }) => { } 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()!)); let inviteOption: JSX.Element | undefined; if (canInvite && !isDm && shouldShowComponent(UIComponent.InviteUsers)) { diff --git a/src/components/views/right_panel/RightPanelTabs.tsx b/src/components/views/right_panel/RightPanelTabs.tsx index 07a423dd05..4873f707b2 100644 --- a/src/components/views/right_panel/RightPanelTabs.tsx +++ b/src/components/views/right_panel/RightPanelTabs.tsx @@ -20,7 +20,7 @@ import { Action } from "../../../dispatcher/actions"; import SettingsStore from "../../../settings/SettingsStore"; import { UIComponent, UIFeature } from "../../../settings/UIFeature"; 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 { const tabs = [ @@ -48,7 +48,7 @@ export const RightPanelTabs: React.FC = ({ phase, room }): JSX.Element | } }); - const isVideoRoom = useIsVideoRoom(room); + const isVideoRoom = room !== undefined && calcIsVideoRoom(room); if (!shouldShowTabsForPhase(phase)) return null; diff --git a/src/components/views/right_panel/RoomSummaryCard.tsx b/src/components/views/right_panel/RoomSummaryCard.tsx index 39575d94f5..e67d38a2ed 100644 --- a/src/components/views/right_panel/RoomSummaryCard.tsx +++ b/src/components/views/right_panel/RoomSummaryCard.tsx @@ -70,7 +70,7 @@ import { useDispatcher } from "../../../hooks/useDispatcher"; import { Action } from "../../../dispatcher/actions"; import { Key } from "../../../Keyboard"; 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 { ReleaseAnnouncement } from "../../structures/ReleaseAnnouncement.tsx"; @@ -219,7 +219,7 @@ const RoomSummaryCard: React.FC = ({ const isRoomEncrypted = useIsEncrypted(cli, room); const roomContext = useContext(RoomContext); const e2eStatus = roomContext.e2eStatus; - const isVideoRoom = useIsVideoRoom(room); + const isVideoRoom = calcIsVideoRoom(room); const roomState = useRoomState(room); const directRoomsList = useAccountData>(room.client, EventType.Direct); diff --git a/src/components/views/rooms/LegacyRoomHeader.tsx b/src/components/views/rooms/LegacyRoomHeader.tsx index 3464aef30d..0e4cd28aa8 100644 --- a/src/components/views/rooms/LegacyRoomHeader.tsx +++ b/src/components/views/rooms/LegacyRoomHeader.tsx @@ -251,8 +251,7 @@ const CallButtons: FC = ({ room }) => { const [busy, setBusy] = useState(false); const showButtons = useSettingValue("showCallButtonsInComposer"); const groupCallsEnabled = useFeatureEnabled("feature_group_calls"); - const videoRoomsEnabled = useFeatureEnabled("feature_video_rooms"); - const isVideoRoom = useMemo(() => videoRoomsEnabled && calcIsVideoRoom(room), [videoRoomsEnabled, room]); + const isVideoRoom = useMemo(() => calcIsVideoRoom(room), [room]); const useElementCallExclusively = useMemo(() => { return SdkConfig.get("element_call").use_exclusively; }, []); @@ -290,53 +289,13 @@ const CallButtons: FC = ({ room }) => { if (isVideoRoom || !showButtons) { return null; - } else if (groupCallsEnabled) { - if (useElementCallExclusively) { - if (hasGroupCall) { - return makeVideoCallButton(new DisabledWithReason(_t("voip|disabled_ongoing_call"))); - } else if (mayCreateElementCalls) { - 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 if (groupCallsEnabled && useElementCallExclusively) { + if (hasGroupCall) { + return makeVideoCallButton(new DisabledWithReason(_t("voip|disabled_ongoing_call"))); + } else if (mayCreateElementCalls) { + return makeVideoCallButton("element"); } else { - const videoCallBehavior = mayCreateElementCalls - ? "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)} - - ); + return makeVideoCallButton(new DisabledWithReason(_t("voip|disabled_no_perms_start_video_call"))); } } else if (hasLegacyCall || hasJitsiWidget) { return ( @@ -352,18 +311,31 @@ const CallButtons: FC = ({ room }) => { {makeVideoCallButton(new DisabledWithReason(_t("voip|disabled_no_one_here")))} ); - } else if (functionalMembers.length === 2 || mayEditWidgets) { + } else if (functionalMembers.length === 2) { + return ( + <> + {makeVoiceCallButton("legacy_or_jitsi")} + {makeVideoCallButton(groupCallsEnabled ? "legacy_or_element" : "legacy_or_jitsi")} + + ); + } else if (mayEditWidgets) { return ( <> {makeVoiceCallButton("legacy_or_jitsi")} - {makeVideoCallButton("legacy_or_jitsi")} + {makeVideoCallButton( + groupCallsEnabled && mayCreateElementCalls ? "jitsi_or_element" : "legacy_or_jitsi", + )} ); } else { + const videoCallBehavior = + groupCallsEnabled && mayCreateElementCalls + ? "element" + : new DisabledWithReason(_t("voip|disabled_no_perms_start_video_call")); return ( <> {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 { } 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; if (this.props.room) { diff --git a/src/components/views/rooms/RoomHeader.tsx b/src/components/views/rooms/RoomHeader.tsx index fae9e264ae..6e04a60601 100644 --- a/src/components/views/rooms/RoomHeader.tsx +++ b/src/components/views/rooms/RoomHeader.tsx @@ -42,7 +42,7 @@ import RightPanelStore from "../../../stores/right-panel/RightPanelStore"; import PosthogTrackers from "../../../PosthogTrackers"; import { VideoRoomChatButton } from "./RoomHeader/VideoRoomChatButton"; import { RoomKnocksBar } from "./RoomKnocksBar"; -import { useIsVideoRoom } from "../../../utils/video-rooms"; +import { isVideoRoom as calcIsVideoRoom } from "../../../utils/video-rooms"; import { notificationLevelToIndicator } from "../../../utils/notifications"; import { CallGuestLinkButton } from "./RoomHeader/CallGuestLinkButton"; import { ButtonEvent } from "../elements/AccessibleButton"; @@ -225,7 +225,7 @@ export default function RoomHeader({ } const roomContext = useContext(RoomContext); - const isVideoRoom = useIsVideoRoom(room); + const isVideoRoom = calcIsVideoRoom(room); const showChatButton = isVideoRoom || roomContext.mainSplitContentType === MainSplitContentType.MaximisedWidget || diff --git a/src/components/views/rooms/RoomInfoLine.tsx b/src/components/views/rooms/RoomInfoLine.tsx index c52b17cd91..5d570abb9a 100644 --- a/src/components/views/rooms/RoomInfoLine.tsx +++ b/src/components/views/rooms/RoomInfoLine.tsx @@ -17,7 +17,7 @@ import { useAsyncMemo } from "../../../hooks/useAsyncMemo"; import { useRoomState } from "../../../hooks/useRoomState"; import { useRoomMemberCount, useMyRoomMembership } from "../../../hooks/useRoomMembers"; import AccessibleButton from "../elements/AccessibleButton"; -import { useIsVideoRoom } from "../../../utils/video-rooms"; +import { isVideoRoom as calcIsVideoRoom } from "../../../utils/video-rooms"; interface IProps { room: Room; @@ -37,7 +37,7 @@ const RoomInfoLine: FC = ({ room }) => { const membership = useMyRoomMembership(room); const memberCount = useRoomMemberCount(room); - const isVideoRoom = useIsVideoRoom(room, true); + const isVideoRoom = calcIsVideoRoom(room); let iconClass: string; let roomType: string; diff --git a/src/components/views/rooms/RoomPreviewCard.tsx b/src/components/views/rooms/RoomPreviewCard.tsx index 9b926e6518..2c816e4743 100644 --- a/src/components/views/rooms/RoomPreviewCard.tsx +++ b/src/components/views/rooms/RoomPreviewCard.tsx @@ -17,7 +17,6 @@ import { UserTab } from "../dialogs/UserTab"; import { EffectiveMembership, getEffectiveMembership } from "../../../utils/membership"; import MatrixClientContext from "../../../contexts/MatrixClientContext"; import { useDispatcher } from "../../../hooks/useDispatcher"; -import { useFeatureEnabled } from "../../../hooks/useSettings"; import { useRoomState } from "../../../hooks/useRoomState"; import { useMyRoomMembership } from "../../../hooks/useRoomMembers"; import AccessibleButton from "../elements/AccessibleButton"; @@ -29,7 +28,7 @@ import RoomAvatar from "../avatars/RoomAvatar"; import MemberAvatar from "../avatars/MemberAvatar"; import { BetaPill } from "../beta/BetaCard"; import RoomInfoLine from "./RoomInfoLine"; -import { useIsVideoRoom } from "../../../utils/video-rooms"; +import { isVideoRoom as calcIsVideoRoom } from "../../../utils/video-rooms"; interface IProps { room: Room; @@ -43,8 +42,7 @@ interface IProps { // and viewing invite reasons to achieve parity with the default invite screen. const RoomPreviewCard: FC = ({ room, onJoinButtonClicked, onRejectButtonClicked }) => { const cli = useContext(MatrixClientContext); - const videoRoomsEnabled = useFeatureEnabled("feature_video_rooms"); - const isVideoRoom = useIsVideoRoom(room, true); + const isVideoRoom = calcIsVideoRoom(room); const myMembership = useMyRoomMembership(room); useDispatcher(defaultDispatcher, (payload) => { if (payload.action === Action.JoinRoomError && payload.roomId === room.roomId) { @@ -164,24 +162,6 @@ const RoomPreviewCard: FC = ({ room, onJoinButtonClicked, onRejectButton avatarRow = ; } - 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 = ( - - {_t("room|show_labs_settings")} - - ); - } - return (
{inviterSection} @@ -192,7 +172,11 @@ const RoomPreviewCard: FC = ({ room, onJoinButtonClicked, onRejectButton {room.getJoinRule() === "public" && } - {notice ?
{notice}
: null} + {cannotJoin ? ( +
+ {_t("room|join_failed_needs_invite", { roomName: room.name })} +
+ ) : null}
{joinButtons}
); diff --git a/src/hooks/room/useRoomCall.ts b/src/hooks/room/useRoomCall.ts index 537f443750..adf16cc5cc 100644 --- a/src/hooks/room/useRoomCall.ts +++ b/src/hooks/room/useRoomCall.ts @@ -133,10 +133,10 @@ export const useRoomCall = ( if (useElementCallExclusively && !hasJitsiWidget) { return [PlatformCallType.ElementCall]; } - if (hasGroupCall && WidgetType.CALL.matches(groupCall.widget.type)) { - // only allow joining the ongoing Element call if there is one. - return [PlatformCallType.ElementCall]; - } + } + if (hasGroupCall && WidgetType.CALL.matches(groupCall.widget.type)) { + // only allow joining the ongoing Element call if there is one. + return [PlatformCallType.ElementCall]; } return options; }, [ diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index 5a0b9d7cac..9b608c9ab3 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -2019,7 +2019,6 @@ "inviter_unknown": "Unknown", "invites_you_text": " invites you", "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_the_discussion": "Join the discussion", "join_title": "Join the room to participate", @@ -2088,7 +2087,6 @@ }, "this_room_button": "Search this room" }, - "show_labs_settings": "Show Labs settings", "status_bar": { "delete_all": "Delete all", "exceeded_resource_limit": "Your message wasn't sent because this homeserver has exceeded a resource limit. Please contact your service administrator to continue using the service.", @@ -2119,7 +2117,6 @@ }, "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_title": "Waiting for users to join %(brand)s" }, diff --git a/src/models/Call.ts b/src/models/Call.ts index 63859ae6f8..0238d15914 100644 --- a/src/models/Call.ts +++ b/src/models/Call.ts @@ -338,7 +338,7 @@ export class JitsiCall extends Call { public static get(room: Room): JitsiCall | null { // Only supported in video rooms - if (SettingsStore.getValue("feature_video_rooms") && room.isElementVideoRoom()) { + if (room.isElementVideoRoom()) { const apps = WidgetStore.instance.getApps(room.roomId); // The isVideoChannel field differentiates rich Jitsi calls from bare Jitsi widgets 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 { - // 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 hasEcWidget = apps.some((app) => WidgetType.CALL.matches(app.type)); - const session = room.client.matrixRTC.getRoomSession(room); - - // A call is present if we - // - have a widget: This means the create function was called. - // - 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. - 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); - } + const apps = WidgetStore.instance.getApps(room.roomId); + const hasEcWidget = apps.some((app) => WidgetType.CALL.matches(app.type)); + const session = room.client.matrixRTC.getRoomSession(room); + + // A call is present if we + // - have a widget: This means the create function was called. + // - 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. + 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; diff --git a/src/utils/video-rooms.ts b/src/utils/video-rooms.ts index bb66747e3f..a0003d59d9 100644 --- a/src/utils/video-rooms.ts +++ b/src/utils/video-rooms.ts @@ -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 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. - * @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. + * Determines whether the given room is a video room. */ -export const useIsVideoRoom = (room?: Room, skipVideoRoomsEnabledCheck = false): boolean => { - 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); -}; +export const isVideoRoom = (room: Room): boolean => room.isElementVideoRoom() || room.isCallRoom(); diff --git a/test/Notifier-test.ts b/test/Notifier-test.ts index e5b1a303e8..08a070104c 100644 --- a/test/Notifier-test.ts +++ b/test/Notifier-test.ts @@ -423,15 +423,7 @@ describe("Notifier", () => { return callEvent; }; - const setGroupCallsEnabled = (val: boolean) => { - 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); - + it("shows group call toast", () => { const notifyEvent = emitCallNotifyEvent(); 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", () => { - setGroupCallsEnabled(true); const spyCallMemberships = jest.spyOn(MatrixRTCSession, "callMembershipsForRoom").mockReturnValue([ new CallMembership( mkEvent({ @@ -483,8 +466,6 @@ describe("Notifier", () => { }); it("should not show toast when calling with non-group call event", () => { - setGroupCallsEnabled(true); - emitCallNotifyEvent("event_type"); expect(ToastStore.sharedInstance().addOrReplaceToast).not.toHaveBeenCalled(); diff --git a/test/components/structures/MatrixChat-test.tsx b/test/components/structures/MatrixChat-test.tsx index 83b4feea3a..e786370b7c 100644 --- a/test/components/structures/MatrixChat-test.tsx +++ b/test/components/structures/MatrixChat-test.tsx @@ -1039,10 +1039,13 @@ describe("", () => { }); describe("when encryption is force disabled", () => { - const unencryptedRoom = new Room("!unencrypted:server.org", loginClient, userId); - const encryptedRoom = new Room("!encrypted:server.org", loginClient, userId); + let unencryptedRoom: Room; + let encryptedRoom: Room; beforeEach(() => { + unencryptedRoom = new Room("!unencrypted:server.org", loginClient, userId); + encryptedRoom = new Room("!encrypted:server.org", loginClient, userId); + loginClient.getClientWellKnown.mockReturnValue({ "io.element.e2ee": { force_disable: true, diff --git a/test/components/views/rooms/RoomPreviewCard-test.tsx b/test/components/views/rooms/RoomPreviewCard-test.tsx index 0a4fa3089f..4d7cf1d51e 100644 --- a/test/components/views/rooms/RoomPreviewCard-test.tsx +++ b/test/components/views/rooms/RoomPreviewCard-test.tsx @@ -83,21 +83,4 @@ describe("RoomPreviewCard", () => { await renderPreview(); 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); - }); }); From 0b3b499fc4acc1e0925588f6f4c668e28d57821b Mon Sep 17 00:00:00 2001 From: David Baker Date: Mon, 30 Sep 2024 16:31:22 +0100 Subject: [PATCH 03/12] Fix label sync (#101) The colours can't have hashes on --- .github/labels.yml | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/.github/labels.yml b/.github/labels.yml index d240f3750b..7c4b66d7f0 100644 --- a/.github/labels.yml +++ b/.github/labels.yml @@ -1,28 +1,28 @@ - name: "A-Timesheet-1" description: "Log any time spent on this into the A-Timesheet-1 project" - color: "#5319E7" + color: "5319E7" - name: "backport staging" description: "Label to automatically backport PR to staging branch" - color: "#B60205" + color: "B60205" - name: "Dependencies" description: "Pull requests that update a dependency file" - color: "#0366d6" + color: "0366d6" - name: "Sponsored" - color: "#b506d8" + color: "b506d8" - name: "T-Deprecation" description: "A pull request that makes something deprecated" - color: "#98e6ae" + color: "98e6ae" - name: "X-Blocked" description: "The PR cannot move forward in any capacity until an action is made" color: "ff7979" - name: "X-Breaking-Change" - color: "#ff7979" + color: "ff7979" - name: "X-Upcoming-Release-Blocker" description: "This does not affect the current release cycle but will affect the next one" - color: "#e99695" + color: "e99695" - name: "Z-Community-PR" description: "Issue is solved by a community member's PR" - color: "#ededed" + color: "ededed" - name: "Z-Experiment" description: "Experimental PR, primarily up for its Netlify build, high likelihood of never making it beyond here." - color: "#b60205" + color: "b60205" From fe402e28bb9d1643121b96ed85c44444171b2def Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Mon, 30 Sep 2024 17:34:39 +0100 Subject: [PATCH 04/12] Fix flaky mobile registration tests (#102) * Fix flaky mobile registration tests Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> * Discard changes to src/theme.ts * Add comment Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --------- Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- test/components/structures/MatrixChat-test.tsx | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/test/components/structures/MatrixChat-test.tsx b/test/components/structures/MatrixChat-test.tsx index e786370b7c..dd4ae5de64 100644 --- a/test/components/structures/MatrixChat-test.tsx +++ b/test/components/structures/MatrixChat-test.tsx @@ -61,6 +61,10 @@ jest.mock("matrix-js-sdk/src/oidc/authorize", () => ({ completeAuthorizationCodeGrant: jest.fn(), })); +// Stub out ThemeWatcher as the necessary bits for themes are done in element-web's index.html and thus are lacking here, +// plus JSDOM's implementation of CSSStyleDeclaration has a bunch of differences to real browsers which cause issues. +jest.mock("../../../src/settings/watchers/ThemeWatcher"); + /** The matrix versions our mock server claims to support */ const SERVER_SUPPORTED_MATRIX_VERSIONS = ["v1.1", "v1.5", "v1.6", "v1.8", "v1.9"]; From 81bb56ae2bc60dde4089499d041c657e91ab290a Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Mon, 30 Sep 2024 17:34:48 +0100 Subject: [PATCH 05/12] Simplify Jest runs in CI to share failures with merge queue (#103) Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- .github/workflows/tests.yml | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 22a02779b3..3bf5700f03 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -89,13 +89,17 @@ jobs: coverage !coverage/lcov-report - skip_sonar: - name: Skip SonarCloud in merge queue - if: github.event_name == 'merge_group' || inputs.disable_coverage == 'true' - runs-on: ubuntu-latest + complete: + name: jest-tests needs: jest + if: always() + runs-on: ubuntu-latest steps: - - name: Skip SonarCloud + - if: needs.jest.result != 'skipped' && needs.jest.result != 'success' + run: exit 1 + + - name: Skip SonarCloud in merge queue + if: github.event_name == 'merge_group' || inputs.disable_coverage == 'true' uses: Sibz/github-status-action@faaa4d96fecf273bd762985e0e7f9f933c774918 # v1 with: authToken: ${{ secrets.GITHUB_TOKEN }} From 33c900e3070ae57d0c5c255107f030508e82a02f Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Mon, 30 Sep 2024 17:36:24 +0100 Subject: [PATCH 06/12] Remove right panel toggling behaviour on room header buttons (#100) * Remove right panel toggling behaviour on room header buttons Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> * Remove stale test Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> * Fix tests Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --------- Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- playwright/e2e/read-receipts/index.ts | 3 +++ src/components/views/rooms/RoomHeader.tsx | 10 +++++----- .../rooms/RoomHeader/VideoRoomChatButton.tsx | 2 +- src/stores/right-panel/RightPanelStore.ts | 17 ----------------- .../RoomHeader/VideoRoomChatButton-test.tsx | 4 ++-- .../stores/right-panel/RightPanelStore-test.ts | 18 ------------------ 6 files changed, 11 insertions(+), 43 deletions(-) diff --git a/playwright/e2e/read-receipts/index.ts b/playwright/e2e/read-receipts/index.ts index 47bd5c2d5c..f097ec839d 100644 --- a/playwright/e2e/read-receipts/index.ts +++ b/playwright/e2e/read-receipts/index.ts @@ -395,6 +395,9 @@ class Helpers { */ async closeThreadsPanel() { await this.page.locator(".mx_RoomHeader").getByLabel("Threads").click(); + if (await this.page.locator("#thread-panel").isVisible()) { + await this.page.locator(".mx_RoomHeader").getByLabel("Threads").click(); + } await expect(this.page.locator(".mx_RightPanel")).not.toBeVisible(); } diff --git a/src/components/views/rooms/RoomHeader.tsx b/src/components/views/rooms/RoomHeader.tsx index 6e04a60601..b09d4c70e2 100644 --- a/src/components/views/rooms/RoomHeader.tsx +++ b/src/components/views/rooms/RoomHeader.tsx @@ -255,7 +255,7 @@ export default function RoomHeader({