diff --git a/res/css/_components.pcss b/res/css/_components.pcss
index dadd9aadb3..a0241fb8b5 100644
--- a/res/css/_components.pcss
+++ b/res/css/_components.pcss
@@ -12,7 +12,6 @@
 @import "./components/views/beacon/_LeftPanelLiveShareWarning.pcss";
 @import "./components/views/beacon/_LiveTimeRemaining.pcss";
 @import "./components/views/beacon/_OwnBeaconStatus.pcss";
-@import "./components/views/beacon/_RoomLiveShareWarning.pcss";
 @import "./components/views/beacon/_ShareLatestLocation.pcss";
 @import "./components/views/beacon/_StyledLiveBeaconIcon.pcss";
 @import "./components/views/context_menus/_KebabContextMenu.pcss";
diff --git a/res/css/components/views/beacon/_RoomLiveShareWarning.pcss b/res/css/components/views/beacon/_RoomLiveShareWarning.pcss
deleted file mode 100644
index 60d23ed49a..0000000000
--- a/res/css/components/views/beacon/_RoomLiveShareWarning.pcss
+++ /dev/null
@@ -1,51 +0,0 @@
-/*
-Copyright 2024 New Vector Ltd.
-Copyright 2022 The Matrix.org Foundation C.I.C.
-
-SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only
-Please see LICENSE files in the repository root for full details.
-*/
-
-.mx_RoomLiveShareWarning {
-    width: 100%;
-
-    display: flex;
-    flex-direction: row;
-    align-items: center;
-
-    box-sizing: border-box;
-    padding: $spacing-12 $spacing-16;
-
-    color: $primary-content;
-    background-color: $system;
-    cursor: pointer;
-}
-
-.mx_RoomLiveShareWarning_icon {
-    height: 32px;
-    width: 32px;
-    margin-right: $spacing-8;
-}
-
-.mx_RoomLiveShareWarning_label {
-    flex: 1;
-    font-size: $font-15px;
-}
-
-.mx_RoomLiveShareWarning_spinner {
-    margin-right: $spacing-16;
-}
-
-.mx_RoomLiveShareWarning_closeButton {
-    @mixin ButtonResetDefault;
-    margin-left: $spacing-16;
-}
-
-.mx_RoomLiveShareWarning_stopButton {
-    margin-left: $spacing-16;
-}
-
-.mx_RoomLiveShareWarning_closeButtonIcon {
-    height: $font-18px;
-    padding: $spacing-4;
-}
diff --git a/src/components/structures/RoomView.tsx b/src/components/structures/RoomView.tsx
index b56172be42..ba30a3445d 100644
--- a/src/components/structures/RoomView.tsx
+++ b/src/components/structures/RoomView.tsx
@@ -65,7 +65,6 @@ import RoomPreviewBar from "../views/rooms/RoomPreviewBar";
 import RoomPreviewCard from "../views/rooms/RoomPreviewCard";
 import RoomUpgradeWarningBar from "../views/rooms/RoomUpgradeWarningBar";
 import AuxPanel from "../views/rooms/AuxPanel";
-import LegacyRoomHeader from "../views/rooms/LegacyRoomHeader";
 import RoomHeader from "../views/rooms/RoomHeader";
 import { IOOBData, IThreepidInvite } from "../../stores/ThreepidInviteStore";
 import EffectsOverlay from "../views/elements/EffectsOverlay";
@@ -313,26 +312,7 @@ function LocalRoomView(props: LocalRoomViewProps): ReactElement {
     return (
         <div className="mx_RoomView mx_RoomView--local">
             <ErrorBoundary>
-                {SettingsStore.getValue("feature_new_room_decoration_ui") ? (
-                    <RoomHeader room={room} />
-                ) : (
-                    <LegacyRoomHeader
-                        room={context.room}
-                        searchInfo={undefined}
-                        inRoom={true}
-                        onSearchClick={null}
-                        onInviteClick={null}
-                        onForgetClick={null}
-                        e2eStatus={room.encrypted ? E2EStatus.Normal : undefined}
-                        onAppsClick={null}
-                        appsShown={false}
-                        excludedRightPanelPhaseButtons={[]}
-                        showButtons={false}
-                        enableRoomOptionsMenu={false}
-                        viewingCall={false}
-                        activeCall={null}
-                    />
-                )}
+                <RoomHeader room={room} />
                 <main className="mx_RoomView_body" ref={props.roomView}>
                     <FileDropTarget parent={props.roomView.current} onFileDrop={props.onFileDrop} />
                     <div className="mx_RoomView_timeline">
@@ -366,26 +346,7 @@ function LocalRoomCreateLoader(props: ILocalRoomCreateLoaderProps): ReactElement
     return (
         <div className="mx_RoomView mx_RoomView--local">
             <ErrorBoundary>
-                {SettingsStore.getValue("feature_new_room_decoration_ui") ? (
-                    <RoomHeader room={props.localRoom} />
-                ) : (
-                    <LegacyRoomHeader
-                        room={props.localRoom}
-                        searchInfo={undefined}
-                        inRoom={true}
-                        onSearchClick={null}
-                        onInviteClick={null}
-                        onForgetClick={null}
-                        e2eStatus={props.localRoom.encrypted ? E2EStatus.Normal : undefined}
-                        onAppsClick={null}
-                        appsShown={false}
-                        excludedRightPanelPhaseButtons={[]}
-                        showButtons={false}
-                        enableRoomOptionsMenu={false}
-                        viewingCall={false}
-                        activeCall={null}
-                    />
-                )}
+                <RoomHeader room={props.localRoom} />
                 <div className="mx_RoomView_body">
                     <LargeLoader text={text} />
                 </div>
@@ -1753,13 +1714,6 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
         });
     };
 
-    private onAppsClick = (): void => {
-        dis.dispatch({
-            action: "appsDrawer",
-            show: !this.state.showApps,
-        });
-    };
-
     private onForgetClick = (): void => {
         dis.dispatch({
             action: "forget_room",
@@ -1836,10 +1790,6 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
         dis.fire(Action.ViewRoomDirectory);
     };
 
-    private onSearchClick = (): void => {
-        dis.fire(Action.FocusMessageSearch);
-    };
-
     private onSearchChange = debounce((e: ChangeEvent): void => {
         const term = (e.target as HTMLInputElement).value;
         this.onSearch(term);
@@ -2121,15 +2071,13 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
             }
         }
 
-        const roomHeaderType = SettingsStore.getValue("feature_new_room_decoration_ui") ? "new" : "legacy";
-
         if (!this.state.room) {
             const loading = !this.state.matrixClientIsReady || this.state.roomLoading || this.state.peekLoading;
             if (loading) {
                 // Assume preview loading if we don't have a ready client or a room ID (still resolving the alias)
                 const previewLoading = !this.state.matrixClientIsReady || !this.state.roomId || this.state.peekLoading;
                 return (
-                    <div className="mx_RoomView" data-room-header={roomHeaderType}>
+                    <div className="mx_RoomView">
                         <ErrorBoundary>
                             <RoomPreviewBar
                                 canPreview={false}
@@ -2154,7 +2102,7 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
                 // We've got to this room by following a link, possibly a third party invite.
                 const roomAlias = this.state.roomAlias;
                 return (
-                    <div className="mx_RoomView" data-room-header={roomHeaderType}>
+                    <div className="mx_RoomView">
                         <ErrorBoundary>
                             <RoomPreviewBar
                                 onJoinClick={this.onJoinButtonClicked}
@@ -2224,7 +2172,7 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
 
                 // We have a regular invite for this room.
                 return (
-                    <div className="mx_RoomView" data-room-header={roomHeaderType}>
+                    <div className="mx_RoomView">
                         <ErrorBoundary>
                             <RoomPreviewBar
                                 onJoinClick={this.onJoinButtonClicked}
@@ -2248,7 +2196,7 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
             ([KnownMembership.Knock, KnownMembership.Leave] as Array<string>).includes(myMembership)
         ) {
             return (
-                <div className="mx_RoomView" data-room-header={roomHeaderType}>
+                <div className="mx_RoomView">
                     <ErrorBoundary>
                         <RoomPreviewBar
                             onJoinClick={this.onJoinButtonClicked}
@@ -2354,11 +2302,7 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
                 />
             );
             if (!this.state.canPeek && !this.state.room?.isSpaceRoom()) {
-                return (
-                    <div className="mx_RoomView" data-room-header={roomHeaderType}>
-                        {previewBar}
-                    </div>
-                );
+                return <div className="mx_RoomView">{previewBar}</div>;
             }
         } else if (hiddenHighlightCount > 0) {
             aux = (
@@ -2587,46 +2531,9 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
         }
         const mainSplitContentClasses = classNames("mx_RoomView_body", mainSplitContentClassName);
 
-        let excludedRightPanelPhaseButtons = [RightPanelPhases.Timeline];
-        let onAppsClick: (() => void) | null = this.onAppsClick;
-        let onForgetClick: (() => void) | null = this.onForgetClick;
-        let onSearchClick: (() => void) | null = this.onSearchClick;
-        let onInviteClick: (() => void) | null = null;
-        let viewingCall = false;
-
-        // Simplify the header for other main split types
-        switch (mainSplitContentType) {
-            case MainSplitContentType.MaximisedWidget:
-                excludedRightPanelPhaseButtons = [];
-                onAppsClick = null;
-                onForgetClick = null;
-                onSearchClick = null;
-                break;
-            case MainSplitContentType.Call:
-                excludedRightPanelPhaseButtons = [];
-                onAppsClick = null;
-                onForgetClick = null;
-                onSearchClick = null;
-                if (this.state.room.canInvite(this.context.client.getSafeUserId())) {
-                    onInviteClick = this.onInviteClick;
-                }
-                viewingCall = true;
-        }
-
-        const myMember = this.state.room!.getMember(this.context.client!.getSafeUserId());
-        const showForgetButton =
-            !this.context.client.isGuest() &&
-            (([KnownMembership.Leave, KnownMembership.Ban] as Array<string>).includes(myMembership) ||
-                myMember?.isKicked());
-
         return (
             <RoomContext.Provider value={this.state}>
-                <div
-                    className={mainClasses}
-                    ref={this.roomView}
-                    onKeyDown={this.onReactKeyDown}
-                    data-room-header={roomHeaderType}
-                >
+                <div className={mainClasses} ref={this.roomView} onKeyDown={this.onReactKeyDown}>
                     {showChatEffects && this.roomView.current && (
                         <EffectsOverlay roomWidth={this.roomView.current.offsetWidth} />
                     )}
@@ -2644,31 +2551,10 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
                                 ref={this.roomViewBody}
                                 data-layout={this.state.layout}
                             >
-                                {SettingsStore.getValue("feature_new_room_decoration_ui") ? (
-                                    <RoomHeader
-                                        room={this.state.room}
-                                        additionalButtons={this.state.viewRoomOpts.buttons}
-                                    />
-                                ) : (
-                                    <LegacyRoomHeader
-                                        room={this.state.room}
-                                        searchInfo={this.state.search}
-                                        oobData={this.props.oobData}
-                                        inRoom={myMembership === KnownMembership.Join}
-                                        onSearchClick={onSearchClick}
-                                        onInviteClick={onInviteClick}
-                                        onForgetClick={showForgetButton ? onForgetClick : null}
-                                        e2eStatus={this.state.e2eStatus}
-                                        onAppsClick={this.state.hasPinnedWidgets ? onAppsClick : null}
-                                        appsShown={this.state.showApps}
-                                        excludedRightPanelPhaseButtons={excludedRightPanelPhaseButtons}
-                                        showButtons={!this.viewsLocalRoom}
-                                        enableRoomOptionsMenu={!this.viewsLocalRoom}
-                                        viewingCall={viewingCall}
-                                        activeCall={this.state.activeCall}
-                                        additionalButtons={this.state.viewRoomOpts.buttons}
-                                    />
-                                )}
+                                <RoomHeader
+                                    room={this.state.room}
+                                    additionalButtons={this.state.viewRoomOpts.buttons}
+                                />
                                 {mainSplitBody}
                             </div>
                         </MainSplit>
diff --git a/src/components/structures/WaitingForThirdPartyRoomView.tsx b/src/components/structures/WaitingForThirdPartyRoomView.tsx
index fd21ebeb0e..fd9afc50f2 100644
--- a/src/components/structures/WaitingForThirdPartyRoomView.tsx
+++ b/src/components/structures/WaitingForThirdPartyRoomView.tsx
@@ -11,9 +11,7 @@ import { MatrixEvent } from "matrix-js-sdk/src/matrix";
 
 import { useRoomContext } from "../../contexts/RoomContext";
 import ResizeNotifier from "../../utils/ResizeNotifier";
-import { E2EStatus } from "../../utils/ShieldUtils";
 import ErrorBoundary from "../views/elements/ErrorBoundary";
-import LegacyRoomHeader from "../views/rooms/LegacyRoomHeader";
 import RoomHeader from "../views/rooms/RoomHeader";
 import ScrollPanel from "./ScrollPanel";
 import EventTileBubble from "../views/messages/EventTileBubble";
@@ -21,7 +19,6 @@ import NewRoomIntro from "../views/rooms/NewRoomIntro";
 import { UnwrappedEventTile } from "../views/rooms/EventTile";
 import { _t } from "../../languageHandler";
 import SdkConfig from "../../SdkConfig";
-import SettingsStore from "../../settings/SettingsStore";
 
 interface Props {
     roomView: RefObject<HTMLElement>;
@@ -41,24 +38,7 @@ export const WaitingForThirdPartyRoomView: React.FC<Props> = ({ roomView, resize
     return (
         <div className="mx_RoomView mx_RoomView--local">
             <ErrorBoundary>
-                {SettingsStore.getValue("feature_new_room_decoration_ui") ? (
-                    <RoomHeader room={context.room!} />
-                ) : (
-                    <LegacyRoomHeader
-                        room={context.room}
-                        inRoom={true}
-                        onInviteClick={null}
-                        onForgetClick={null}
-                        e2eStatus={E2EStatus.Normal}
-                        onAppsClick={null}
-                        appsShown={false}
-                        excludedRightPanelPhaseButtons={[]}
-                        showButtons={false}
-                        enableRoomOptionsMenu={false}
-                        viewingCall={false}
-                        activeCall={null}
-                    />
-                )}
+                <RoomHeader room={context.room!} />
                 <main className="mx_RoomView_body" ref={roomView}>
                     <div className="mx_RoomView_timeline">
                         <ScrollPanel className="mx_RoomView_messagePanel" resizeNotifier={resizeNotifier}>
diff --git a/src/components/views/beacon/RoomLiveShareWarning.tsx b/src/components/views/beacon/RoomLiveShareWarning.tsx
deleted file mode 100644
index 5d37ef075e..0000000000
--- a/src/components/views/beacon/RoomLiveShareWarning.tsx
+++ /dev/null
@@ -1,148 +0,0 @@
-/*
-Copyright 2024 New Vector Ltd.
-Copyright 2022 The Matrix.org Foundation C.I.C.
-
-SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only
-Please see LICENSE files in the repository root for full details.
-*/
-
-import React from "react";
-import { Room } from "matrix-js-sdk/src/matrix";
-import CloseIcon from "@vector-im/compound-design-tokens/assets/web/icons/close";
-
-import { _t } from "../../../languageHandler";
-import { useEventEmitterState } from "../../../hooks/useEventEmitter";
-import { OwnBeaconStore, OwnBeaconStoreEvent } from "../../../stores/OwnBeaconStore";
-import { useOwnLiveBeacons } from "../../../utils/beacon";
-import AccessibleButton, { ButtonEvent } from "../elements/AccessibleButton";
-import Spinner from "../elements/Spinner";
-import StyledLiveBeaconIcon from "./StyledLiveBeaconIcon";
-import LiveTimeRemaining from "./LiveTimeRemaining";
-import dispatcher from "../../../dispatcher/dispatcher";
-import { ViewRoomPayload } from "../../../dispatcher/payloads/ViewRoomPayload";
-import { Action } from "../../../dispatcher/actions";
-
-const getLabel = (hasLocationPublishError: boolean, hasStopSharingError: boolean): string => {
-    if (hasLocationPublishError) {
-        return _t("location_sharing|error_sharing_live_location_try_again");
-    }
-    if (hasStopSharingError) {
-        return _t("location_sharing|error_stopping_live_location_try_again");
-    }
-    return _t("location_sharing|live_location_active");
-};
-
-interface RoomLiveShareWarningInnerProps {
-    liveBeaconIds: string[];
-    roomId: Room["roomId"];
-}
-const RoomLiveShareWarningInner: React.FC<RoomLiveShareWarningInnerProps> = ({ liveBeaconIds, roomId }) => {
-    const {
-        onStopSharing,
-        onResetLocationPublishError,
-        beacon,
-        stoppingInProgress,
-        hasStopSharingError,
-        hasLocationPublishError,
-    } = useOwnLiveBeacons(liveBeaconIds);
-
-    if (!beacon) {
-        return null;
-    }
-
-    const hasError = hasStopSharingError || hasLocationPublishError;
-
-    // eat events from buttons so navigate to tile
-    // is not triggered
-    const stopPropagationWrapper =
-        (callback: () => void) =>
-        (e?: ButtonEvent): void => {
-            e?.stopPropagation();
-            callback();
-        };
-
-    const onButtonClick = (): void => {
-        if (hasLocationPublishError) {
-            onResetLocationPublishError();
-        } else {
-            onStopSharing();
-        }
-    };
-
-    const onClick = (): void => {
-        dispatcher.dispatch<ViewRoomPayload>({
-            action: Action.ViewRoom,
-            room_id: beacon.roomId,
-            metricsTrigger: undefined,
-            event_id: beacon.beaconInfoId,
-            scroll_into_view: true,
-            highlighted: true,
-        });
-    };
-
-    return (
-        <div className="mx_RoomLiveShareWarning" onClick={onClick}>
-            <StyledLiveBeaconIcon className="mx_RoomLiveShareWarning_icon" withError={hasError} />
-
-            <span className="mx_RoomLiveShareWarning_label">
-                {getLabel(hasLocationPublishError, hasStopSharingError)}
-            </span>
-
-            {stoppingInProgress && (
-                <span className="mx_RoomLiveShareWarning_spinner">
-                    <Spinner h={16} w={16} />
-                </span>
-            )}
-            {!stoppingInProgress && !hasError && <LiveTimeRemaining beacon={beacon} />}
-
-            <AccessibleButton
-                className="mx_RoomLiveShareWarning_stopButton"
-                data-testid="room-live-share-primary-button"
-                onClick={stopPropagationWrapper(onButtonClick)}
-                kind="danger"
-                element="button"
-                disabled={stoppingInProgress}
-            >
-                {hasError ? _t("action|retry") : _t("action|stop")}
-            </AccessibleButton>
-            {hasLocationPublishError && (
-                <AccessibleButton
-                    data-testid="room-live-share-wire-error-close-button"
-                    title={_t("location_sharing|stop_and_close")}
-                    element="button"
-                    className="mx_RoomLiveShareWarning_closeButton"
-                    onClick={stopPropagationWrapper(onStopSharing)}
-                >
-                    <CloseIcon className="mx_RoomLiveShareWarning_closeButtonIcon" />
-                </AccessibleButton>
-            )}
-        </div>
-    );
-};
-
-interface Props {
-    roomId: Room["roomId"];
-}
-const RoomLiveShareWarning: React.FC<Props> = ({ roomId }) => {
-    // do we have an active geolocation.watchPosition
-    const isMonitoringLiveLocation = useEventEmitterState(
-        OwnBeaconStore.instance,
-        OwnBeaconStoreEvent.MonitoringLivePosition,
-        () => OwnBeaconStore.instance.isMonitoringLiveLocation,
-    );
-
-    const liveBeaconIds = useEventEmitterState(OwnBeaconStore.instance, OwnBeaconStoreEvent.LivenessChange, () =>
-        OwnBeaconStore.instance.getLiveBeaconIds(roomId),
-    );
-
-    if (!isMonitoringLiveLocation || !liveBeaconIds.length) {
-        // This logic is entangled with the RoomCallBanner-test's. The tests need updating if this logic changes.
-        return null;
-    }
-
-    // split into outer/inner to avoid watching various parts of live beacon state
-    // when there are none
-    return <RoomLiveShareWarningInner liveBeaconIds={liveBeaconIds} roomId={roomId} />;
-};
-
-export default RoomLiveShareWarning;
diff --git a/src/components/views/context_menus/RoomContextMenu.tsx b/src/components/views/context_menus/RoomContextMenu.tsx
deleted file mode 100644
index f431218b06..0000000000
--- a/src/components/views/context_menus/RoomContextMenu.tsx
+++ /dev/null
@@ -1,389 +0,0 @@
-/*
-Copyright 2024 New Vector Ltd.
-Copyright 2021-2023 The Matrix.org Foundation C.I.C.
-
-SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only
-Please see LICENSE files in the repository root for full details.
-*/
-
-import React, { useContext } from "react";
-import { Room, RoomMemberEvent } from "matrix-js-sdk/src/matrix";
-import { KnownMembership } from "matrix-js-sdk/src/types";
-
-import { IProps as IContextMenuProps } from "../../structures/ContextMenu";
-import IconizedContextMenu, {
-    IconizedContextMenuCheckbox,
-    IconizedContextMenuOption,
-    IconizedContextMenuOptionList,
-} from "./IconizedContextMenu";
-import { _t } from "../../../languageHandler";
-import MatrixClientContext from "../../../contexts/MatrixClientContext";
-import { ButtonEvent } from "../elements/AccessibleButton";
-import { DefaultTagID, TagID } from "../../../stores/room-list/models";
-import RoomListStore, { LISTS_UPDATE_EVENT } from "../../../stores/room-list/RoomListStore";
-import dis from "../../../dispatcher/dispatcher";
-import { EchoChamber } from "../../../stores/local-echo/EchoChamber";
-import { RoomNotifState } from "../../../RoomNotifs";
-import Modal from "../../../Modal";
-import ExportDialog from "../dialogs/ExportDialog";
-import { RightPanelPhases } from "../../../stores/right-panel/RightPanelStorePhases";
-import { RoomSettingsTab } from "../dialogs/RoomSettingsDialog";
-import { useEventEmitterState } from "../../../hooks/useEventEmitter";
-import RightPanelStore from "../../../stores/right-panel/RightPanelStore";
-import DMRoomMap from "../../../utils/DMRoomMap";
-import { Action } from "../../../dispatcher/actions";
-import PosthogTrackers from "../../../PosthogTrackers";
-import { ViewRoomPayload } from "../../../dispatcher/payloads/ViewRoomPayload";
-import { getKeyBindingsManager } from "../../../KeyBindingsManager";
-import { KeyBindingAction } from "../../../accessibility/KeyboardShortcuts";
-import SettingsStore from "../../../settings/SettingsStore";
-import { SdkContextClass } from "../../../contexts/SDKContext";
-import { shouldShowComponent } from "../../../customisations/helpers/UIComponents";
-import { UIComponent } from "../../../settings/UIFeature";
-import { DeveloperToolsOption } from "./DeveloperToolsOption";
-import { tagRoom } from "../../../utils/room/tagRoom";
-import { isVideoRoom as calcIsVideoRoom } from "../../../utils/video-rooms";
-import { usePinnedEvents } from "../../../hooks/usePinnedEvents";
-
-interface IProps extends IContextMenuProps {
-    room: Room;
-}
-
-/**
- * Room context menu accessible via the room header.
- * @deprecated will be removed as part of `feature_new_room_decoration_ui`
- */
-const RoomContextMenu: React.FC<IProps> = ({ room, onFinished, ...props }) => {
-    const cli = useContext(MatrixClientContext);
-    const roomTags = useEventEmitterState(RoomListStore.instance, LISTS_UPDATE_EVENT, () =>
-        RoomListStore.instance.getTagsForRoom(room),
-    );
-
-    let leaveOption: JSX.Element | undefined;
-    if (roomTags.includes(DefaultTagID.Archived)) {
-        const onForgetRoomClick = (ev: ButtonEvent): void => {
-            ev.preventDefault();
-            ev.stopPropagation();
-
-            dis.dispatch({
-                action: "forget_room",
-                room_id: room.roomId,
-            });
-            onFinished();
-        };
-
-        leaveOption = (
-            <IconizedContextMenuOption
-                iconClassName="mx_RoomTile_iconSignOut"
-                label={_t("room|context_menu|forget")}
-                className="mx_IconizedContextMenu_option_red"
-                onClick={onForgetRoomClick}
-            />
-        );
-    } else {
-        const onLeaveRoomClick = (ev: ButtonEvent): void => {
-            ev.preventDefault();
-            ev.stopPropagation();
-
-            dis.dispatch({
-                action: "leave_room",
-                room_id: room.roomId,
-            });
-            onFinished();
-
-            PosthogTrackers.trackInteraction("WebRoomHeaderContextMenuLeaveItem", ev);
-        };
-
-        leaveOption = (
-            <IconizedContextMenuOption
-                onClick={onLeaveRoomClick}
-                label={_t("action|leave")}
-                className="mx_IconizedContextMenu_option_red"
-                iconClassName="mx_RoomTile_iconSignOut"
-            />
-        );
-    }
-
-    const isDm = DMRoomMap.shared().getUserIdForRoomId(room.roomId);
-    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)) {
-        const onInviteClick = (ev: ButtonEvent): void => {
-            ev.preventDefault();
-            ev.stopPropagation();
-
-            dis.dispatch({
-                action: "view_invite",
-                roomId: room.roomId,
-            });
-            onFinished();
-
-            PosthogTrackers.trackInteraction("WebRoomHeaderContextMenuInviteItem", ev);
-        };
-
-        inviteOption = (
-            <IconizedContextMenuOption
-                onClick={onInviteClick}
-                label={_t("action|invite")}
-                iconClassName="mx_RoomTile_iconInvite"
-            />
-        );
-    }
-
-    let favouriteOption: JSX.Element | undefined;
-    let lowPriorityOption: JSX.Element | undefined;
-    let notificationOption: JSX.Element | undefined;
-    if (room.getMyMembership() === KnownMembership.Join) {
-        const isFavorite = roomTags.includes(DefaultTagID.Favourite);
-        favouriteOption = (
-            <IconizedContextMenuCheckbox
-                onClick={(e) => {
-                    onTagRoom(e, DefaultTagID.Favourite);
-                    PosthogTrackers.trackInteraction("WebRoomHeaderContextMenuFavouriteToggle", e);
-                }}
-                active={isFavorite}
-                label={isFavorite ? _t("room|context_menu|unfavourite") : _t("room|context_menu|favourite")}
-                iconClassName="mx_RoomTile_iconStar"
-            />
-        );
-
-        const isLowPriority = roomTags.includes(DefaultTagID.LowPriority);
-        lowPriorityOption = (
-            <IconizedContextMenuCheckbox
-                onClick={(e) => onTagRoom(e, DefaultTagID.LowPriority)}
-                active={isLowPriority}
-                label={_t("common|low_priority")}
-                iconClassName="mx_RoomTile_iconArrowDown"
-            />
-        );
-
-        const echoChamber = EchoChamber.forRoom(room);
-        let notificationLabel: string | undefined;
-        let iconClassName: string | undefined;
-        switch (echoChamber.notificationVolume) {
-            case RoomNotifState.AllMessages:
-                notificationLabel = _t("notifications|default");
-                iconClassName = "mx_RoomTile_iconNotificationsDefault";
-                break;
-            case RoomNotifState.AllMessagesLoud:
-                notificationLabel = _t("notifications|all_messages");
-                iconClassName = "mx_RoomTile_iconNotificationsAllMessages";
-                break;
-            case RoomNotifState.MentionsOnly:
-                notificationLabel = _t("room|context_menu|mentions_only");
-                iconClassName = "mx_RoomTile_iconNotificationsMentionsKeywords";
-                break;
-            case RoomNotifState.Mute:
-                notificationLabel = _t("common|mute");
-                iconClassName = "mx_RoomTile_iconNotificationsNone";
-                break;
-        }
-
-        notificationOption = (
-            <IconizedContextMenuOption
-                onClick={(ev: ButtonEvent) => {
-                    ev.preventDefault();
-                    ev.stopPropagation();
-
-                    dis.dispatch({
-                        action: "open_room_settings",
-                        room_id: room.roomId,
-                        initial_tab_id: RoomSettingsTab.Notifications,
-                    });
-                    onFinished();
-
-                    PosthogTrackers.trackInteraction("WebRoomHeaderContextMenuNotificationsItem", ev);
-                }}
-                label={_t("notifications|enable_prompt_toast_title")}
-                iconClassName={iconClassName}
-            >
-                <span className="mx_IconizedContextMenu_sublabel">{notificationLabel}</span>
-            </IconizedContextMenuOption>
-        );
-    }
-
-    let peopleOption: JSX.Element | undefined;
-    let copyLinkOption: JSX.Element | undefined;
-    if (!isDm) {
-        peopleOption = (
-            <IconizedContextMenuOption
-                onClick={(ev: ButtonEvent) => {
-                    ev.preventDefault();
-                    ev.stopPropagation();
-
-                    ensureViewingRoom(ev);
-                    RightPanelStore.instance.pushCard({ phase: RightPanelPhases.RoomMemberList }, false);
-                    onFinished();
-                    PosthogTrackers.trackInteraction("WebRoomHeaderContextMenuPeopleItem", ev);
-                }}
-                label={_t("common|people")}
-                iconClassName="mx_RoomTile_iconPeople"
-            >
-                <span className="mx_IconizedContextMenu_sublabel">{room.getJoinedMemberCount()}</span>
-            </IconizedContextMenuOption>
-        );
-
-        copyLinkOption = (
-            <IconizedContextMenuOption
-                onClick={(ev: ButtonEvent) => {
-                    ev.preventDefault();
-                    ev.stopPropagation();
-
-                    dis.dispatch({
-                        action: "copy_room",
-                        room_id: room.roomId,
-                    });
-                    onFinished();
-                }}
-                label={_t("room|context_menu|copy_link")}
-                iconClassName="mx_RoomTile_iconCopyLink"
-            />
-        );
-    }
-
-    let filesOption: JSX.Element | undefined;
-    if (!isVideoRoom) {
-        filesOption = (
-            <IconizedContextMenuOption
-                onClick={(ev: ButtonEvent) => {
-                    ev.preventDefault();
-                    ev.stopPropagation();
-
-                    ensureViewingRoom(ev);
-                    RightPanelStore.instance.pushCard({ phase: RightPanelPhases.FilePanel }, false);
-                    onFinished();
-                }}
-                label={_t("right_panel|files_button")}
-                iconClassName="mx_RoomTile_iconFiles"
-            />
-        );
-    }
-
-    const pinCount = usePinnedEvents(room).length;
-
-    let pinsOption: JSX.Element | undefined;
-    if (!isVideoRoom) {
-        pinsOption = (
-            <IconizedContextMenuOption
-                onClick={(ev: ButtonEvent) => {
-                    ev.preventDefault();
-                    ev.stopPropagation();
-
-                    ensureViewingRoom(ev);
-                    RightPanelStore.instance.pushCard({ phase: RightPanelPhases.PinnedMessages }, false);
-                    onFinished();
-                }}
-                label={_t("right_panel|pinned_messages_button")}
-                iconClassName="mx_RoomTile_iconPins"
-            >
-                {pinCount > 0 && <span className="mx_IconizedContextMenu_sublabel">{pinCount}</span>}
-            </IconizedContextMenuOption>
-        );
-    }
-
-    let widgetsOption: JSX.Element | undefined;
-    if (!isVideoRoom) {
-        widgetsOption = (
-            <IconizedContextMenuOption
-                onClick={(ev: ButtonEvent) => {
-                    ev.preventDefault();
-                    ev.stopPropagation();
-
-                    ensureViewingRoom(ev);
-                    RightPanelStore.instance.setCard({ phase: RightPanelPhases.RoomSummary }, false);
-                    onFinished();
-                }}
-                label={_t("right_panel|widgets_section")}
-                iconClassName="mx_RoomTile_iconWidgets"
-            />
-        );
-    }
-
-    let exportChatOption: JSX.Element | undefined;
-    if (!isVideoRoom) {
-        exportChatOption = (
-            <IconizedContextMenuOption
-                onClick={(ev: ButtonEvent) => {
-                    ev.preventDefault();
-                    ev.stopPropagation();
-
-                    Modal.createDialog(ExportDialog, { room });
-                    onFinished();
-                }}
-                label={_t("right_panel|export_chat_button")}
-                iconClassName="mx_RoomTile_iconExport"
-            />
-        );
-    }
-
-    const onTagRoom = (ev: ButtonEvent, tagId: TagID): void => {
-        ev.preventDefault();
-        ev.stopPropagation();
-
-        tagRoom(room, tagId);
-
-        const action = getKeyBindingsManager().getAccessibilityAction(ev as React.KeyboardEvent);
-        switch (action) {
-            case KeyBindingAction.Enter:
-                // Implements https://www.w3.org/TR/wai-aria-practices/#keyboard-interaction-12
-                onFinished();
-                break;
-        }
-    };
-
-    const ensureViewingRoom = (ev: ButtonEvent): void => {
-        if (SdkContextClass.instance.roomViewStore.getRoomId() === room.roomId) return;
-        dis.dispatch<ViewRoomPayload>(
-            {
-                action: Action.ViewRoom,
-                room_id: room.roomId,
-                metricsTrigger: "RoomList",
-                metricsViaKeyboard: ev.type !== "click",
-            },
-            true,
-        );
-    };
-
-    return (
-        <IconizedContextMenu {...props} onFinished={onFinished} className="mx_RoomTile_contextMenu" compact>
-            <IconizedContextMenuOptionList>
-                {inviteOption}
-                {notificationOption}
-                {favouriteOption}
-                {peopleOption}
-                {filesOption}
-                {pinsOption}
-                {widgetsOption}
-                {lowPriorityOption}
-                {copyLinkOption}
-
-                <IconizedContextMenuOption
-                    onClick={(ev: ButtonEvent) => {
-                        ev.preventDefault();
-                        ev.stopPropagation();
-
-                        dis.dispatch({
-                            action: "open_room_settings",
-                            room_id: room.roomId,
-                        });
-                        onFinished();
-                        PosthogTrackers.trackInteraction("WebRoomHeaderContextMenuSettingsItem", ev);
-                    }}
-                    label={_t("common|settings")}
-                    iconClassName="mx_RoomTile_iconSettings"
-                />
-
-                {exportChatOption}
-
-                {SettingsStore.getValue("developerMode") && (
-                    <DeveloperToolsOption onFinished={onFinished} roomId={room.roomId} />
-                )}
-
-                {leaveOption}
-            </IconizedContextMenuOptionList>
-        </IconizedContextMenu>
-    );
-};
-
-export default RoomContextMenu;
diff --git a/src/components/views/right_panel/LegacyRoomHeaderButtons.tsx b/src/components/views/right_panel/LegacyRoomHeaderButtons.tsx
deleted file mode 100644
index 85b173c40c..0000000000
--- a/src/components/views/right_panel/LegacyRoomHeaderButtons.tsx
+++ /dev/null
@@ -1,318 +0,0 @@
-/*
-Copyright 2024 New Vector Ltd.
-Copyright 2019-2023 The Matrix.org Foundation C.I.C.
-Copyright 2018 New Vector Ltd
-Copyright 2017 Vector Creations Ltd
-Copyright 2017 New Vector Ltd
-Copyright 2015, 2016 OpenMarket Ltd
-
-SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only
-Please see LICENSE files in the repository root for full details.
-*/
-
-import React from "react";
-import classNames from "classnames";
-import { NotificationCountType, Room, RoomEvent, ThreadEvent } from "matrix-js-sdk/src/matrix";
-
-import { _t } from "../../../languageHandler";
-import HeaderButton from "./HeaderButton";
-import HeaderButtons, { HeaderKind } from "./HeaderButtons";
-import { RightPanelPhases } from "../../../stores/right-panel/RightPanelStorePhases";
-import { ActionPayload } from "../../../dispatcher/payloads";
-import RightPanelStore from "../../../stores/right-panel/RightPanelStore";
-import { showThreadPanel } from "../../../dispatcher/dispatch-actions/threads";
-import {
-    RoomNotificationStateStore,
-    UPDATE_STATUS_INDICATOR,
-} from "../../../stores/notifications/RoomNotificationStateStore";
-import { NotificationLevel } from "../../../stores/notifications/NotificationLevel";
-import { SummarizedNotificationState } from "../../../stores/notifications/SummarizedNotificationState";
-import PosthogTrackers from "../../../PosthogTrackers";
-import { ButtonEvent } from "../elements/AccessibleButton";
-import { doesRoomOrThreadHaveUnreadMessages } from "../../../Unread";
-import { usePinnedEvents, useReadPinnedEvents } from "../../../hooks/usePinnedEvents";
-
-const ROOM_INFO_PHASES = [
-    RightPanelPhases.RoomSummary,
-    RightPanelPhases.Widget,
-    RightPanelPhases.FilePanel,
-    RightPanelPhases.RoomMemberList,
-    RightPanelPhases.RoomMemberInfo,
-    RightPanelPhases.EncryptionPanel,
-    RightPanelPhases.Room3pidMemberInfo,
-];
-
-interface IUnreadIndicatorProps {
-    color?: NotificationLevel;
-}
-
-const UnreadIndicator: React.FC<IUnreadIndicatorProps> = ({ color }) => {
-    if (color === NotificationLevel.None) {
-        return null;
-    }
-
-    const classes = classNames({
-        mx_Indicator: true,
-        mx_LegacyRoomHeader_button_unreadIndicator: true,
-        mx_Indicator_activity: color === NotificationLevel.Activity,
-        mx_Indicator_notification: color === NotificationLevel.Notification,
-        mx_Indicator_highlight: color === NotificationLevel.Highlight,
-    });
-    return (
-        <>
-            <div className="mx_LegacyRoomHeader_button_unreadIndicator_bg" />
-            <div className={classes} />
-        </>
-    );
-};
-
-interface IHeaderButtonProps {
-    room: Room;
-    isHighlighted: boolean;
-    onClick: () => void;
-}
-
-const PinnedMessagesHeaderButton: React.FC<IHeaderButtonProps> = ({ room, isHighlighted, onClick }) => {
-    const pinnedEvents = usePinnedEvents(room);
-    const readPinnedEvents = useReadPinnedEvents(room);
-    if (!pinnedEvents?.length) return null;
-
-    let unreadIndicator;
-    if (pinnedEvents.some((id) => !readPinnedEvents.has(id))) {
-        unreadIndicator = <UnreadIndicator />;
-    }
-
-    return (
-        <HeaderButton
-            name="pinnedMessagesButton"
-            title={_t("right_panel|pinned_messages|title")}
-            isHighlighted={isHighlighted}
-            isUnread={!!unreadIndicator}
-            onClick={onClick}
-        >
-            {unreadIndicator}
-        </HeaderButton>
-    );
-};
-
-const TimelineCardHeaderButton: React.FC<IHeaderButtonProps> = ({ room, isHighlighted, onClick }) => {
-    let unreadIndicator;
-    const color = RoomNotificationStateStore.instance.getRoomState(room).level;
-    switch (color) {
-        case NotificationLevel.Activity:
-        case NotificationLevel.Notification:
-        case NotificationLevel.Highlight:
-            unreadIndicator = <UnreadIndicator color={color} />;
-    }
-    return (
-        <HeaderButton
-            name="timelineCardButton"
-            title={_t("right_panel|video_room_chat|title")}
-            isHighlighted={isHighlighted}
-            onClick={onClick}
-        >
-            {unreadIndicator}
-        </HeaderButton>
-    );
-};
-
-interface IProps {
-    room?: Room;
-    excludedRightPanelPhaseButtons?: Array<RightPanelPhases>;
-}
-
-/**
- * @deprecated will be removed as part of 'feature_new_room_decoration_ui'
- */
-export default class LegacyRoomHeaderButtons extends HeaderButtons<IProps> {
-    private static readonly THREAD_PHASES = [RightPanelPhases.ThreadPanel, RightPanelPhases.ThreadView];
-    private globalNotificationState: SummarizedNotificationState;
-
-    public constructor(props: IProps) {
-        super(props, HeaderKind.Room);
-        this.globalNotificationState = RoomNotificationStateStore.instance.globalState;
-    }
-
-    public componentDidMount(): void {
-        super.componentDidMount();
-        // Notification badge may change if the notification counts from the
-        // server change, if a new thread is created or updated, or if a
-        // receipt is sent in the thread.
-        this.props.room?.on(RoomEvent.UnreadNotifications, this.onNotificationUpdate);
-        this.props.room?.on(RoomEvent.Receipt, this.onNotificationUpdate);
-        this.props.room?.on(RoomEvent.Timeline, this.onNotificationUpdate);
-        this.props.room?.on(RoomEvent.Redaction, this.onNotificationUpdate);
-        this.props.room?.on(RoomEvent.LocalEchoUpdated, this.onNotificationUpdate);
-        this.props.room?.on(RoomEvent.MyMembership, this.onNotificationUpdate);
-        this.props.room?.on(ThreadEvent.New, this.onNotificationUpdate);
-        this.props.room?.on(ThreadEvent.Update, this.onNotificationUpdate);
-        this.onNotificationUpdate();
-        RoomNotificationStateStore.instance.on(UPDATE_STATUS_INDICATOR, this.onUpdateStatus);
-    }
-
-    public componentWillUnmount(): void {
-        super.componentWillUnmount();
-        this.props.room?.off(RoomEvent.UnreadNotifications, this.onNotificationUpdate);
-        this.props.room?.off(RoomEvent.Receipt, this.onNotificationUpdate);
-        this.props.room?.off(RoomEvent.Timeline, this.onNotificationUpdate);
-        this.props.room?.off(RoomEvent.Redaction, this.onNotificationUpdate);
-        this.props.room?.off(RoomEvent.LocalEchoUpdated, this.onNotificationUpdate);
-        this.props.room?.off(RoomEvent.MyMembership, this.onNotificationUpdate);
-        this.props.room?.off(ThreadEvent.New, this.onNotificationUpdate);
-        this.props.room?.off(ThreadEvent.Update, this.onNotificationUpdate);
-        RoomNotificationStateStore.instance.off(UPDATE_STATUS_INDICATOR, this.onUpdateStatus);
-    }
-
-    private onNotificationUpdate = (): void => {
-        // console.log
-        // XXX: why don't we read from this.state.threadNotificationLevel in the render methods?
-        this.setState({
-            threadNotificationLevel: this.notificationLevel,
-        });
-    };
-
-    private get notificationLevel(): NotificationLevel {
-        switch (this.props.room?.threadsAggregateNotificationType) {
-            case NotificationCountType.Highlight:
-                return NotificationLevel.Highlight;
-            case NotificationCountType.Total:
-                return NotificationLevel.Notification;
-        }
-        // We don't have any notified messages, but we might have unread messages. Let's
-        // find out.
-        for (const thread of this.props.room!.getThreads()) {
-            // If the current thread has unread messages, we're done.
-            if (doesRoomOrThreadHaveUnreadMessages(thread)) {
-                return NotificationLevel.Activity;
-            }
-        }
-        // Otherwise, no notification color.
-        return NotificationLevel.None;
-    }
-
-    private onUpdateStatus = (notificationState: SummarizedNotificationState): void => {
-        // XXX: why don't we read from this.state.globalNotificationCount in the render methods?
-        this.globalNotificationState = notificationState;
-        this.setState({
-            globalNotificationLevel: notificationState.level,
-        });
-    };
-
-    protected onAction(payload: ActionPayload): void {}
-
-    private onRoomSummaryClicked = (): void => {
-        // use roomPanelPhase rather than this.state.phase as it remembers the latest one if we close
-        const currentPhase = RightPanelStore.instance.currentCard.phase;
-        if (currentPhase && ROOM_INFO_PHASES.includes(currentPhase)) {
-            if (this.state.phase === currentPhase) {
-                RightPanelStore.instance.showOrHidePhase(currentPhase);
-            } else {
-                RightPanelStore.instance.showOrHidePhase(currentPhase, RightPanelStore.instance.currentCard.state);
-            }
-        } else {
-            // This toggles for us, if needed
-            RightPanelStore.instance.showOrHidePhase(RightPanelPhases.RoomSummary);
-        }
-    };
-
-    private onNotificationsClicked = (): void => {
-        // This toggles for us, if needed
-        RightPanelStore.instance.showOrHidePhase(RightPanelPhases.NotificationPanel);
-    };
-
-    private onPinnedMessagesClicked = (): void => {
-        // This toggles for us, if needed
-        RightPanelStore.instance.showOrHidePhase(RightPanelPhases.PinnedMessages);
-    };
-    private onTimelineCardClicked = (): void => {
-        RightPanelStore.instance.showOrHidePhase(RightPanelPhases.Timeline);
-    };
-
-    private onThreadsPanelClicked = (ev: ButtonEvent): void => {
-        if (this.state.phase && LegacyRoomHeaderButtons.THREAD_PHASES.includes(this.state.phase)) {
-            RightPanelStore.instance.togglePanel(this.props.room?.roomId ?? null);
-        } else {
-            showThreadPanel();
-            PosthogTrackers.trackInteraction("WebRoomHeaderButtonsThreadsButton", ev);
-        }
-    };
-
-    public renderButtons(): JSX.Element {
-        if (!this.props.room) {
-            return <></>;
-        }
-
-        const rightPanelPhaseButtons: Map<RightPanelPhases, any> = new Map();
-
-        rightPanelPhaseButtons.set(
-            RightPanelPhases.PinnedMessages,
-            <PinnedMessagesHeaderButton
-                key="pinnedMessagesButton"
-                room={this.props.room}
-                isHighlighted={this.isPhase(RightPanelPhases.PinnedMessages)}
-                onClick={this.onPinnedMessagesClicked}
-            />,
-        );
-
-        rightPanelPhaseButtons.set(
-            RightPanelPhases.Timeline,
-            <TimelineCardHeaderButton
-                key="timelineButton"
-                room={this.props.room}
-                isHighlighted={this.isPhase(RightPanelPhases.Timeline)}
-                onClick={this.onTimelineCardClicked}
-            />,
-        );
-        rightPanelPhaseButtons.set(
-            RightPanelPhases.ThreadPanel,
-            <HeaderButton
-                key={RightPanelPhases.ThreadPanel}
-                name="threadsButton"
-                data-testid="threadsButton"
-                title={_t("common|threads")}
-                onClick={this.onThreadsPanelClicked}
-                isHighlighted={this.isPhase(LegacyRoomHeaderButtons.THREAD_PHASES)}
-                isUnread={this.state.threadNotificationLevel > NotificationLevel.None}
-            >
-                <UnreadIndicator color={this.state.threadNotificationLevel} />
-            </HeaderButton>,
-        );
-        if (this.state.notificationsEnabled) {
-            rightPanelPhaseButtons.set(
-                RightPanelPhases.NotificationPanel,
-                <HeaderButton
-                    key="notifsButton"
-                    name="notifsButton"
-                    title={_t("notifications|enable_prompt_toast_title")}
-                    isHighlighted={this.isPhase(RightPanelPhases.NotificationPanel)}
-                    onClick={this.onNotificationsClicked}
-                    isUnread={this.globalNotificationState.level === NotificationLevel.Highlight}
-                >
-                    {this.globalNotificationState.level === NotificationLevel.Highlight ? (
-                        <UnreadIndicator color={this.globalNotificationState.level} />
-                    ) : null}
-                </HeaderButton>,
-            );
-        }
-        rightPanelPhaseButtons.set(
-            RightPanelPhases.RoomSummary,
-            <HeaderButton
-                key="roomSummaryButton"
-                name="roomSummaryButton"
-                title={_t("right_panel|room_summary_card|title")}
-                isHighlighted={this.isPhase(ROOM_INFO_PHASES)}
-                onClick={this.onRoomSummaryClicked}
-            />,
-        );
-
-        return (
-            <>
-                {Array.from(rightPanelPhaseButtons.keys()).map((phase) =>
-                    this.props.excludedRightPanelPhaseButtons?.includes(phase)
-                        ? null
-                        : rightPanelPhaseButtons.get(phase),
-                )}
-            </>
-        );
-    }
-}
diff --git a/src/components/views/rooms/LegacyRoomHeader.tsx b/src/components/views/rooms/LegacyRoomHeader.tsx
deleted file mode 100644
index 0e4cd28aa8..0000000000
--- a/src/components/views/rooms/LegacyRoomHeader.tsx
+++ /dev/null
@@ -1,818 +0,0 @@
-/*
-Copyright 2024 New Vector Ltd.
-Copyright 2019-2021 The Matrix.org Foundation C.I.C.
-Copyright 2015, 2016 OpenMarket Ltd
-
-SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only
-Please see LICENSE files in the repository root for full details.
-*/
-
-import React, { FC, useState, useMemo, useCallback } from "react";
-import classNames from "classnames";
-import { throttle } from "lodash";
-import { RoomStateEvent } from "matrix-js-sdk/src/matrix";
-import { CallType } from "matrix-js-sdk/src/webrtc/call";
-import { IconButton, Tooltip } from "@vector-im/compound-web";
-import { ViewRoomOpts } from "@matrix-org/react-sdk-module-api/lib/lifecycles/RoomViewLifecycle";
-
-import type { MatrixEvent, Room } from "matrix-js-sdk/src/matrix";
-import { _t } from "../../../languageHandler";
-import defaultDispatcher from "../../../dispatcher/dispatcher";
-import { Action } from "../../../dispatcher/actions";
-import { UserTab } from "../dialogs/UserTab";
-import SettingsStore from "../../../settings/SettingsStore";
-import RoomHeaderButtons from "../right_panel/LegacyRoomHeaderButtons";
-import E2EIcon from "./E2EIcon";
-import DecoratedRoomAvatar from "../avatars/DecoratedRoomAvatar";
-import AccessibleButton, { ButtonEvent } from "../elements/AccessibleButton";
-import RoomTopic from "../elements/RoomTopic";
-import RoomName from "../elements/RoomName";
-import { E2EStatus } from "../../../utils/ShieldUtils";
-import { IOOBData } from "../../../stores/ThreepidInviteStore";
-import { RoomKnocksBar } from "./RoomKnocksBar";
-import { aboveLeftOf, ContextMenuTooltipButton, useContextMenu } from "../../structures/ContextMenu";
-import RoomContextMenu from "../context_menus/RoomContextMenu";
-import { contextMenuBelow } from "./RoomTile";
-import { RoomNotificationStateStore } from "../../../stores/notifications/RoomNotificationStateStore";
-import { RightPanelPhases } from "../../../stores/right-panel/RightPanelStorePhases";
-import { NotificationStateEvents } from "../../../stores/notifications/NotificationState";
-import RoomContext from "../../../contexts/RoomContext";
-import RoomLiveShareWarning from "../beacon/RoomLiveShareWarning";
-import { BetaPill } from "../beta/BetaCard";
-import RightPanelStore from "../../../stores/right-panel/RightPanelStore";
-import { UPDATE_EVENT } from "../../../stores/AsyncStore";
-import { isVideoRoom as calcIsVideoRoom } from "../../../utils/video-rooms";
-import LegacyCallHandler, { LegacyCallHandlerEvent } from "../../../LegacyCallHandler";
-import { useFeatureEnabled, useSettingValue } from "../../../hooks/useSettings";
-import SdkConfig from "../../../SdkConfig";
-import { useEventEmitterState, useTypedEventEmitterState } from "../../../hooks/useEventEmitter";
-import { useWidgets } from "../../../utils/WidgetUtils";
-import { WidgetType } from "../../../widgets/WidgetType";
-import { useCall, useLayout } from "../../../hooks/useCall";
-import { getJoinedNonFunctionalMembers } from "../../../utils/room/getJoinedNonFunctionalMembers";
-import { Call, ElementCall, Layout } from "../../../models/Call";
-import IconizedContextMenu, {
-    IconizedContextMenuOption,
-    IconizedContextMenuOptionList,
-    IconizedContextMenuRadio,
-} from "../context_menus/IconizedContextMenu";
-import { ViewRoomPayload } from "../../../dispatcher/payloads/ViewRoomPayload";
-import { SessionDuration } from "../voip/CallDuration";
-import RoomCallBanner from "../beacon/RoomCallBanner";
-import { shouldShowComponent } from "../../../customisations/helpers/UIComponents";
-import { UIComponent } from "../../../settings/UIFeature";
-import { SearchInfo } from "../../../Searching";
-
-class DisabledWithReason {
-    public constructor(public readonly reason: string) {}
-}
-
-interface VoiceCallButtonProps {
-    room: Room;
-    busy: boolean;
-    setBusy: (value: boolean) => void;
-    behavior: DisabledWithReason | "legacy_or_jitsi";
-}
-
-/**
- * Button for starting voice calls, supporting only legacy 1:1 calls and Jitsi
- * widgets.
- */
-const VoiceCallButton: FC<VoiceCallButtonProps> = ({ room, busy, setBusy, behavior }) => {
-    const { onClick, tooltip, disabled } = useMemo(() => {
-        if (behavior instanceof DisabledWithReason) {
-            return {
-                onClick: () => {},
-                tooltip: behavior.reason,
-                disabled: true,
-            };
-        } else {
-            // behavior === "legacy_or_jitsi"
-            return {
-                onClick: async (ev: ButtonEvent): Promise<void> => {
-                    ev.preventDefault();
-                    setBusy(true);
-                    await LegacyCallHandler.instance.placeCall(room.roomId, CallType.Voice);
-                    setBusy(false);
-                },
-                disabled: false,
-            };
-        }
-    }, [behavior, room, setBusy]);
-
-    return (
-        <AccessibleButton
-            className="mx_LegacyRoomHeader_button mx_LegacyRoomHeader_voiceCallButton"
-            onClick={onClick}
-            aria-label={_t("voip|voice_call")}
-            title={tooltip ?? _t("voip|voice_call")}
-            placement="bottom"
-            disabled={disabled || busy}
-        />
-    );
-};
-
-interface VideoCallButtonProps {
-    room: Room;
-    busy: boolean;
-    setBusy: (value: boolean) => void;
-    behavior: DisabledWithReason | "legacy_or_jitsi" | "element" | "jitsi_or_element" | "legacy_or_element";
-}
-
-/**
- * Button for starting video calls, supporting both legacy 1:1 calls, Jitsi
- * widgets, and native group calls. If multiple calling options are available,
- * this shows a menu to pick between them.
- */
-const VideoCallButton: FC<VideoCallButtonProps> = ({ room, busy, setBusy, behavior }) => {
-    const [menuOpen, buttonRef, openMenu, closeMenu] = useContextMenu();
-
-    const startLegacyCall = useCallback(async (): Promise<void> => {
-        setBusy(true);
-        await LegacyCallHandler.instance.placeCall(room.roomId, CallType.Video);
-        setBusy(false);
-    }, [setBusy, room]);
-
-    const startElementCall = useCallback(
-        (skipLobby: boolean) => {
-            setBusy(true);
-            defaultDispatcher.dispatch<ViewRoomPayload>({
-                action: Action.ViewRoom,
-                room_id: room.roomId,
-                view_call: true,
-                skipLobby: skipLobby,
-                metricsTrigger: undefined,
-            });
-            setBusy(false);
-        },
-        [setBusy, room],
-    );
-
-    const { onClick, tooltip, disabled } = useMemo(() => {
-        if (behavior instanceof DisabledWithReason) {
-            return {
-                onClick: () => {},
-                tooltip: behavior.reason,
-                disabled: true,
-            };
-        } else if (behavior === "legacy_or_jitsi") {
-            return {
-                onClick: async (ev: ButtonEvent): Promise<void> => {
-                    ev.preventDefault();
-                    await startLegacyCall();
-                },
-                disabled: false,
-            };
-        } else if (behavior === "element") {
-            return {
-                onClick: async (ev: ButtonEvent): Promise<void> => {
-                    ev.preventDefault();
-                    startElementCall("shiftKey" in ev ? ev.shiftKey : false);
-                },
-                disabled: false,
-            };
-        } else {
-            // behavior === "jitsi_or_element" | "legacy_or_element"
-            return {
-                onClick: async (ev: ButtonEvent): Promise<void> => {
-                    ev.preventDefault();
-                    openMenu();
-                },
-                disabled: false,
-            };
-        }
-    }, [behavior, startLegacyCall, startElementCall, openMenu]);
-
-    const onJitsiClick = useCallback(
-        async (ev: ButtonEvent): Promise<void> => {
-            ev.preventDefault();
-            closeMenu();
-            await startLegacyCall();
-        },
-        [closeMenu, startLegacyCall],
-    );
-
-    const onElementClick = useCallback(
-        (ev: ButtonEvent) => {
-            ev.preventDefault();
-            closeMenu();
-            startElementCall("shiftKey" in ev ? ev.shiftKey : false);
-        },
-        [closeMenu, startElementCall],
-    );
-
-    let menu: JSX.Element | null = null;
-    if (menuOpen) {
-        const buttonRect = buttonRef.current!.getBoundingClientRect();
-        const brand = SdkConfig.get("element_call").brand;
-        menu = (
-            <IconizedContextMenu {...aboveLeftOf(buttonRect)} onFinished={closeMenu}>
-                <IconizedContextMenuOptionList>
-                    <IconizedContextMenuOption
-                        label={
-                            behavior == "legacy_or_element"
-                                ? _t("room|header|video_call_button_legacy")
-                                : _t("room|header|video_call_button_jitsi")
-                        }
-                        onClick={onJitsiClick}
-                    />
-                    <IconizedContextMenuOption
-                        label={_t("room|header|video_call_button_ec", { brand })}
-                        onClick={onElementClick}
-                    />
-                </IconizedContextMenuOptionList>
-            </IconizedContextMenu>
-        );
-    }
-
-    return (
-        <>
-            <AccessibleButton
-                ref={buttonRef}
-                className="mx_LegacyRoomHeader_button mx_LegacyRoomHeader_videoCallButton"
-                onClick={onClick}
-                aria-label={_t("voip|video_call")}
-                title={tooltip ?? _t("voip|video_call")}
-                placement="bottom"
-                disabled={disabled || busy}
-            />
-            {menu}
-        </>
-    );
-};
-
-interface CallButtonsProps {
-    room: Room;
-}
-
-// The header buttons for placing calls have become stupidly complex, so here
-// they are as a separate component
-const CallButtons: FC<CallButtonsProps> = ({ room }) => {
-    const [busy, setBusy] = useState(false);
-    const showButtons = useSettingValue<boolean>("showCallButtonsInComposer");
-    const groupCallsEnabled = useFeatureEnabled("feature_group_calls");
-    const isVideoRoom = useMemo(() => calcIsVideoRoom(room), [room]);
-    const useElementCallExclusively = useMemo(() => {
-        return SdkConfig.get("element_call").use_exclusively;
-    }, []);
-
-    const hasLegacyCall = useEventEmitterState(
-        LegacyCallHandler.instance,
-        LegacyCallHandlerEvent.CallsChanged,
-        useCallback(() => LegacyCallHandler.instance.getCallForRoom(room.roomId) !== null, [room]),
-    );
-
-    const widgets = useWidgets(room);
-    const hasJitsiWidget = useMemo(() => widgets.some((widget) => WidgetType.JITSI.matches(widget.type)), [widgets]);
-
-    const hasGroupCall = useCall(room.roomId) !== null;
-
-    const [functionalMembers, mayEditWidgets, mayCreateElementCalls] = useTypedEventEmitterState(
-        room,
-        RoomStateEvent.Update,
-        useCallback(
-            () => [
-                getJoinedNonFunctionalMembers(room),
-                room.currentState.mayClientSendStateEvent("im.vector.modular.widgets", room.client),
-                room.currentState.mayClientSendStateEvent(ElementCall.CALL_EVENT_TYPE.name, room.client),
-            ],
-            [room],
-        ),
-    );
-
-    const makeVoiceCallButton = (behavior: VoiceCallButtonProps["behavior"]): JSX.Element => (
-        <VoiceCallButton room={room} busy={busy} setBusy={setBusy} behavior={behavior} />
-    );
-    const makeVideoCallButton = (behavior: VideoCallButtonProps["behavior"]): JSX.Element => (
-        <VideoCallButton room={room} busy={busy} setBusy={setBusy} behavior={behavior} />
-    );
-
-    if (isVideoRoom || !showButtons) {
-        return null;
-    } else if (groupCallsEnabled && 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(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 {
-        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(videoCallBehavior)}
-            </>
-        );
-    }
-};
-
-interface CallLayoutSelectorProps {
-    call: ElementCall;
-}
-
-const CallLayoutSelector: FC<CallLayoutSelectorProps> = ({ call }) => {
-    const layout = useLayout(call);
-    const [menuOpen, buttonRef, openMenu, closeMenu] = useContextMenu();
-
-    const onClick = useCallback(
-        (ev: ButtonEvent) => {
-            ev.preventDefault();
-            openMenu();
-        },
-        [openMenu],
-    );
-
-    const onFreedomClick = useCallback(
-        (ev: ButtonEvent) => {
-            ev.preventDefault();
-            closeMenu();
-            call.setLayout(Layout.Tile);
-        },
-        [closeMenu, call],
-    );
-
-    const onSpotlightClick = useCallback(
-        (ev: ButtonEvent) => {
-            ev.preventDefault();
-            closeMenu();
-            call.setLayout(Layout.Spotlight);
-        },
-        [closeMenu, call],
-    );
-
-    let menu: JSX.Element | null = null;
-    if (menuOpen) {
-        const buttonRect = buttonRef.current!.getBoundingClientRect();
-        menu = (
-            <IconizedContextMenu
-                className="mx_LegacyRoomHeader_layoutMenu"
-                {...aboveLeftOf(buttonRect)}
-                onFinished={closeMenu}
-            >
-                <IconizedContextMenuOptionList>
-                    <IconizedContextMenuRadio
-                        iconClassName="mx_LegacyRoomHeader_freedomIcon"
-                        label={_t("room|header|video_call_ec_layout_freedom")}
-                        active={layout === Layout.Tile}
-                        onClick={onFreedomClick}
-                    />
-                    <IconizedContextMenuRadio
-                        iconClassName="mx_LegacyRoomHeader_spotlightIcon"
-                        label={_t("room|header|video_call_ec_layout_spotlight")}
-                        active={layout === Layout.Spotlight}
-                        onClick={onSpotlightClick}
-                    />
-                </IconizedContextMenuOptionList>
-            </IconizedContextMenu>
-        );
-    }
-
-    return (
-        <>
-            <AccessibleButton
-                ref={buttonRef}
-                className={classNames("mx_LegacyRoomHeader_button", {
-                    "mx_LegacyRoomHeader_layoutButton--freedom": layout === Layout.Tile,
-                    "mx_LegacyRoomHeader_layoutButton--spotlight": layout === Layout.Spotlight,
-                })}
-                onClick={onClick}
-                title={_t("room|header|video_call_ec_change_layout")}
-                placement="bottom"
-                key="layout"
-            />
-            {menu}
-        </>
-    );
-};
-
-export interface IProps {
-    room: Room;
-    oobData?: IOOBData;
-    inRoom: boolean;
-    onSearchClick: (() => void) | null;
-    onInviteClick: (() => void) | null;
-    onForgetClick: (() => void) | null;
-    onAppsClick: (() => void) | null;
-    e2eStatus: E2EStatus;
-    appsShown: boolean;
-    searchInfo?: SearchInfo;
-    excludedRightPanelPhaseButtons?: Array<RightPanelPhases>;
-    showButtons?: boolean;
-    enableRoomOptionsMenu?: boolean;
-    viewingCall: boolean;
-    activeCall: Call | null;
-    additionalButtons?: ViewRoomOpts["buttons"];
-}
-
-interface IState {
-    contextMenuPosition?: DOMRect;
-    rightPanelOpen: boolean;
-    featureAskToJoin: boolean;
-}
-
-/**
- * @deprecated use `src/components/views/rooms/RoomHeader.tsx` instead
- */
-export default class RoomHeader extends React.Component<IProps, IState> {
-    public static defaultProps: Partial<IProps> = {
-        inRoom: false,
-        excludedRightPanelPhaseButtons: [],
-        showButtons: true,
-        enableRoomOptionsMenu: true,
-    };
-
-    public static contextType = RoomContext;
-    public declare context: React.ContextType<typeof RoomContext>;
-    private readonly client = this.props.room.client;
-    private readonly featureAskToJoinWatcher: string;
-
-    public constructor(props: IProps, context: React.ContextType<typeof RoomContext>) {
-        super(props, context);
-        const notiStore = RoomNotificationStateStore.instance.getRoomState(props.room);
-        notiStore.on(NotificationStateEvents.Update, this.onNotificationUpdate);
-        this.state = {
-            rightPanelOpen: RightPanelStore.instance.isOpen,
-            featureAskToJoin: SettingsStore.getValue("feature_ask_to_join"),
-        };
-        this.featureAskToJoinWatcher = SettingsStore.watchSetting(
-            "feature_ask_to_join",
-            null,
-            (_settingName, _roomId, _atLevel, _newValAtLevel, featureAskToJoin) => {
-                this.setState({ featureAskToJoin });
-            },
-        );
-    }
-
-    public componentDidMount(): void {
-        this.client.on(RoomStateEvent.Events, this.onRoomStateEvents);
-        RightPanelStore.instance.on(UPDATE_EVENT, this.onRightPanelStoreUpdate);
-    }
-
-    public componentWillUnmount(): void {
-        this.client.removeListener(RoomStateEvent.Events, this.onRoomStateEvents);
-        const notiStore = RoomNotificationStateStore.instance.getRoomState(this.props.room);
-        notiStore.removeListener(NotificationStateEvents.Update, this.onNotificationUpdate);
-        RightPanelStore.instance.off(UPDATE_EVENT, this.onRightPanelStoreUpdate);
-        SettingsStore.unwatchSetting(this.featureAskToJoinWatcher);
-    }
-
-    private onRightPanelStoreUpdate = (): void => {
-        this.setState({ rightPanelOpen: RightPanelStore.instance.isOpen });
-    };
-
-    private onRoomStateEvents = (event: MatrixEvent): void => {
-        if (!this.props.room || event.getRoomId() !== this.props.room.roomId) {
-            return;
-        }
-
-        // redisplay the room name, topic, etc.
-        this.rateLimitedUpdate();
-    };
-
-    private onNotificationUpdate = (): void => {
-        this.forceUpdate();
-    };
-
-    private rateLimitedUpdate = throttle(
-        () => {
-            this.forceUpdate();
-        },
-        500,
-        { leading: true, trailing: true },
-    );
-
-    private onContextMenuOpenClick = (ev: ButtonEvent): void => {
-        ev.preventDefault();
-        ev.stopPropagation();
-        const target = ev.target as HTMLButtonElement;
-        this.setState({ contextMenuPosition: target.getBoundingClientRect() });
-    };
-
-    private onContextMenuCloseClick = (): void => {
-        this.setState({ contextMenuPosition: undefined });
-    };
-
-    private onHideCallClick = (ev: ButtonEvent): void => {
-        ev.preventDefault();
-        defaultDispatcher.dispatch<ViewRoomPayload>({
-            action: Action.ViewRoom,
-            room_id: this.props.room.roomId,
-            view_call: false,
-            metricsTrigger: undefined,
-        });
-    };
-
-    private renderButtons(isVideoRoom: boolean): React.ReactNode {
-        const startButtons: JSX.Element[] = [];
-
-        if (!this.props.viewingCall && this.props.inRoom && !this.context.tombstone) {
-            startButtons.push(<CallButtons key="calls" room={this.props.room} />);
-        }
-
-        if (this.props.viewingCall && this.props.activeCall instanceof ElementCall) {
-            startButtons.push(<CallLayoutSelector key="layout" call={this.props.activeCall} />);
-        }
-
-        if (!this.props.viewingCall && this.props.onForgetClick) {
-            startButtons.push(
-                <AccessibleButton
-                    className="mx_LegacyRoomHeader_button mx_LegacyRoomHeader_forgetButton"
-                    onClick={this.props.onForgetClick}
-                    title={_t("room|header|forget_room_button")}
-                    placement="bottom"
-                    key="forget"
-                />,
-            );
-        }
-
-        if (!this.props.viewingCall && this.props.onAppsClick) {
-            startButtons.push(
-                <AccessibleButton
-                    className={classNames("mx_LegacyRoomHeader_button mx_LegacyRoomHeader_appsButton", {
-                        mx_LegacyRoomHeader_appsButton_highlight: this.props.appsShown,
-                    })}
-                    onClick={this.props.onAppsClick}
-                    title={
-                        this.props.appsShown
-                            ? _t("room|header|hide_widgets_button")
-                            : _t("room|header|show_widgets_button")
-                    }
-                    aria-checked={this.props.appsShown}
-                    placement="bottom"
-                    key="apps"
-                />,
-            );
-        }
-
-        if (!this.props.viewingCall && this.props.onSearchClick && this.props.inRoom) {
-            startButtons.push(
-                <AccessibleButton
-                    className="mx_LegacyRoomHeader_button mx_LegacyRoomHeader_searchButton"
-                    onClick={this.props.onSearchClick}
-                    title={_t("action|search")}
-                    placement="bottom"
-                    key="search"
-                />,
-            );
-        }
-
-        if (this.props.onInviteClick && (!this.props.viewingCall || isVideoRoom) && this.props.inRoom) {
-            startButtons.push(
-                <AccessibleButton
-                    className="mx_LegacyRoomHeader_button mx_LegacyRoomHeader_inviteButton"
-                    onClick={this.props.onInviteClick}
-                    title={_t("action|invite")}
-                    placement="bottom"
-                    key="invite"
-                />,
-            );
-        }
-
-        const endButtons: JSX.Element[] = [];
-
-        if (this.props.viewingCall && !isVideoRoom) {
-            if (this.props.activeCall === null) {
-                endButtons.push(
-                    <AccessibleButton
-                        className="mx_LegacyRoomHeader_button mx_LegacyRoomHeader_closeButton"
-                        onClick={this.onHideCallClick}
-                        title={_t("room|header|close_call_button")}
-                        key="close"
-                    />,
-                );
-            } else {
-                endButtons.push(
-                    <AccessibleButton
-                        className="mx_LegacyRoomHeader_button mx_LegacyRoomHeader_minimiseButton"
-                        onClick={this.onHideCallClick}
-                        title={_t("room|header|video_room_view_chat_button")}
-                        placement="bottom"
-                        key="minimise"
-                    />,
-                );
-            }
-        }
-
-        return (
-            <>
-                {this.props.additionalButtons?.map((props) => {
-                    const label = props.label();
-
-                    return (
-                        <Tooltip label={label} key={props.id}>
-                            <IconButton
-                                onClick={() => {
-                                    props.onClick();
-                                    this.forceUpdate();
-                                }}
-                                title={label}
-                            >
-                                {typeof props.icon === "function" ? props.icon() : props.icon}
-                            </IconButton>
-                        </Tooltip>
-                    );
-                })}
-                {startButtons}
-                <RoomHeaderButtons
-                    room={this.props.room}
-                    excludedRightPanelPhaseButtons={this.props.excludedRightPanelPhaseButtons}
-                />
-                {endButtons}
-            </>
-        );
-    }
-
-    private renderName(oobName: string): JSX.Element {
-        let contextMenu: JSX.Element | null = null;
-        if (this.state.contextMenuPosition && this.props.room) {
-            contextMenu = (
-                <RoomContextMenu
-                    {...contextMenuBelow(this.state.contextMenuPosition)}
-                    room={this.props.room}
-                    onFinished={this.onContextMenuCloseClick}
-                />
-            );
-        }
-
-        // XXX: this is a bit inefficient - we could just compare room.name for 'Empty room'...
-        let settingsHint = false;
-        const members = this.props.room ? this.props.room.getJoinedMembers() : undefined;
-        if (members) {
-            if (members.length === 1 && members[0].userId === this.client.credentials.userId) {
-                const nameEvent = this.props.room.currentState.getStateEvents("m.room.name", "");
-                if (!nameEvent || !nameEvent.getContent().name) {
-                    settingsHint = true;
-                }
-            }
-        }
-
-        const textClasses = classNames("mx_LegacyRoomHeader_nametext", {
-            mx_LegacyRoomHeader_settingsHint: settingsHint,
-        });
-        const roomName = (
-            <RoomName room={this.props.room}>
-                {(name) => {
-                    const roomName = name || oobName;
-                    return (
-                        <div dir="auto" className={textClasses} title={roomName} role="heading" aria-level={1}>
-                            {roomName}
-                        </div>
-                    );
-                }}
-            </RoomName>
-        );
-
-        if (this.props.enableRoomOptionsMenu && shouldShowComponent(UIComponent.RoomOptionsMenu)) {
-            return (
-                <ContextMenuTooltipButton
-                    className="mx_LegacyRoomHeader_name"
-                    onClick={this.onContextMenuOpenClick}
-                    isExpanded={!!this.state.contextMenuPosition}
-                    title={_t("room|context_menu|title")}
-                    placement="bottom"
-                >
-                    {roomName}
-                    {this.props.room && <div className="mx_LegacyRoomHeader_chevron" />}
-                    {contextMenu}
-                </ContextMenuTooltipButton>
-            );
-        }
-
-        return <div className="mx_LegacyRoomHeader_name mx_LegacyRoomHeader_name--textonly">{roomName}</div>;
-    }
-
-    public render(): React.ReactNode {
-        const isVideoRoom = calcIsVideoRoom(this.props.room);
-
-        let roomAvatar: JSX.Element | null = null;
-        if (this.props.room) {
-            roomAvatar = (
-                <DecoratedRoomAvatar
-                    room={this.props.room}
-                    size="24px"
-                    oobData={this.props.oobData}
-                    viewAvatarOnClick={true}
-                />
-            );
-        }
-
-        const icon = this.props.viewingCall ? (
-            <div className="mx_LegacyRoomHeader_icon mx_LegacyRoomHeader_icon_video" />
-        ) : this.props.e2eStatus ? (
-            <E2EIcon className="mx_LegacyRoomHeader_icon" status={this.props.e2eStatus} tooltipPlacement="bottom" />
-        ) : // If we're expecting an E2EE status to come in, but it hasn't
-        // yet been loaded, insert a blank div to reserve space
-        this.client.isRoomEncrypted(this.props.room.roomId) && this.client.isCryptoEnabled() ? (
-            <div className="mx_LegacyRoomHeader_icon" />
-        ) : null;
-
-        const buttons = this.props.showButtons ? this.renderButtons(isVideoRoom) : null;
-
-        let oobName = _t("common|unnamed_room");
-        if (this.props.oobData && this.props.oobData.name) {
-            oobName = this.props.oobData.name;
-        }
-
-        const name = this.renderName(oobName);
-
-        if (this.props.viewingCall && !isVideoRoom) {
-            return (
-                <header className="mx_LegacyRoomHeader light-panel">
-                    <div
-                        className="mx_LegacyRoomHeader_wrapper"
-                        aria-owns={this.state.rightPanelOpen ? "mx_RightPanel" : undefined}
-                    >
-                        <div className="mx_LegacyRoomHeader_avatar">{roomAvatar}</div>
-                        {icon}
-                        {name}
-                        {this.props.activeCall instanceof ElementCall && (
-                            <SessionDuration session={this.props.activeCall?.session} />
-                        )}
-                        {/* Empty topic element to fill out space */}
-                        <div className="mx_LegacyRoomHeader_topic" />
-                        {buttons}
-                    </div>
-                </header>
-            );
-        }
-
-        let searchStatus: JSX.Element | null = null;
-
-        // don't display the search count until the search completes and
-        // gives us a valid (possibly zero) searchCount.
-        if (typeof this.props.searchInfo?.count === "number") {
-            searchStatus = (
-                <div className="mx_LegacyRoomHeader_searchStatus">
-                    &nbsp;
-                    {_t("room|search|result_count", { count: this.props.searchInfo.count })}
-                </div>
-            );
-        }
-
-        const topicElement = <RoomTopic room={this.props.room} className="mx_LegacyRoomHeader_topic" />;
-
-        const viewLabs = (): void =>
-            defaultDispatcher.dispatch({
-                action: Action.ViewUserSettings,
-                initialTabId: UserTab.Labs,
-            });
-        const betaPill = isVideoRoom ? (
-            <BetaPill onClick={viewLabs} tooltipTitle={_t("labs|video_rooms_beta")} />
-        ) : null;
-
-        return (
-            <header className="mx_LegacyRoomHeader light-panel">
-                <div
-                    className="mx_LegacyRoomHeader_wrapper"
-                    aria-owns={this.state.rightPanelOpen ? "mx_RightPanel" : undefined}
-                >
-                    <div className="mx_LegacyRoomHeader_avatar">{roomAvatar}</div>
-                    {icon}
-                    {name}
-                    {searchStatus}
-                    {topicElement}
-                    {betaPill}
-                    {buttons}
-                </div>
-                {!isVideoRoom && <RoomCallBanner roomId={this.props.room.roomId} />}
-                <RoomLiveShareWarning roomId={this.props.room.roomId} />
-                {this.state.featureAskToJoin && <RoomKnocksBar room={this.props.room} />}
-            </header>
-        );
-    }
-}
diff --git a/src/hooks/useCall.ts b/src/hooks/useCall.ts
index 1fbe4d65e6..5a913f84ad 100644
--- a/src/hooks/useCall.ts
+++ b/src/hooks/useCall.ts
@@ -9,7 +9,7 @@ Please see LICENSE files in the repository root for full details.
 import { useState, useCallback, useMemo } from "react";
 
 import type { RoomMember } from "matrix-js-sdk/src/matrix";
-import { Call, ConnectionState, ElementCall, Layout, CallEvent } from "../models/Call";
+import { Call, ConnectionState, CallEvent } from "../models/Call";
 import { useTypedEventEmitterState, useEventEmitter } from "./useEventEmitter";
 import { CallStore, CallStoreEvent } from "../stores/CallStore";
 import SdkConfig, { DEFAULTS } from "../SdkConfig";
@@ -81,10 +81,3 @@ export const useJoinCallButtonDisabledTooltip = (call: Call | null): string | nu
     if (isFull) return _t("voip|join_button_tooltip_call_full");
     return null;
 };
-
-export const useLayout = (call: ElementCall): Layout =>
-    useTypedEventEmitterState(
-        call,
-        CallEvent.Layout,
-        useCallback((state) => state ?? call.layout, [call]),
-    );
diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json
index 9b608c9ab3..dc7bf30bbd 100644
--- a/src/i18n/strings/en_EN.json
+++ b/src/i18n/strings/en_EN.json
@@ -1461,9 +1461,6 @@
         "location_share_live_description": "Temporary implementation. Locations persist in room history.",
         "mjolnir": "New ways to ignore people",
         "msc3531_hide_messages_pending_moderation": "Let moderators hide messages pending moderation.",
-        "new_room_decoration_ui": "New room header",
-        "new_room_decoration_ui_beta_caption": "A new look for your rooms with a simpler, cleaner and more accessible room header.",
-        "new_room_decoration_ui_beta_title": "Room header",
         "notification_settings": "New Notification Settings",
         "notification_settings_beta_caption": "Introducing a simpler way to change your notification settings. Customize your %(brand)s, just the way you like.",
         "notification_settings_beta_title": "Notification Settings",
@@ -1558,9 +1555,7 @@
         "error_send_description": "%(brand)s could not send your location. Please try again later.",
         "error_send_title": "We couldn't send your location",
         "error_sharing_live_location": "An error occurred whilst sharing your live location",
-        "error_sharing_live_location_try_again": "An error occurred whilst sharing your live location, please try again",
         "error_stopping_live_location": "An error occurred while stopping your live location",
-        "error_stopping_live_location_try_again": "An error occurred while stopping your live location, please try again",
         "expand_map": "Expand map",
         "failed_generic": "Failed to fetch your location. Please try again later.",
         "failed_load_map": "Unable to load map",
@@ -1590,7 +1585,6 @@
         "share_type_own": "My current location",
         "share_type_pin": "Drop a Pin",
         "share_type_prompt": "What location type do you want to share?",
-        "stop_and_close": "Stop and close",
         "toggle_attribution": "Toggle attribution"
     },
     "member_list": {
@@ -1838,7 +1832,6 @@
     "right_panel": {
         "add_integrations": "Add extensions",
         "add_topic": "Add topic",
-        "export_chat_button": "Export chat",
         "extensions_empty_description": "Select “%(addIntegrations)s” to browse and add extensions to this room",
         "extensions_empty_title": "Boost productivity with more tools, widgets and bots",
         "files_button": "Files",
@@ -1861,7 +1854,6 @@
                 "title": "All new pinned messages"
             },
             "reply_thread": "Reply to a <link>thread message</link>",
-            "title": "Pinned messages",
             "unpin_all": {
                 "button": "Unpin all messages",
                 "content": "Make sure that you really want to remove all pinned messages. This action can’t be undone.",
@@ -1903,8 +1895,7 @@
         },
         "video_room_chat": {
             "title": "Chat"
-        },
-        "widgets_section": "Widgets"
+        }
     },
     "room": {
         "3pid_invite_email_not_found_account": "This invite was sent to %(email)s which is not associated with your account",
@@ -1925,7 +1916,6 @@
             "low_priority": "Low Priority",
             "mark_read": "Mark as read",
             "mark_unread": "Mark as unread",
-            "mentions_only": "Mentions only",
             "notifications_default": "Match default setting",
             "notifications_mute": "Mute room",
             "title": "Room options",
@@ -1968,22 +1958,11 @@
         "forget_room": "Forget this room",
         "forget_space": "Forget this space",
         "header": {
-            "close_call_button": "Close call",
-            "forget_room_button": "Forget room",
-            "hide_widgets_button": "Hide Widgets",
             "n_people_asking_to_join": {
                 "one": "Asking to join",
                 "other": "%(count)s people asking to join"
             },
-            "room_is_public": "This room is public",
-            "show_widgets_button": "Show Widgets",
-            "video_call_button_ec": "Video call (%(brand)s)",
-            "video_call_button_jitsi": "Video call (Jitsi)",
-            "video_call_button_legacy": "Legacy video call",
-            "video_call_ec_change_layout": "Change layout",
-            "video_call_ec_layout_freedom": "Freedom",
-            "video_call_ec_layout_spotlight": "Spotlight",
-            "video_room_view_chat_button": "View chat timeline"
+            "room_is_public": "This room is public"
         },
         "header_avatar_open_settings_label": "Open room settings",
         "header_face_pile_tooltip": "People",
@@ -2077,10 +2056,6 @@
         "search": {
             "all_rooms_button": "Search all rooms",
             "placeholder": "Search messages…",
-            "result_count": {
-                "one": "(~%(count)s result)",
-                "other": "(~%(count)s results)"
-            },
             "summary": {
                 "one": "1 result found for “<query/>”",
                 "other": "%(count)s results found for “<query/>”"
diff --git a/src/settings/Settings.tsx b/src/settings/Settings.tsx
index 76bb109cac..298102332e 100644
--- a/src/settings/Settings.tsx
+++ b/src/settings/Settings.tsx
@@ -578,18 +578,6 @@ export const SETTINGS: { [setting: string]: ISetting } = {
         supportedLevels: LEVELS_DEVICE_ONLY_SETTINGS_WITH_CONFIG_PRIORITISED,
         supportedLevelsAreOrdered: true,
     },
-    "feature_new_room_decoration_ui": {
-        isFeature: true,
-        labsGroup: LabGroup.Rooms,
-        displayName: _td("labs|new_room_decoration_ui"),
-        supportedLevels: LEVELS_DEVICE_ONLY_SETTINGS_WITH_CONFIG,
-        default: true,
-        controller: new ReloadOnChangeController(),
-        betaInfo: {
-            title: _td("labs|new_room_decoration_ui_beta_title"),
-            caption: () => <p>{_t("labs|new_room_decoration_ui_beta_caption")}</p>,
-        },
-    },
     "feature_notifications": {
         isFeature: true,
         labsGroup: LabGroup.Messaging,
diff --git a/test/components/structures/__snapshots__/RoomView-test.tsx.snap b/test/components/structures/__snapshots__/RoomView-test.tsx.snap
index 5ee31e2207..25740fbd0b 100644
--- a/test/components/structures/__snapshots__/RoomView-test.tsx.snap
+++ b/test/components/structures/__snapshots__/RoomView-test.tsx.snap
@@ -1100,7 +1100,6 @@ exports[`RoomView should show error view if failed to look up room alias 1`] = `
 <DocumentFragment>
   <div
     class="mx_RoomView"
-    data-room-header="new"
   >
     <div
       class="mx_RoomPreviewBar mx_RoomPreviewBar_RoomNotFound mx_RoomPreviewBar_dialog"
diff --git a/test/components/views/beacon/RoomLiveShareWarning-test.tsx b/test/components/views/beacon/RoomLiveShareWarning-test.tsx
deleted file mode 100644
index e0d248eda1..0000000000
--- a/test/components/views/beacon/RoomLiveShareWarning-test.tsx
+++ /dev/null
@@ -1,420 +0,0 @@
-/*
-Copyright 2024 New Vector Ltd.
-Copyright 2022 The Matrix.org Foundation C.I.C.
-
-SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only
-Please see LICENSE files in the repository root for full details.
-*/
-
-import React from "react";
-import { Room, Beacon, BeaconEvent, getBeaconInfoIdentifier, MatrixEvent } from "matrix-js-sdk/src/matrix";
-import { logger } from "matrix-js-sdk/src/logger";
-import { act, fireEvent, getByTestId, render, screen, waitFor } from "@testing-library/react";
-
-import RoomLiveShareWarning from "../../../../src/components/views/beacon/RoomLiveShareWarning";
-import { OwnBeaconStore, OwnBeaconStoreEvent } from "../../../../src/stores/OwnBeaconStore";
-import {
-    advanceDateAndTime,
-    flushPromisesWithFakeTimers,
-    getMockClientWithEventEmitter,
-    makeBeaconInfoEvent,
-    mockGeolocation,
-    resetAsyncStoreWithClient,
-    setupAsyncStoreWithClient,
-} from "../../../test-utils";
-import defaultDispatcher from "../../../../src/dispatcher/dispatcher";
-import { Action } from "../../../../src/dispatcher/actions";
-
-jest.useFakeTimers();
-describe("<RoomLiveShareWarning />", () => {
-    const aliceId = "@alice:server.org";
-    const room1Id = "$room1:server.org";
-    const room2Id = "$room2:server.org";
-    const room3Id = "$room3:server.org";
-    const mockClient = getMockClientWithEventEmitter({
-        getVisibleRooms: jest.fn().mockReturnValue([]),
-        getUserId: jest.fn().mockReturnValue(aliceId),
-        getSafeUserId: jest.fn().mockReturnValue(aliceId),
-        unstable_setLiveBeacon: jest.fn().mockResolvedValue({ event_id: "1" }),
-        sendEvent: jest.fn(),
-        isGuest: jest.fn().mockReturnValue(false),
-    });
-
-    // 14.03.2022 16:15
-    const now = 1647270879403;
-    const MINUTE_MS = 60000;
-    const HOUR_MS = 3600000;
-    // mock the date so events are stable for snapshots etc
-    jest.spyOn(global.Date, "now").mockReturnValue(now);
-    const room1Beacon1 = makeBeaconInfoEvent(
-        aliceId,
-        room1Id,
-        {
-            isLive: true,
-            timeout: HOUR_MS,
-        },
-        "$0",
-    );
-    const room2Beacon1 = makeBeaconInfoEvent(aliceId, room2Id, { isLive: true, timeout: HOUR_MS }, "$1");
-    const room2Beacon2 = makeBeaconInfoEvent(aliceId, room2Id, { isLive: true, timeout: HOUR_MS * 12 }, "$2");
-    const room3Beacon1 = makeBeaconInfoEvent(aliceId, room3Id, { isLive: true, timeout: HOUR_MS }, "$3");
-
-    // make fresh rooms every time
-    // as we update room state
-    const makeRoomsWithStateEvents = (stateEvents: MatrixEvent[] = []): [Room, Room] => {
-        const room1 = new Room(room1Id, mockClient, aliceId);
-        const room2 = new Room(room2Id, mockClient, aliceId);
-
-        room1.currentState.setStateEvents(stateEvents);
-        room2.currentState.setStateEvents(stateEvents);
-        mockClient.getVisibleRooms.mockReturnValue([room1, room2]);
-
-        return [room1, room2];
-    };
-
-    const makeOwnBeaconStore = async () => {
-        const store = OwnBeaconStore.instance;
-
-        await setupAsyncStoreWithClient(store, mockClient);
-        return store;
-    };
-
-    const defaultProps = {
-        roomId: room1Id,
-    };
-    const getComponent = (props = {}) => {
-        return render(<RoomLiveShareWarning {...defaultProps} {...props} />);
-    };
-
-    const localStorageSpy = jest.spyOn(localStorage.__proto__, "getItem").mockReturnValue(undefined);
-
-    beforeEach(() => {
-        mockGeolocation();
-        jest.spyOn(global.Date, "now").mockReturnValue(now);
-        mockClient.unstable_setLiveBeacon.mockReset().mockResolvedValue({ event_id: "1" });
-
-        // assume all beacons were created on this device
-        localStorageSpy.mockReturnValue(
-            JSON.stringify([room1Beacon1.getId(), room2Beacon1.getId(), room2Beacon2.getId(), room3Beacon1.getId()]),
-        );
-    });
-
-    afterEach(async () => {
-        jest.spyOn(OwnBeaconStore.instance, "beaconHasLocationPublishError").mockRestore();
-        await resetAsyncStoreWithClient(OwnBeaconStore.instance);
-    });
-
-    afterAll(() => {
-        jest.spyOn(global.Date, "now").mockRestore();
-        localStorageSpy.mockRestore();
-        jest.spyOn(defaultDispatcher, "dispatch").mockRestore();
-    });
-
-    it("renders nothing when user has no live beacons at all", async () => {
-        await makeOwnBeaconStore();
-        const { asFragment } = getComponent();
-        expect(asFragment()).toMatchInlineSnapshot(`<DocumentFragment />`);
-    });
-
-    it("renders nothing when user has no live beacons in room", async () => {
-        await act(async () => {
-            await makeRoomsWithStateEvents([room2Beacon1]);
-            await makeOwnBeaconStore();
-        });
-        const { asFragment } = getComponent({ roomId: room1Id });
-        expect(asFragment()).toMatchInlineSnapshot(`<DocumentFragment />`);
-    });
-
-    it("does not render when geolocation is not working", async () => {
-        jest.spyOn(logger, "error").mockImplementation(() => {});
-        // @ts-ignore
-        navigator.geolocation = undefined;
-        await act(async () => {
-            await makeRoomsWithStateEvents([room1Beacon1, room2Beacon1, room2Beacon2]);
-            await makeOwnBeaconStore();
-        });
-        const { asFragment } = getComponent({ roomId: room1Id });
-
-        expect(asFragment()).toMatchInlineSnapshot(`<DocumentFragment />`);
-    });
-    describe("when user has live beacons and geolocation is available", () => {
-        beforeEach(async () => {
-            await act(async () => {
-                await makeRoomsWithStateEvents([room1Beacon1, room2Beacon1, room2Beacon2]);
-                await makeOwnBeaconStore();
-            });
-        });
-
-        it("renders correctly with one live beacon in room", () => {
-            const { asFragment } = getComponent({ roomId: room1Id });
-            // beacons have generated ids that break snapshots
-            // assert on html
-            expect(asFragment()).toMatchSnapshot();
-        });
-
-        it("renders correctly with two live beacons in room", () => {
-            const { asFragment, container } = getComponent({ roomId: room2Id });
-            // beacons have generated ids that break snapshots
-            // assert on html
-            expect(asFragment()).toMatchSnapshot();
-            // later expiry displayed
-            expect(container).toHaveTextContent("12h left");
-        });
-
-        it("removes itself when user stops having live beacons", async () => {
-            const { container } = getComponent({ roomId: room1Id });
-            // started out rendered
-            expect(container.firstChild).toBeTruthy();
-
-            // time travel until room1Beacon1 is expired
-            act(() => {
-                advanceDateAndTime(HOUR_MS + 1);
-            });
-            act(() => {
-                mockClient.emit(BeaconEvent.LivenessChange, false, new Beacon(room1Beacon1));
-            });
-
-            await waitFor(() => expect(container.firstChild).toBeFalsy());
-        });
-
-        it("removes itself when user stops monitoring live position", async () => {
-            const { container } = getComponent({ roomId: room1Id });
-            // started out rendered
-            expect(container.firstChild).toBeTruthy();
-
-            act(() => {
-                // cheat to clear this
-                // @ts-ignore
-                OwnBeaconStore.instance.clearPositionWatch = undefined;
-                OwnBeaconStore.instance.emit(OwnBeaconStoreEvent.MonitoringLivePosition);
-            });
-
-            await waitFor(() => expect(container.firstChild).toBeFalsy());
-        });
-
-        it("renders when user adds a live beacon", async () => {
-            const { container } = getComponent({ roomId: room3Id });
-            // started out not rendered
-            expect(container.firstChild).toBeFalsy();
-            act(() => {
-                mockClient.emit(BeaconEvent.New, room3Beacon1, new Beacon(room3Beacon1));
-            });
-
-            await waitFor(() => expect(container.firstChild).toBeTruthy());
-        });
-
-        it("updates beacon time left periodically", () => {
-            const { container } = getComponent({ roomId: room1Id });
-            expect(container).toHaveTextContent("1h left");
-
-            act(() => {
-                advanceDateAndTime(MINUTE_MS * 25);
-            });
-
-            expect(container).toHaveTextContent("35m left");
-        });
-
-        it("updates beacon time left when beacon updates", () => {
-            const { container } = getComponent({ roomId: room1Id });
-            expect(container).toHaveTextContent("1h left");
-
-            act(() => {
-                const beacon = OwnBeaconStore.instance.getBeaconById(getBeaconInfoIdentifier(room1Beacon1));
-                const room1Beacon1Update = makeBeaconInfoEvent(
-                    aliceId,
-                    room1Id,
-                    {
-                        isLive: true,
-                        timeout: 3 * HOUR_MS,
-                    },
-                    "$0",
-                );
-                beacon?.update(room1Beacon1Update);
-            });
-
-            // update to expiry of new beacon
-            expect(container).toHaveTextContent("3h left");
-        });
-
-        it("clears expiry time interval on unmount", () => {
-            const clearIntervalSpy = jest.spyOn(global, "clearInterval");
-            const { container, unmount } = getComponent({ roomId: room1Id });
-            expect(container).toHaveTextContent("1h left");
-
-            unmount();
-
-            expect(clearIntervalSpy).toHaveBeenCalled();
-        });
-
-        it("navigates to beacon tile on click", () => {
-            const dispatcherSpy = jest.spyOn(defaultDispatcher, "dispatch");
-            const { container } = getComponent({ roomId: room1Id });
-
-            act(() => {
-                fireEvent.click(container.firstChild! as Node);
-            });
-
-            expect(dispatcherSpy).toHaveBeenCalledWith({
-                action: Action.ViewRoom,
-                event_id: room1Beacon1.getId(),
-                room_id: room1Id,
-                highlighted: true,
-                scroll_into_view: true,
-                metricsTrigger: undefined,
-            });
-        });
-
-        describe("stopping beacons", () => {
-            it("stops beacon on stop sharing click", async () => {
-                const { container } = getComponent({ roomId: room2Id });
-
-                const btn = getByTestId(container, "room-live-share-primary-button");
-
-                fireEvent.click(btn);
-
-                expect(mockClient.unstable_setLiveBeacon).toHaveBeenCalled();
-
-                await waitFor(() => expect(screen.queryByTestId("spinner")).toBeInTheDocument());
-
-                expect(btn.hasAttribute("disabled")).toBe(true);
-            });
-
-            it("displays error when stop sharing fails", async () => {
-                const { container, asFragment } = getComponent({ roomId: room1Id });
-                const btn = getByTestId(container, "room-live-share-primary-button");
-
-                // fail first time
-                mockClient.unstable_setLiveBeacon
-                    .mockRejectedValueOnce(new Error("oups"))
-                    .mockResolvedValue({ event_id: "1" });
-
-                await act(async () => {
-                    fireEvent.click(btn);
-                    await flushPromisesWithFakeTimers();
-                });
-
-                expect(asFragment()).toMatchSnapshot();
-
-                act(() => {
-                    fireEvent.click(btn);
-                });
-
-                expect(mockClient.unstable_setLiveBeacon).toHaveBeenCalledTimes(2);
-            });
-
-            it("displays again with correct state after stopping a beacon", () => {
-                // make sure the loading state is reset correctly after removing a beacon
-                const { container } = getComponent({ roomId: room1Id });
-                const btn = getByTestId(container, "room-live-share-primary-button");
-
-                // stop the beacon
-                act(() => {
-                    fireEvent.click(btn);
-                });
-                // time travel until room1Beacon1 is expired
-                act(() => {
-                    advanceDateAndTime(HOUR_MS + 1);
-                });
-                act(() => {
-                    mockClient.emit(BeaconEvent.LivenessChange, false, new Beacon(room1Beacon1));
-                });
-
-                const newLiveBeacon = makeBeaconInfoEvent(aliceId, room1Id, { isLive: true });
-                act(() => {
-                    mockClient.emit(BeaconEvent.New, newLiveBeacon, new Beacon(newLiveBeacon));
-                });
-
-                // button not disabled and expiry time shown
-                expect(btn.hasAttribute("disabled")).toBe(true);
-            });
-        });
-
-        describe("with location publish errors", () => {
-            it("displays location publish error when mounted with location publish errors", async () => {
-                const locationPublishErrorSpy = jest
-                    .spyOn(OwnBeaconStore.instance, "beaconHasLocationPublishError")
-                    .mockReturnValue(true);
-                const { asFragment } = getComponent({ roomId: room2Id });
-
-                expect(asFragment()).toMatchSnapshot();
-                expect(locationPublishErrorSpy).toHaveBeenCalledWith(getBeaconInfoIdentifier(room2Beacon1), 0, [
-                    getBeaconInfoIdentifier(room2Beacon1),
-                ]);
-            });
-
-            it(
-                "displays location publish error when locationPublishError event is emitted" +
-                    " and beacons have errors",
-                async () => {
-                    const locationPublishErrorSpy = jest
-                        .spyOn(OwnBeaconStore.instance, "beaconHasLocationPublishError")
-                        .mockReturnValue(false);
-                    const { container } = getComponent({ roomId: room2Id });
-
-                    // update mock and emit event
-                    act(() => {
-                        locationPublishErrorSpy.mockReturnValue(true);
-                        OwnBeaconStore.instance.emit(
-                            OwnBeaconStoreEvent.LocationPublishError,
-                            getBeaconInfoIdentifier(room2Beacon1),
-                        );
-                    });
-
-                    // renders wire error ui
-                    expect(container).toHaveTextContent(
-                        "An error occurred whilst sharing your live location, please try again",
-                    );
-
-                    expect(screen.queryByTestId("room-live-share-wire-error-close-button")).toBeInTheDocument();
-                },
-            );
-
-            it("stops displaying wire error when errors are cleared", async () => {
-                const locationPublishErrorSpy = jest
-                    .spyOn(OwnBeaconStore.instance, "beaconHasLocationPublishError")
-                    .mockReturnValue(true);
-                const { container } = getComponent({ roomId: room2Id });
-
-                // update mock and emit event
-                act(() => {
-                    locationPublishErrorSpy.mockReturnValue(false);
-                    OwnBeaconStore.instance.emit(
-                        OwnBeaconStoreEvent.LocationPublishError,
-                        getBeaconInfoIdentifier(room2Beacon1),
-                    );
-                });
-
-                // renders error-free ui
-                expect(container).toHaveTextContent("You are sharing your live location");
-                expect(screen.queryByTestId("room-live-share-wire-error-close-button")).not.toBeInTheDocument();
-            });
-
-            it("clicking retry button resets location publish errors", async () => {
-                jest.spyOn(OwnBeaconStore.instance, "beaconHasLocationPublishError").mockReturnValue(true);
-                const resetErrorSpy = jest.spyOn(OwnBeaconStore.instance, "resetLocationPublishError");
-
-                const { container } = getComponent({ roomId: room2Id });
-                const btn = getByTestId(container, "room-live-share-primary-button");
-
-                act(() => {
-                    fireEvent.click(btn);
-                });
-
-                expect(resetErrorSpy).toHaveBeenCalledWith(getBeaconInfoIdentifier(room2Beacon1));
-            });
-
-            it("clicking close button stops beacons", async () => {
-                jest.spyOn(OwnBeaconStore.instance, "beaconHasLocationPublishError").mockReturnValue(true);
-                const stopBeaconSpy = jest.spyOn(OwnBeaconStore.instance, "stopBeacon");
-
-                const { container } = getComponent({ roomId: room2Id });
-                const btn = getByTestId(container, "room-live-share-wire-error-close-button");
-                act(() => {
-                    fireEvent.click(btn);
-                });
-
-                expect(stopBeaconSpy).toHaveBeenCalledWith(getBeaconInfoIdentifier(room2Beacon1));
-            });
-        });
-    });
-});
diff --git a/test/components/views/beacon/__snapshots__/RoomLiveShareWarning-test.tsx.snap b/test/components/views/beacon/__snapshots__/RoomLiveShareWarning-test.tsx.snap
deleted file mode 100644
index c2bb48e33c..0000000000
--- a/test/components/views/beacon/__snapshots__/RoomLiveShareWarning-test.tsx.snap
+++ /dev/null
@@ -1,133 +0,0 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`<RoomLiveShareWarning /> when user has live beacons and geolocation is available renders correctly with one live beacon in room 1`] = `
-<DocumentFragment>
-  <div
-    class="mx_RoomLiveShareWarning"
-  >
-    <div
-      class="mx_StyledLiveBeaconIcon mx_RoomLiveShareWarning_icon"
-    />
-    <span
-      class="mx_RoomLiveShareWarning_label"
-    >
-      You are sharing your live location
-    </span>
-    <span
-      class="mx_LiveTimeRemaining"
-      data-testid="room-live-share-expiry"
-    >
-      1h left
-    </span>
-    <button
-      class="mx_AccessibleButton mx_RoomLiveShareWarning_stopButton mx_AccessibleButton_hasKind mx_AccessibleButton_kind_danger"
-      data-testid="room-live-share-primary-button"
-      role="button"
-      tabindex="0"
-    >
-      Stop
-    </button>
-  </div>
-</DocumentFragment>
-`;
-
-exports[`<RoomLiveShareWarning /> when user has live beacons and geolocation is available renders correctly with two live beacons in room 1`] = `
-<DocumentFragment>
-  <div
-    class="mx_RoomLiveShareWarning"
-  >
-    <div
-      class="mx_StyledLiveBeaconIcon mx_RoomLiveShareWarning_icon"
-    />
-    <span
-      class="mx_RoomLiveShareWarning_label"
-    >
-      You are sharing your live location
-    </span>
-    <span
-      class="mx_LiveTimeRemaining"
-      data-testid="room-live-share-expiry"
-    >
-      12h left
-    </span>
-    <button
-      class="mx_AccessibleButton mx_RoomLiveShareWarning_stopButton mx_AccessibleButton_hasKind mx_AccessibleButton_kind_danger"
-      data-testid="room-live-share-primary-button"
-      role="button"
-      tabindex="0"
-    >
-      Stop
-    </button>
-  </div>
-</DocumentFragment>
-`;
-
-exports[`<RoomLiveShareWarning /> when user has live beacons and geolocation is available stopping beacons displays error when stop sharing fails 1`] = `
-<DocumentFragment>
-  <div
-    class="mx_RoomLiveShareWarning"
-  >
-    <div
-      class="mx_StyledLiveBeaconIcon mx_RoomLiveShareWarning_icon mx_StyledLiveBeaconIcon_error"
-    />
-    <span
-      class="mx_RoomLiveShareWarning_label"
-    >
-      An error occurred while stopping your live location, please try again
-    </span>
-    <button
-      class="mx_AccessibleButton mx_RoomLiveShareWarning_stopButton mx_AccessibleButton_hasKind mx_AccessibleButton_kind_danger"
-      data-testid="room-live-share-primary-button"
-      role="button"
-      tabindex="0"
-    >
-      Retry
-    </button>
-  </div>
-</DocumentFragment>
-`;
-
-exports[`<RoomLiveShareWarning /> when user has live beacons and geolocation is available with location publish errors displays location publish error when mounted with location publish errors 1`] = `
-<DocumentFragment>
-  <div
-    class="mx_RoomLiveShareWarning"
-  >
-    <div
-      class="mx_StyledLiveBeaconIcon mx_RoomLiveShareWarning_icon mx_StyledLiveBeaconIcon_error"
-    />
-    <span
-      class="mx_RoomLiveShareWarning_label"
-    >
-      An error occurred whilst sharing your live location, please try again
-    </span>
-    <button
-      class="mx_AccessibleButton mx_RoomLiveShareWarning_stopButton mx_AccessibleButton_hasKind mx_AccessibleButton_kind_danger"
-      data-testid="room-live-share-primary-button"
-      role="button"
-      tabindex="0"
-    >
-      Retry
-    </button>
-    <button
-      aria-label="Stop and close"
-      class="mx_AccessibleButton mx_RoomLiveShareWarning_closeButton"
-      data-testid="room-live-share-wire-error-close-button"
-      role="button"
-      tabindex="0"
-    >
-      <svg
-        class="mx_RoomLiveShareWarning_closeButtonIcon"
-        fill="currentColor"
-        height="1em"
-        viewBox="0 0 24 24"
-        width="1em"
-        xmlns="http://www.w3.org/2000/svg"
-      >
-        <path
-          d="M6.293 6.293a1 1 0 0 1 1.414 0L12 10.586l4.293-4.293a1 1 0 1 1 1.414 1.414L13.414 12l4.293 4.293a1 1 0 0 1-1.414 1.414L12 13.414l-4.293 4.293a1 1 0 0 1-1.414-1.414L10.586 12 6.293 7.707a1 1 0 0 1 0-1.414Z"
-        />
-      </svg>
-    </button>
-  </div>
-</DocumentFragment>
-`;
diff --git a/test/components/views/context_menus/RoomContextMenu-test.tsx b/test/components/views/context_menus/RoomContextMenu-test.tsx
deleted file mode 100644
index 3644d2c325..0000000000
--- a/test/components/views/context_menus/RoomContextMenu-test.tsx
+++ /dev/null
@@ -1,109 +0,0 @@
-/*
-Copyright 2024 New Vector Ltd.
-Copyright 2023 Mikhail Aheichyk
-Copyright 2023 Nordeck IT + Consulting GmbH.
-Copyright 2023 The Matrix.org Foundation C.I.C.
-
-SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only
-Please see LICENSE files in the repository root for full details.
-*/
-
-import { render, screen } from "@testing-library/react";
-import React, { ComponentProps } from "react";
-import { mocked } from "jest-mock";
-import { MatrixClient, PendingEventOrdering, Room } from "matrix-js-sdk/src/matrix";
-import { KnownMembership } from "matrix-js-sdk/src/types";
-
-import MatrixClientContext from "../../../../src/contexts/MatrixClientContext";
-import RoomContextMenu from "../../../../src/components/views/context_menus/RoomContextMenu";
-import { shouldShowComponent } from "../../../../src/customisations/helpers/UIComponents";
-import { stubClient } from "../../../test-utils";
-import { MatrixClientPeg } from "../../../../src/MatrixClientPeg";
-import DMRoomMap from "../../../../src/utils/DMRoomMap";
-import SettingsStore from "../../../../src/settings/SettingsStore";
-import { EchoChamber } from "../../../../src/stores/local-echo/EchoChamber";
-import { RoomNotifState } from "../../../../src/RoomNotifs";
-
-jest.mock("../../../../src/customisations/helpers/UIComponents", () => ({
-    shouldShowComponent: jest.fn(),
-}));
-
-describe("RoomContextMenu", () => {
-    const ROOM_ID = "!123:matrix.org";
-
-    let room: Room;
-    let mockClient: MatrixClient;
-
-    let onFinished: () => void;
-
-    beforeEach(() => {
-        jest.clearAllMocks();
-
-        stubClient();
-        mockClient = mocked(MatrixClientPeg.safeGet());
-
-        room = new Room(ROOM_ID, mockClient, mockClient.getUserId() ?? "", {
-            pendingEventOrdering: PendingEventOrdering.Detached,
-        });
-
-        const dmRoomMap = {
-            getUserIdForRoomId: jest.fn(),
-        } as unknown as DMRoomMap;
-        DMRoomMap.setShared(dmRoomMap);
-
-        onFinished = jest.fn();
-    });
-
-    function renderComponent(props: Partial<ComponentProps<typeof RoomContextMenu>> = {}) {
-        render(
-            <MatrixClientContext.Provider value={mockClient}>
-                <RoomContextMenu room={room} onFinished={onFinished} {...props} />
-            </MatrixClientContext.Provider>,
-        );
-    }
-
-    it("does not render invite menu item when UIComponent customisations disable invite", () => {
-        jest.spyOn(room, "canInvite").mockReturnValue(true);
-        mocked(shouldShowComponent).mockReturnValue(false);
-
-        renderComponent();
-
-        expect(screen.queryByRole("menuitem", { name: "Invite" })).not.toBeInTheDocument();
-    });
-
-    it("renders invite menu item when UIComponent customisations enable invite", () => {
-        jest.spyOn(room, "canInvite").mockReturnValue(true);
-        mocked(shouldShowComponent).mockReturnValue(true);
-
-        renderComponent();
-
-        expect(screen.getByRole("menuitem", { name: "Invite" })).toBeInTheDocument();
-    });
-
-    it("when developer mode is disabled, it should not render the developer tools option", () => {
-        renderComponent();
-        expect(screen.queryByText("Developer tools")).not.toBeInTheDocument();
-    });
-
-    describe("when developer mode is enabled", () => {
-        beforeEach(() => {
-            jest.spyOn(SettingsStore, "getValue").mockImplementation((setting) => setting === "developerMode");
-        });
-
-        it("should render the developer tools option", () => {
-            renderComponent();
-            expect(screen.getByText("Developer tools")).toBeInTheDocument();
-        });
-    });
-
-    it("should render notification option for joined rooms", () => {
-        const chamber = EchoChamber.forRoom(room);
-        chamber.notificationVolume = RoomNotifState.Mute;
-        jest.spyOn(room, "getMyMembership").mockReturnValue(KnownMembership.Join);
-        renderComponent();
-
-        expect(
-            screen.getByRole("menuitem", { name: "Notifications" }).querySelector(".mx_IconizedContextMenu_sublabel"),
-        ).toHaveTextContent("Mute");
-    });
-});
diff --git a/test/components/views/right_panel/LegacyRoomHeaderButtons-test.tsx b/test/components/views/right_panel/LegacyRoomHeaderButtons-test.tsx
deleted file mode 100644
index 89e9c330d0..0000000000
--- a/test/components/views/right_panel/LegacyRoomHeaderButtons-test.tsx
+++ /dev/null
@@ -1,187 +0,0 @@
-/*
-Copyright 2024 New Vector Ltd.
-Copyright 2022, 2023 The Matrix.org Foundation C.I.C.
-
-SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only
-Please see LICENSE files in the repository root for full details.
-*/
-
-import { render, waitFor } from "@testing-library/react";
-import {
-    MatrixEvent,
-    MsgType,
-    RelationType,
-    NotificationCountType,
-    Room,
-    MatrixClient,
-    PendingEventOrdering,
-    ReceiptType,
-} from "matrix-js-sdk/src/matrix";
-import React from "react";
-
-import LegacyRoomHeaderButtons from "../../../../src/components/views/right_panel/LegacyRoomHeaderButtons";
-import { MatrixClientPeg } from "../../../../src/MatrixClientPeg";
-import { mkEvent, stubClient } from "../../../test-utils";
-import { mkThread } from "../../../test-utils/threads";
-
-describe("LegacyRoomHeaderButtons-test.tsx", function () {
-    const ROOM_ID = "!roomId:example.org";
-    let room: Room;
-    let client: MatrixClient;
-
-    beforeEach(() => {
-        jest.clearAllMocks();
-
-        stubClient();
-        client = MatrixClientPeg.safeGet();
-        client.supportsThreads = () => true;
-        room = new Room(ROOM_ID, client, client.getUserId() ?? "", {
-            pendingEventOrdering: PendingEventOrdering.Detached,
-        });
-    });
-
-    function getComponent(room?: Room) {
-        return render(<LegacyRoomHeaderButtons room={room} excludedRightPanelPhaseButtons={[]} />);
-    }
-
-    function getThreadButton(container: HTMLElement) {
-        return container.querySelector(".mx_RightPanel_threadsButton");
-    }
-
-    function isIndicatorOfType(container: HTMLElement, type: "highlight" | "notification" | "activity") {
-        return container.querySelector(".mx_RightPanel_threadsButton .mx_Indicator")!.className.includes(type);
-    }
-
-    it("should render", () => {
-        const { asFragment } = getComponent(room);
-        expect(asFragment()).toMatchSnapshot();
-    });
-
-    it("shows the thread button", () => {
-        const { container } = getComponent(room);
-        expect(getThreadButton(container)).not.toBeNull();
-    });
-
-    it("room wide notification does not change the thread button", () => {
-        room.setUnreadNotificationCount(NotificationCountType.Highlight, 1);
-        room.setUnreadNotificationCount(NotificationCountType.Total, 1);
-
-        const { container } = getComponent(room);
-
-        expect(container.querySelector(".mx_RightPanel_threadsButton .mx_Indicator")).toBeNull();
-    });
-
-    it("thread notification does change the thread button", async () => {
-        const { container } = getComponent(room);
-        expect(getThreadButton(container)!.className.includes("mx_LegacyRoomHeader_button--unread")).toBeFalsy();
-
-        room.setThreadUnreadNotificationCount("$123", NotificationCountType.Total, 1);
-        await waitFor(() => {
-            expect(getThreadButton(container)!.className.includes("mx_LegacyRoomHeader_button--unread")).toBeTruthy();
-            expect(isIndicatorOfType(container, "notification")).toBe(true);
-        });
-
-        room.setThreadUnreadNotificationCount("$123", NotificationCountType.Highlight, 1);
-        await waitFor(() => expect(isIndicatorOfType(container, "highlight")).toBe(true));
-
-        room.setThreadUnreadNotificationCount("$123", NotificationCountType.Total, 0);
-        room.setThreadUnreadNotificationCount("$123", NotificationCountType.Highlight, 0);
-
-        await waitFor(() => expect(container.querySelector(".mx_RightPanel_threadsButton .mx_Indicator")).toBeNull());
-    });
-
-    it("thread activity does change the thread button", async () => {
-        const { container } = getComponent(room);
-
-        // Thread activity should appear on the icon.
-        const { rootEvent, events } = mkThread({
-            room,
-            client,
-            authorId: client.getUserId()!,
-            participantUserIds: ["@alice:example.org"],
-            length: 5,
-        });
-        // We need some receipt, otherwise we treat this thread as
-        // "older than all threaded receipts" and consider it read.
-        let receipt = new MatrixEvent({
-            type: "m.receipt",
-            room_id: room.roomId,
-            content: {
-                [events[1].getId()!]: {
-                    // Receipt for the first event in the thread
-                    [ReceiptType.Read]: {
-                        [client.getUserId()!]: { ts: 1, thread_id: rootEvent.getId() },
-                    },
-                },
-            },
-        });
-        room.addReceipt(receipt);
-        await waitFor(() => expect(isIndicatorOfType(container, "activity")).toBe(true));
-
-        // Sending the last event should clear the notification.
-        let event = mkEvent({
-            event: true,
-            type: "m.room.message",
-            user: client.getUserId()!,
-            room: room.roomId,
-            content: {
-                "msgtype": MsgType.Text,
-                "body": "Test",
-                "m.relates_to": {
-                    event_id: rootEvent.getId(),
-                    rel_type: RelationType.Thread,
-                },
-            },
-        });
-        room.addLiveEvents([event]);
-        await waitFor(() => expect(container.querySelector(".mx_RightPanel_threadsButton .mx_Indicator")).toBeNull());
-
-        // Mark it as unread again.
-        event = mkEvent({
-            event: true,
-            type: "m.room.message",
-            user: "@alice:example.org",
-            room: room.roomId,
-            content: {
-                "msgtype": MsgType.Text,
-                "body": "Test",
-                "m.relates_to": {
-                    event_id: rootEvent.getId(),
-                    rel_type: RelationType.Thread,
-                },
-            },
-        });
-        room.addLiveEvents([event]);
-        await waitFor(() => expect(isIndicatorOfType(container, "activity")).toBe(true));
-
-        // Sending a read receipt on an earlier event shouldn't do anything.
-        receipt = new MatrixEvent({
-            type: "m.receipt",
-            room_id: room.roomId,
-            content: {
-                [events.at(-1)!.getId()!]: {
-                    [ReceiptType.Read]: {
-                        [client.getUserId()!]: { ts: 1, thread_id: rootEvent.getId() },
-                    },
-                },
-            },
-        });
-        room.addReceipt(receipt);
-        await waitFor(() => expect(isIndicatorOfType(container, "activity")).toBe(true));
-
-        // Sending a receipt on the latest event should clear the notification.
-        receipt = new MatrixEvent({
-            type: "m.receipt",
-            room_id: room.roomId,
-            content: {
-                [event.getId()!]: {
-                    [ReceiptType.Read]: {
-                        [client.getUserId()!]: { ts: 1, thread_id: rootEvent.getId() },
-                    },
-                },
-            },
-        });
-        room.addReceipt(receipt);
-        await waitFor(() => expect(container.querySelector(".mx_RightPanel_threadsButton .mx_Indicator")).toBeNull());
-    });
-});
diff --git a/test/components/views/right_panel/__snapshots__/LegacyRoomHeaderButtons-test.tsx.snap b/test/components/views/right_panel/__snapshots__/LegacyRoomHeaderButtons-test.tsx.snap
deleted file mode 100644
index 6dbe9ed2e3..0000000000
--- a/test/components/views/right_panel/__snapshots__/LegacyRoomHeaderButtons-test.tsx.snap
+++ /dev/null
@@ -1,28 +0,0 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`LegacyRoomHeaderButtons-test.tsx should render 1`] = `
-<DocumentFragment>
-  <div
-    aria-current="false"
-    aria-label="Chat"
-    class="mx_AccessibleButton mx_LegacyRoomHeader_button mx_RightPanel_timelineCardButton"
-    role="button"
-    tabindex="0"
-  />
-  <div
-    aria-current="false"
-    aria-label="Threads"
-    class="mx_AccessibleButton mx_LegacyRoomHeader_button mx_RightPanel_threadsButton"
-    data-testid="threadsButton"
-    role="button"
-    tabindex="0"
-  />
-  <div
-    aria-current="false"
-    aria-label="Room info"
-    class="mx_AccessibleButton mx_LegacyRoomHeader_button mx_RightPanel_roomSummaryButton"
-    role="button"
-    tabindex="0"
-  />
-</DocumentFragment>
-`;
diff --git a/test/components/views/rooms/LegacyRoomHeader-test.tsx b/test/components/views/rooms/LegacyRoomHeader-test.tsx
deleted file mode 100644
index 16dbefbab0..0000000000
--- a/test/components/views/rooms/LegacyRoomHeader-test.tsx
+++ /dev/null
@@ -1,917 +0,0 @@
-/*
-Copyright 2024 New Vector Ltd.
-Copyright 2022 The Matrix.org Foundation C.I.C.
-
-SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only
-Please see LICENSE files in the repository root for full details.
-*/
-
-import React from "react";
-import { render, screen, act, fireEvent, waitFor, getByRole, RenderResult } from "@testing-library/react";
-import { mocked, Mocked } from "jest-mock";
-import {
-    EventType,
-    RoomType,
-    Room,
-    RoomStateEvent,
-    PendingEventOrdering,
-    ISearchResults,
-    IContent,
-} from "matrix-js-sdk/src/matrix";
-import { KnownMembership } from "matrix-js-sdk/src/types";
-import { CallType } from "matrix-js-sdk/src/webrtc/call";
-import { ClientWidgetApi, Widget } from "matrix-widget-api";
-import EventEmitter from "events";
-import { setupJestCanvasMock } from "jest-canvas-mock";
-import { ViewRoomOpts } from "@matrix-org/react-sdk-module-api/lib/lifecycles/RoomViewLifecycle";
-import { MatrixRTCSession, MatrixRTCSessionManagerEvents } from "matrix-js-sdk/src/matrixrtc";
-
-import type { MatrixClient, MatrixEvent, RoomMember } from "matrix-js-sdk/src/matrix";
-import type { MatrixCall } from "matrix-js-sdk/src/webrtc/call";
-import {
-    stubClient,
-    mkRoomMember,
-    setupAsyncStoreWithClient,
-    resetAsyncStoreWithClient,
-    mockPlatformPeg,
-    mkEvent,
-    filterConsole,
-} from "../../../test-utils";
-import { MatrixClientPeg } from "../../../../src/MatrixClientPeg";
-import DMRoomMap from "../../../../src/utils/DMRoomMap";
-import RoomHeader, { IProps as RoomHeaderProps } from "../../../../src/components/views/rooms/LegacyRoomHeader";
-import { E2EStatus } from "../../../../src/utils/ShieldUtils";
-import { IRoomState } from "../../../../src/components/structures/RoomView";
-import RoomContext from "../../../../src/contexts/RoomContext";
-import SdkConfig from "../../../../src/SdkConfig";
-import SettingsStore from "../../../../src/settings/SettingsStore";
-import { ElementCall, JitsiCall } from "../../../../src/models/Call";
-import { CallStore } from "../../../../src/stores/CallStore";
-import LegacyCallHandler from "../../../../src/LegacyCallHandler";
-import defaultDispatcher from "../../../../src/dispatcher/dispatcher";
-import { Action } from "../../../../src/dispatcher/actions";
-import WidgetStore from "../../../../src/stores/WidgetStore";
-import { WidgetMessagingStore } from "../../../../src/stores/widgets/WidgetMessagingStore";
-import MediaDeviceHandler, { MediaDeviceKindEnum } from "../../../../src/MediaDeviceHandler";
-import { shouldShowComponent } from "../../../../src/customisations/helpers/UIComponents";
-import { UIComponent } from "../../../../src/settings/UIFeature";
-import WidgetUtils from "../../../../src/utils/WidgetUtils";
-import { ElementWidgetActions } from "../../../../src/stores/widgets/ElementWidgetActions";
-import { SearchScope } from "../../../../src/Searching";
-
-jest.mock("../../../../src/customisations/helpers/UIComponents", () => ({
-    shouldShowComponent: jest.fn(),
-}));
-
-describe("LegacyRoomHeader", () => {
-    let client: Mocked<MatrixClient>;
-    let room: Room;
-    let alice: RoomMember;
-    let bob: RoomMember;
-    let carol: RoomMember;
-
-    filterConsole(
-        "Age for event was not available, using `now - origin_server_ts` as a fallback. If the device clock is not correct issues might occur.",
-    );
-
-    beforeEach(async () => {
-        // some of our tests rely on the jest canvas mock, and `afterEach` will have reset the mock, so we need to
-        // restore it.
-        setupJestCanvasMock();
-
-        mockPlatformPeg({ supportsJitsiScreensharing: () => true });
-
-        stubClient();
-        client = mocked(MatrixClientPeg.safeGet());
-        client.getUserId.mockReturnValue("@alice:example.org");
-
-        room = new Room("!1:example.org", client, "@alice:example.org", {
-            pendingEventOrdering: PendingEventOrdering.Detached,
-        });
-        room.currentState.setStateEvents([mkCreationEvent(room.roomId, "@alice:example.org")]);
-
-        client.getRoom.mockImplementation((roomId) => (roomId === room.roomId ? room : null));
-        client.getRooms.mockReturnValue([room]);
-        client.reEmitter.reEmit(room, [RoomStateEvent.Events]);
-        client.sendStateEvent.mockImplementation(async (roomId, eventType, content, stateKey = "") => {
-            if (roomId !== room.roomId) throw new Error("Unknown room");
-            const event = mkEvent({
-                event: true,
-                type: eventType,
-                room: roomId,
-                user: alice.userId,
-                skey: stateKey,
-                content: content as IContent,
-            });
-            room.addLiveEvents([event]);
-            return { event_id: event.getId()! };
-        });
-
-        alice = mkRoomMember(room.roomId, "@alice:example.org");
-        bob = mkRoomMember(room.roomId, "@bob:example.org");
-        carol = mkRoomMember(room.roomId, "@carol:example.org");
-
-        client.getRoom.mockImplementation((roomId) => (roomId === room.roomId ? room : null));
-        client.getRooms.mockReturnValue([room]);
-        client.reEmitter.reEmit(room, [RoomStateEvent.Events]);
-
-        await Promise.all(
-            [CallStore.instance, WidgetStore.instance].map((store) => setupAsyncStoreWithClient(store, client)),
-        );
-
-        jest.spyOn(MediaDeviceHandler, "getDevices").mockResolvedValue({
-            [MediaDeviceKindEnum.AudioInput]: [],
-            [MediaDeviceKindEnum.VideoInput]: [],
-            [MediaDeviceKindEnum.AudioOutput]: [],
-        });
-
-        DMRoomMap.makeShared(client);
-        jest.spyOn(DMRoomMap.shared(), "getUserIdForRoomId").mockReturnValue(carol.userId);
-    });
-
-    afterEach(async () => {
-        await Promise.all([CallStore.instance, WidgetStore.instance].map(resetAsyncStoreWithClient));
-        client.reEmitter.stopReEmitting(room, [RoomStateEvent.Events]);
-        jest.restoreAllMocks();
-        SdkConfig.reset();
-    });
-
-    const mockRoomType = (type: string) => {
-        jest.spyOn(room, "getType").mockReturnValue(type);
-    };
-    const mockRoomMembers = (members: RoomMember[]) => {
-        jest.spyOn(room, "getJoinedMembers").mockReturnValue(members);
-        jest.spyOn(room, "getMember").mockImplementation(
-            (userId) => members.find((member) => member.userId === userId) ?? null,
-        );
-    };
-    const mockEnabledSettings = (settings: string[]) => {
-        jest.spyOn(SettingsStore, "getValue").mockImplementation((settingName) => settings.includes(settingName));
-    };
-    const mockEventPowerLevels = (events: { [eventType: string]: number }) => {
-        room.currentState.setStateEvents([
-            mkEvent({
-                event: true,
-                type: EventType.RoomPowerLevels,
-                room: room.roomId,
-                user: alice.userId,
-                skey: "",
-                content: { events, state_default: 0 },
-            }),
-        ]);
-    };
-    const mockLegacyCall = () => {
-        jest.spyOn(LegacyCallHandler.instance, "getCallForRoom").mockReturnValue({} as unknown as MatrixCall);
-    };
-    const withCall = async (fn: (call: ElementCall) => void | Promise<void>): Promise<void> => {
-        await ElementCall.create(room);
-        const call = CallStore.instance.getCall(room.roomId);
-        if (!(call instanceof ElementCall)) throw new Error("Failed to create call");
-
-        const widget = new Widget(call.widget);
-
-        const eventEmitter = new EventEmitter();
-        const messaging = {
-            on: eventEmitter.on.bind(eventEmitter),
-            off: eventEmitter.off.bind(eventEmitter),
-            once: eventEmitter.once.bind(eventEmitter),
-            emit: eventEmitter.emit.bind(eventEmitter),
-            stop: jest.fn(),
-            transport: {
-                send: jest.fn(),
-                reply: jest.fn(),
-            },
-        } as unknown as Mocked<ClientWidgetApi>;
-        WidgetMessagingStore.instance.storeMessaging(widget, call.roomId, messaging);
-
-        await fn(call);
-
-        call.destroy();
-        WidgetMessagingStore.instance.stopMessaging(widget, call.roomId);
-    };
-
-    const renderHeader = (props: Partial<RoomHeaderProps> = {}, roomContext: Partial<IRoomState> = {}) => {
-        render(
-            <RoomContext.Provider value={{ ...roomContext, room } as IRoomState}>
-                <RoomHeader
-                    room={room}
-                    inRoom={true}
-                    onSearchClick={() => {}}
-                    onInviteClick={null}
-                    onForgetClick={() => {}}
-                    onAppsClick={() => {}}
-                    e2eStatus={E2EStatus.Normal}
-                    appsShown={true}
-                    searchInfo={{
-                        searchId: Math.random(),
-                        promise: new Promise<ISearchResults>(() => {}),
-                        term: "",
-                        scope: SearchScope.Room,
-                        count: 0,
-                    }}
-                    viewingCall={false}
-                    activeCall={null}
-                    {...props}
-                />
-            </RoomContext.Provider>,
-        );
-    };
-
-    it("hides call buttons in video rooms", () => {
-        mockRoomType(RoomType.UnstableCall);
-        mockEnabledSettings(["showCallButtonsInComposer", "feature_video_rooms", "feature_element_call_video_rooms"]);
-
-        renderHeader();
-        expect(screen.queryByRole("button", { name: /call/i })).toBeNull();
-    });
-
-    it("hides call buttons if showCallButtonsInComposer is disabled", () => {
-        mockEnabledSettings([]);
-
-        renderHeader();
-        expect(screen.queryByRole("button", { name: /call/i })).toBeNull();
-    });
-
-    it(
-        "hides the voice call button and disables the video call button if configured to use Element Call exclusively " +
-            "and there's an ongoing call",
-        async () => {
-            mockEnabledSettings(["showCallButtonsInComposer", "feature_group_calls"]);
-            SdkConfig.put({
-                element_call: { url: "https://call.element.io", use_exclusively: true, brand: "Element Call" },
-            });
-            await ElementCall.create(room);
-
-            renderHeader();
-            expect(screen.queryByRole("button", { name: "Voice call" })).toBeNull();
-            expect(screen.getByRole("button", { name: "Video call" })).toHaveAttribute("aria-disabled", "true");
-        },
-    );
-
-    it(
-        "hides the voice call button and starts an Element call when the video call button is pressed if configured to " +
-            "use Element Call exclusively",
-        async () => {
-            mockEnabledSettings(["showCallButtonsInComposer", "feature_group_calls"]);
-            SdkConfig.put({
-                element_call: { url: "https://call.element.io", use_exclusively: true, brand: "Element Call" },
-            });
-
-            renderHeader();
-            expect(screen.queryByRole("button", { name: "Voice call" })).toBeNull();
-
-            const dispatcherSpy = jest.fn();
-            const dispatcherRef = defaultDispatcher.register(dispatcherSpy);
-            fireEvent.click(screen.getByRole("button", { name: "Video call" }));
-            await waitFor(() =>
-                expect(dispatcherSpy).toHaveBeenCalledWith({
-                    action: Action.ViewRoom,
-                    room_id: room.roomId,
-                    skipLobby: false,
-                    view_call: true,
-                }),
-            );
-            defaultDispatcher.unregister(dispatcherRef);
-        },
-    );
-
-    it(
-        "hides the voice call button and disables the video call button if configured to use Element Call exclusively " +
-            "and the user lacks permission",
-        () => {
-            mockEnabledSettings(["showCallButtonsInComposer", "feature_group_calls"]);
-            SdkConfig.put({
-                element_call: { url: "https://call.element.io", use_exclusively: true, brand: "Element Call" },
-            });
-            mockEventPowerLevels({ [ElementCall.CALL_EVENT_TYPE.name]: 100 });
-
-            renderHeader();
-            expect(screen.queryByRole("button", { name: "Voice call" })).toBeNull();
-            expect(screen.getByRole("button", { name: "Video call" })).toHaveAttribute("aria-disabled", "true");
-        },
-    );
-
-    it("disables call buttons in the new group call experience if there's an ongoing Element call", async () => {
-        mockEnabledSettings(["showCallButtonsInComposer", "feature_group_calls"]);
-        await ElementCall.create(room);
-
-        renderHeader();
-        expect(screen.getByRole("button", { name: "Voice call" })).toHaveAttribute("aria-disabled", "true");
-        expect(screen.getByRole("button", { name: "Video call" })).toHaveAttribute("aria-disabled", "true");
-    });
-
-    it("disables call buttons in the new group call experience if there's an ongoing legacy 1:1 call", () => {
-        mockEnabledSettings(["showCallButtonsInComposer", "feature_group_calls"]);
-        mockLegacyCall();
-
-        renderHeader();
-        expect(screen.getByRole("button", { name: "Voice call" })).toHaveAttribute("aria-disabled", "true");
-        expect(screen.getByRole("button", { name: "Video call" })).toHaveAttribute("aria-disabled", "true");
-    });
-
-    it("disables call buttons in the new group call experience if there's an existing Jitsi widget", async () => {
-        mockEnabledSettings(["showCallButtonsInComposer", "feature_group_calls"]);
-        await JitsiCall.create(room);
-
-        renderHeader();
-        expect(screen.getByRole("button", { name: "Voice call" })).toHaveAttribute("aria-disabled", "true");
-        expect(screen.getByRole("button", { name: "Video call" })).toHaveAttribute("aria-disabled", "true");
-    });
-
-    it("disables call buttons in the new group call experience if there's no other members", () => {
-        mockEnabledSettings(["showCallButtonsInComposer", "feature_group_calls"]);
-
-        renderHeader();
-        expect(screen.getByRole("button", { name: "Voice call" })).toHaveAttribute("aria-disabled", "true");
-        expect(screen.getByRole("button", { name: "Video call" })).toHaveAttribute("aria-disabled", "true");
-    });
-
-    it(
-        "starts a legacy 1:1 call when call buttons are pressed in the new group call experience if there's 1 other " +
-            "member",
-        async () => {
-            mockEnabledSettings(["showCallButtonsInComposer", "feature_group_calls"]);
-            mockRoomMembers([alice, bob]);
-
-            renderHeader();
-
-            const placeCallSpy = jest.spyOn(LegacyCallHandler.instance, "placeCall").mockResolvedValue(undefined);
-            fireEvent.click(screen.getByRole("button", { name: "Voice call" }));
-            await act(() => Promise.resolve()); // Allow effects to settle
-            expect(placeCallSpy).toHaveBeenCalledWith(room.roomId, CallType.Voice);
-
-            placeCallSpy.mockClear();
-            fireEvent.click(screen.getByRole("button", { name: "Video call" }));
-            await act(() => Promise.resolve()); // Allow effects to settle
-            fireEvent.click(screen.getByRole("menuitem", { name: "Legacy video call" }));
-            await act(() => Promise.resolve()); // Allow effects to settle
-            expect(placeCallSpy).toHaveBeenCalledWith(room.roomId, CallType.Video);
-        },
-    );
-
-    it(
-        "creates a Jitsi widget when call buttons are pressed in the new group call experience if the user lacks " +
-            "permission to start Element calls",
-        async () => {
-            mockEnabledSettings(["showCallButtonsInComposer", "feature_group_calls"]);
-            mockRoomMembers([alice, bob, carol]);
-            mockEventPowerLevels({ [ElementCall.CALL_EVENT_TYPE.name]: 100 });
-
-            renderHeader();
-
-            const placeCallSpy = jest.spyOn(LegacyCallHandler.instance, "placeCall").mockResolvedValue(undefined);
-            fireEvent.click(screen.getByRole("button", { name: "Voice call" }));
-            await act(() => Promise.resolve()); // Allow effects to settle
-            expect(placeCallSpy).toHaveBeenCalledWith(room.roomId, CallType.Voice);
-
-            placeCallSpy.mockClear();
-            fireEvent.click(screen.getByRole("button", { name: "Video call" }));
-            await act(() => Promise.resolve()); // Allow effects to settle
-            expect(placeCallSpy).toHaveBeenCalledWith(room.roomId, CallType.Video);
-        },
-    );
-
-    it(
-        "creates a Jitsi widget when the voice call button is pressed and shows a menu when the video call button is " +
-            "pressed in the new group call experience",
-        async () => {
-            mockEnabledSettings(["showCallButtonsInComposer", "feature_group_calls"]);
-            mockRoomMembers([alice, bob, carol]);
-
-            renderHeader();
-
-            const placeCallSpy = jest.spyOn(LegacyCallHandler.instance, "placeCall").mockResolvedValue(undefined);
-            fireEvent.click(screen.getByRole("button", { name: "Voice call" }));
-            await act(() => Promise.resolve()); // Allow effects to settle
-            expect(placeCallSpy).toHaveBeenCalledWith(room.roomId, CallType.Voice);
-
-            // First try creating a Jitsi widget from the menu
-            placeCallSpy.mockClear();
-            fireEvent.click(screen.getByRole("button", { name: "Video call" }));
-            fireEvent.click(getByRole(screen.getByRole("menu"), "menuitem", { name: /jitsi/i }));
-            await act(() => Promise.resolve()); // Allow effects to settle
-            expect(placeCallSpy).toHaveBeenCalledWith(room.roomId, CallType.Video);
-
-            // Then try starting an Element call from the menu
-            const dispatcherSpy = jest.fn();
-            const dispatcherRef = defaultDispatcher.register(dispatcherSpy);
-            fireEvent.click(screen.getByRole("button", { name: "Video call" }));
-            fireEvent.click(getByRole(screen.getByRole("menu"), "menuitem", { name: /element/i }));
-            await waitFor(() =>
-                expect(dispatcherSpy).toHaveBeenCalledWith({
-                    action: Action.ViewRoom,
-                    room_id: room.roomId,
-                    skipLobby: false,
-                    view_call: true,
-                }),
-            );
-            defaultDispatcher.unregister(dispatcherRef);
-        },
-    );
-
-    it(
-        "disables the voice call button and starts an Element call when the video call button is pressed in the new " +
-            "group call experience if the user lacks permission to edit widgets",
-        async () => {
-            mockEnabledSettings(["showCallButtonsInComposer", "feature_group_calls"]);
-            mockRoomMembers([alice, bob, carol]);
-            mockEventPowerLevels({ "im.vector.modular.widgets": 100 });
-
-            renderHeader();
-            expect(screen.getByRole("button", { name: "Voice call" })).toHaveAttribute("aria-disabled", "true");
-
-            const dispatcherSpy = jest.fn();
-            const dispatcherRef = defaultDispatcher.register(dispatcherSpy);
-            fireEvent.click(screen.getByRole("button", { name: "Video call" }));
-            await waitFor(() =>
-                expect(dispatcherSpy).toHaveBeenCalledWith({
-                    action: Action.ViewRoom,
-                    room_id: room.roomId,
-                    skipLobby: false,
-                    view_call: true,
-                }),
-            );
-            defaultDispatcher.unregister(dispatcherRef);
-        },
-    );
-
-    it("disables call buttons in the new group call experience if the user lacks permission", () => {
-        mockEnabledSettings(["showCallButtonsInComposer", "feature_group_calls"]);
-        mockRoomMembers([alice, bob, carol]);
-        mockEventPowerLevels({ [ElementCall.CALL_EVENT_TYPE.name]: 100, "im.vector.modular.widgets": 100 });
-
-        renderHeader();
-        expect(screen.getByRole("button", { name: "Voice call" })).toHaveAttribute("aria-disabled", "true");
-        expect(screen.getByRole("button", { name: "Video call" })).toHaveAttribute("aria-disabled", "true");
-    });
-
-    it("disables call buttons if there's an ongoing legacy 1:1 call", () => {
-        mockEnabledSettings(["showCallButtonsInComposer"]);
-        mockLegacyCall();
-
-        renderHeader();
-        expect(screen.getByRole("button", { name: "Voice call" })).toHaveAttribute("aria-disabled", "true");
-        expect(screen.getByRole("button", { name: "Video call" })).toHaveAttribute("aria-disabled", "true");
-    });
-
-    it("disables call buttons if there's an existing Jitsi widget", async () => {
-        mockEnabledSettings(["showCallButtonsInComposer"]);
-        await JitsiCall.create(room);
-
-        renderHeader();
-        expect(screen.getByRole("button", { name: "Voice call" })).toHaveAttribute("aria-disabled", "true");
-        expect(screen.getByRole("button", { name: "Video call" })).toHaveAttribute("aria-disabled", "true");
-    });
-
-    it("disables call buttons if there's no other members", () => {
-        mockEnabledSettings(["showCallButtonsInComposer"]);
-
-        renderHeader();
-        expect(screen.getByRole("button", { name: "Voice call" })).toHaveAttribute("aria-disabled", "true");
-        expect(screen.getByRole("button", { name: "Video call" })).toHaveAttribute("aria-disabled", "true");
-    });
-
-    it("starts a legacy 1:1 call when call buttons are pressed if there's 1 other member", async () => {
-        mockEnabledSettings(["showCallButtonsInComposer"]);
-        mockRoomMembers([alice, bob]);
-        mockEventPowerLevels({ "im.vector.modular.widgets": 100 }); // Just to verify that it doesn't try to use Jitsi
-
-        renderHeader();
-
-        const placeCallSpy = jest.spyOn(LegacyCallHandler.instance, "placeCall").mockResolvedValue(undefined);
-        fireEvent.click(screen.getByRole("button", { name: "Voice call" }));
-        await act(() => Promise.resolve()); // Allow effects to settle
-        expect(placeCallSpy).toHaveBeenCalledWith(room.roomId, CallType.Voice);
-
-        placeCallSpy.mockClear();
-        fireEvent.click(screen.getByRole("button", { name: "Video call" }));
-        await act(() => Promise.resolve()); // Allow effects to settle
-        expect(placeCallSpy).toHaveBeenCalledWith(room.roomId, CallType.Video);
-    });
-
-    it("creates a Jitsi widget when call buttons are pressed", async () => {
-        mockEnabledSettings(["showCallButtonsInComposer"]);
-        mockRoomMembers([alice, bob, carol]);
-
-        renderHeader();
-
-        const placeCallSpy = jest.spyOn(LegacyCallHandler.instance, "placeCall").mockResolvedValue(undefined);
-        fireEvent.click(screen.getByRole("button", { name: "Voice call" }));
-        await act(() => Promise.resolve()); // Allow effects to settle
-        expect(placeCallSpy).toHaveBeenCalledWith(room.roomId, CallType.Voice);
-
-        placeCallSpy.mockClear();
-        fireEvent.click(screen.getByRole("button", { name: "Video call" }));
-        await act(() => Promise.resolve()); // Allow effects to settle
-        expect(placeCallSpy).toHaveBeenCalledWith(room.roomId, CallType.Video);
-    });
-
-    it("disables call buttons if the user lacks permission", () => {
-        mockEnabledSettings(["showCallButtonsInComposer"]);
-        mockRoomMembers([alice, bob, carol]);
-        mockEventPowerLevels({ "im.vector.modular.widgets": 100 });
-
-        renderHeader();
-        expect(screen.getByRole("button", { name: "Voice call" })).toHaveAttribute("aria-disabled", "true");
-        expect(screen.getByRole("button", { name: "Video call" })).toHaveAttribute("aria-disabled", "true");
-    });
-
-    it("shows a close button when viewing a call lobby that returns to the timeline when pressed", async () => {
-        mockEnabledSettings(["feature_group_calls"]);
-
-        renderHeader({ viewingCall: true });
-
-        const dispatcherSpy = jest.fn();
-        const dispatcherRef = defaultDispatcher.register(dispatcherSpy);
-        fireEvent.click(screen.getByRole("button", { name: /close/i }));
-        await waitFor(() =>
-            expect(dispatcherSpy).toHaveBeenCalledWith({
-                action: Action.ViewRoom,
-                room_id: room.roomId,
-                view_call: false,
-            }),
-        );
-        defaultDispatcher.unregister(dispatcherRef);
-    });
-
-    it("shows a reduce button when viewing a call that returns to the timeline when pressed", async () => {
-        mockEnabledSettings(["feature_group_calls"]);
-
-        await withCall(async (call) => {
-            renderHeader({ viewingCall: true, activeCall: call });
-
-            const dispatcherSpy = jest.fn();
-            const dispatcherRef = defaultDispatcher.register(dispatcherSpy);
-            fireEvent.click(screen.getByRole("button", { name: /timeline/i }));
-            await waitFor(() =>
-                expect(dispatcherSpy).toHaveBeenCalledWith({
-                    action: Action.ViewRoom,
-                    room_id: room.roomId,
-                    view_call: false,
-                }),
-            );
-            defaultDispatcher.unregister(dispatcherRef);
-        });
-    });
-
-    it("shows a layout button when viewing a call that shows a menu when pressed", async () => {
-        mockEnabledSettings(["feature_group_calls"]);
-
-        await withCall(async (call) => {
-            // We set the call to skip lobby because otherwise the connection will wait until
-            // the user clicks the "join" button, inside the widget lobby which is hard to mock.
-            call.widget.data = { ...call.widget.data, skipLobby: true };
-            // The connect method will wait until the session actually connected. Otherwise it will timeout.
-            // Emitting SessionStarted will trigger the connect method to resolve.
-            setTimeout(
-                () =>
-                    client.matrixRTC.emit(MatrixRTCSessionManagerEvents.SessionStarted, room.roomId, {
-                        room,
-                    } as MatrixRTCSession),
-                100,
-            );
-            await call.start();
-
-            const messaging = WidgetMessagingStore.instance.getMessagingForUid(WidgetUtils.getWidgetUid(call.widget))!;
-            renderHeader({ viewingCall: true, activeCall: call });
-
-            // Should start with Freedom selected
-            fireEvent.click(screen.getByRole("button", { name: /layout/i }));
-            screen.getByRole("menuitemradio", { name: "Freedom", checked: true });
-
-            // Clicking Spotlight should tell the widget to switch and close the menu
-            fireEvent.click(screen.getByRole("menuitemradio", { name: "Spotlight" }));
-            expect(mocked(messaging.transport).send).toHaveBeenCalledWith(ElementWidgetActions.SpotlightLayout, {});
-            expect(screen.queryByRole("menu")).toBeNull();
-
-            // When the widget responds and the user reopens the menu, they should see Spotlight selected
-            act(() => {
-                messaging.emit(
-                    `action:${ElementWidgetActions.SpotlightLayout}`,
-                    new CustomEvent("widgetapirequest", { detail: { data: {} } }),
-                );
-            });
-            fireEvent.click(screen.getByRole("button", { name: /layout/i }));
-            screen.getByRole("menuitemradio", { name: "Spotlight", checked: true });
-
-            // Now try switching back to Freedom
-            fireEvent.click(screen.getByRole("menuitemradio", { name: "Freedom" }));
-            expect(mocked(messaging.transport).send).toHaveBeenCalledWith(ElementWidgetActions.TileLayout, {});
-            expect(screen.queryByRole("menu")).toBeNull();
-
-            // When the widget responds and the user reopens the menu, they should see Freedom selected
-            act(() => {
-                messaging.emit(
-                    `action:${ElementWidgetActions.TileLayout}`,
-                    new CustomEvent("widgetapirequest", { detail: { data: {} } }),
-                );
-            });
-            fireEvent.click(screen.getByRole("button", { name: /layout/i }));
-            screen.getByRole("menuitemradio", { name: "Freedom", checked: true });
-        });
-    });
-
-    it("shows an invite button in video rooms", () => {
-        mockEnabledSettings(["feature_video_rooms", "feature_element_call_video_rooms"]);
-        mockRoomType(RoomType.UnstableCall);
-
-        const onInviteClick = jest.fn();
-        renderHeader({ onInviteClick, viewingCall: true });
-
-        fireEvent.click(screen.getByRole("button", { name: /invite/i }));
-        expect(onInviteClick).toHaveBeenCalled();
-    });
-
-    it("hides the invite button in non-video rooms when viewing a call", () => {
-        renderHeader({ onInviteClick: () => {}, viewingCall: true });
-
-        expect(screen.queryByRole("button", { name: /invite/i })).toBeNull();
-    });
-
-    it("shows the room avatar in a room with only ourselves", () => {
-        // When we render a non-DM room with 1 person in it
-        const room = createRoom({ name: "X Room", isDm: false, userIds: [] });
-        const rendered = mountHeader(room);
-
-        // Then the room's avatar is the initial of its name
-        const initial = rendered.container.querySelector(".mx_BaseAvatar");
-        expect(initial).toHaveTextContent("X");
-    });
-
-    it("shows the room avatar in a room with 2 people", () => {
-        // When we render a non-DM room with 2 people in it
-        const room = createRoom({ name: "Y Room", isDm: false, userIds: ["other"] });
-        const rendered = mountHeader(room);
-
-        // Then the room's avatar is the initial of its name
-        const initial = rendered.container.querySelector(".mx_BaseAvatar");
-        expect(initial).toHaveTextContent("Y");
-    });
-
-    it("shows the room avatar in a room with >2 people", () => {
-        // When we render a non-DM room with 3 people in it
-        const room = createRoom({ name: "Z Room", isDm: false, userIds: ["other1", "other2"] });
-        const rendered = mountHeader(room);
-
-        // Then the room's avatar is the initial of its name
-        const initial = rendered.container.querySelector(".mx_BaseAvatar");
-        expect(initial).toHaveTextContent("Z");
-    });
-
-    it("shows the room avatar in a DM with only ourselves", () => {
-        // When we render a non-DM room with 1 person in it
-        const room = createRoom({ name: "Z Room", isDm: true, userIds: [] });
-        const rendered = mountHeader(room);
-
-        // Then the room's avatar is the initial of its name
-        const initial = rendered.container.querySelector(".mx_BaseAvatar");
-        expect(initial).toHaveTextContent("Z");
-    });
-
-    it("shows the user avatar in a DM with 2 people", () => {
-        // Note: this is the interesting case - this is the ONLY
-        //       time we should use the user's avatar.
-
-        // When we render a DM room with only 2 people in it
-        const room = createRoom({ name: "Y Room", isDm: true, userIds: ["other"] });
-        const rendered = mountHeader(room);
-
-        // Then we use the other user's avatar as our room's image avatar
-        const image = rendered.container.querySelector(".mx_BaseAvatar img");
-        expect(image).toHaveAttribute("src", "http://this.is.a.url/example.org/other");
-    });
-
-    it("shows the room avatar in a DM with >2 people", () => {
-        // When we render a DM room with 3 people in it
-        const room = createRoom({
-            name: "Z Room",
-            isDm: true,
-            userIds: ["other1", "other2"],
-        });
-        const rendered = mountHeader(room);
-
-        // Then the room's avatar is the initial of its name
-        const initial = rendered.container.querySelector(".mx_BaseAvatar");
-        expect(initial).toHaveTextContent("Z");
-    });
-
-    it("renders call buttons normally", () => {
-        const room = createRoom({ name: "Room", isDm: false, userIds: ["other"] });
-        const wrapper = mountHeader(room);
-
-        expect(wrapper.container.querySelector('[aria-label="Voice call"]')).toBeDefined();
-        expect(wrapper.container.querySelector('[aria-label="Video call"]')).toBeDefined();
-    });
-
-    it("hides call buttons when the room is tombstoned", () => {
-        const room = createRoom({ name: "Room", isDm: false, userIds: [] });
-        const wrapper = mountHeader(
-            room,
-            {},
-            {
-                tombstone: mkEvent({
-                    event: true,
-                    type: "m.room.tombstone",
-                    room: room.roomId,
-                    user: "@user1:server",
-                    skey: "",
-                    content: {},
-                    ts: Date.now(),
-                }),
-            },
-        );
-
-        expect(wrapper.container.querySelector('[aria-label="Voice call"]')).toBeFalsy();
-        expect(wrapper.container.querySelector('[aria-label="Video call"]')).toBeFalsy();
-    });
-
-    it("should render buttons if not passing showButtons (default true)", () => {
-        const room = createRoom({ name: "Room", isDm: false, userIds: [] });
-        const wrapper = mountHeader(room);
-        expect(wrapper.container.querySelector(".mx_LegacyRoomHeader_button")).toBeDefined();
-    });
-
-    it("should not render buttons if passing showButtons = false", () => {
-        const room = createRoom({ name: "Room", isDm: false, userIds: [] });
-        const wrapper = mountHeader(room, { showButtons: false });
-        expect(wrapper.container.querySelector(".mx_LegacyRoomHeader_button")).toBeFalsy();
-    });
-
-    it("should render the room options context menu if not passing enableRoomOptionsMenu (default true) and UIComponent customisations room options enabled", () => {
-        mocked(shouldShowComponent).mockReturnValue(true);
-        const room = createRoom({ name: "Room", isDm: false, userIds: [] });
-        const wrapper = mountHeader(room);
-        expect(shouldShowComponent).toHaveBeenCalledWith(UIComponent.RoomOptionsMenu);
-        expect(wrapper.container.querySelector(".mx_LegacyRoomHeader_name.mx_AccessibleButton")).toBeDefined();
-    });
-
-    it.each([
-        [false, true],
-        [true, false],
-    ])(
-        "should not render the room options context menu if passing enableRoomOptionsMenu = %s and UIComponent customisations room options enable = %s",
-        (enableRoomOptionsMenu, showRoomOptionsMenu) => {
-            mocked(shouldShowComponent).mockReturnValue(showRoomOptionsMenu);
-            const room = createRoom({ name: "Room", isDm: false, userIds: [] });
-            const wrapper = mountHeader(room, { enableRoomOptionsMenu });
-            expect(wrapper.container.querySelector(".mx_LegacyRoomHeader_name.mx_AccessibleButton")).toBeFalsy();
-        },
-    );
-
-    it("renders additionalButtons", async () => {
-        const additionalButtons: ViewRoomOpts["buttons"] = [
-            {
-                icon: () => <>test-icon</>,
-                id: "test-id",
-                label: () => "test-label",
-                onClick: () => {},
-            },
-        ];
-        renderHeader({ additionalButtons });
-        expect(screen.getByRole("button", { name: "test-icon" })).toBeInTheDocument();
-    });
-
-    it("calls onClick-callback on additionalButtons", () => {
-        const callback = jest.fn();
-        const additionalButtons: ViewRoomOpts["buttons"] = [
-            {
-                icon: () => <>test-icon</>,
-                id: "test-id",
-                label: () => "test-label",
-                onClick: callback,
-            },
-        ];
-        renderHeader({ additionalButtons });
-        fireEvent.click(screen.getByRole("button", { name: "test-icon" }));
-        expect(callback).toHaveBeenCalled();
-    });
-});
-
-interface IRoomCreationInfo {
-    name: string;
-    isDm: boolean;
-    userIds: string[];
-}
-
-function createRoom(info: IRoomCreationInfo) {
-    stubClient();
-    const client: MatrixClient = MatrixClientPeg.safeGet();
-
-    const roomId = "!1234567890:domain";
-    const userId = client.getUserId()!;
-    if (info.isDm) {
-        client.getAccountData = (eventType) => {
-            if (eventType === "m.direct") {
-                return mkDirectEvent(roomId, userId, info.userIds);
-            } else {
-                return undefined;
-            }
-        };
-    }
-
-    DMRoomMap.makeShared(client).start();
-
-    const room = new Room(roomId, client, userId, {
-        pendingEventOrdering: PendingEventOrdering.Detached,
-    });
-
-    const otherJoinEvents: MatrixEvent[] = [];
-    for (const otherUserId of info.userIds) {
-        otherJoinEvents.push(mkJoinEvent(roomId, otherUserId));
-    }
-
-    room.currentState.setStateEvents([
-        mkCreationEvent(roomId, userId),
-        mkNameEvent(roomId, userId, info.name),
-        mkJoinEvent(roomId, userId),
-        ...otherJoinEvents,
-    ]);
-    room.recalculate();
-
-    return room;
-}
-
-function mountHeader(room: Room, propsOverride = {}, roomContext?: Partial<IRoomState>): RenderResult {
-    const props: RoomHeaderProps = {
-        room,
-        inRoom: true,
-        onSearchClick: () => {},
-        onInviteClick: null,
-        onForgetClick: () => {},
-        onAppsClick: () => {},
-        e2eStatus: E2EStatus.Normal,
-        appsShown: true,
-        searchInfo: {
-            searchId: Math.random(),
-            promise: new Promise<ISearchResults>(() => {}),
-            term: "",
-            scope: SearchScope.Room,
-            count: 0,
-        },
-        viewingCall: false,
-        activeCall: null,
-        ...propsOverride,
-    };
-
-    return render(
-        <RoomContext.Provider value={{ ...roomContext, room } as IRoomState}>
-            <RoomHeader {...props} />
-        </RoomContext.Provider>,
-    );
-}
-
-function mkCreationEvent(roomId: string, userId: string): MatrixEvent {
-    return mkEvent({
-        event: true,
-        type: "m.room.create",
-        room: roomId,
-        user: userId,
-        content: {
-            creator: userId,
-            room_version: "5",
-            predecessor: {
-                room_id: "!prevroom",
-                event_id: "$someevent",
-            },
-        },
-    });
-}
-
-function mkNameEvent(roomId: string, userId: string, name: string): MatrixEvent {
-    return mkEvent({
-        event: true,
-        type: "m.room.name",
-        room: roomId,
-        user: userId,
-        content: { name },
-    });
-}
-
-function mkJoinEvent(roomId: string, userId: string) {
-    const ret = mkEvent({
-        event: true,
-        type: "m.room.member",
-        room: roomId,
-        user: userId,
-        content: {
-            membership: KnownMembership.Join,
-            avatar_url: "mxc://example.org/" + userId,
-        },
-    });
-    ret.event.state_key = userId;
-    return ret;
-}
-
-function mkDirectEvent(roomId: string, userId: string, otherUsers: string[]): MatrixEvent {
-    const content: Record<string, string[]> = {};
-    for (const otherUserId of otherUsers) {
-        content[otherUserId] = [roomId];
-    }
-    return mkEvent({
-        event: true,
-        type: "m.direct",
-        room: roomId,
-        user: userId,
-        content,
-    });
-}
diff --git a/test/components/views/settings/tabs/user/__snapshots__/LabsUserSettingsTab-test.tsx.snap b/test/components/views/settings/tabs/user/__snapshots__/LabsUserSettingsTab-test.tsx.snap
index 51ab465503..4e65f7b61f 100644
--- a/test/components/views/settings/tabs/user/__snapshots__/LabsUserSettingsTab-test.tsx.snap
+++ b/test/components/views/settings/tabs/user/__snapshots__/LabsUserSettingsTab-test.tsx.snap
@@ -129,61 +129,6 @@ exports[`<LabsUserSettingsTab /> renders settings marked as beta as beta cards 1
         </div>
       </div>
     </div>
-    <div
-      class="mx_BetaCard"
-    >
-      <div
-        class="mx_BetaCard_columns"
-      >
-        <div
-          class="mx_BetaCard_columns_description"
-        >
-          <h3
-            class="mx_BetaCard_title"
-          >
-            <span>
-              Room header
-            </span>
-            <span
-              class="mx_BetaCard_betaPill"
-            >
-              Beta
-            </span>
-          </h3>
-          <div
-            class="mx_BetaCard_caption"
-          >
-            <p>
-              A new look for your rooms with a simpler, cleaner and more accessible room header.
-            </p>
-          </div>
-          <div
-            class="mx_BetaCard_buttons"
-          >
-            <div
-              class="mx_AccessibleButton mx_AccessibleButton_hasKind mx_AccessibleButton_kind_primary"
-              role="button"
-              tabindex="0"
-            >
-              Join the beta
-            </div>
-          </div>
-          <div
-            class="mx_BetaCard_refreshWarning"
-          >
-            Joining the beta will reload BrandedClient.
-          </div>
-        </div>
-        <div
-          class="mx_BetaCard_columns_image_wrapper"
-        >
-          <img
-            alt=""
-            class="mx_BetaCard_columns_image"
-          />
-        </div>
-      </div>
-    </div>
   </div>
 </div>
 `;