diff --git a/src/components/structures/SpaceRoomView.tsx b/src/components/structures/SpaceRoomView.tsx
index 4564741a290..a99a22687e0 100644
--- a/src/components/structures/SpaceRoomView.tsx
+++ b/src/components/structures/SpaceRoomView.tsx
@@ -34,7 +34,7 @@ import { useStateArray } from "../../hooks/useStateArray";
import { _t } from "../../languageHandler";
import PosthogTrackers from "../../PosthogTrackers";
import { inviteMultipleToRoom, showRoomInviteDialog } from "../../RoomInvite";
-import { UIComponent } from "../../settings/UIFeature";
+import { UIComponent, UIFeature } from "../../settings/UIFeature";
import { UPDATE_EVENT } from "../../stores/AsyncStore";
import RightPanelStore from "../../stores/right-panel/RightPanelStore";
import { RightPanelPhases } from "../../stores/right-panel/RightPanelStorePhases";
@@ -74,6 +74,7 @@ import MainSplit from "./MainSplit";
import RightPanel from "./RightPanel";
import SpaceHierarchy, { showRoom } from "./SpaceHierarchy";
import { RoomPermalinkCreator } from "../../utils/permalinks/Permalinks";
+import SettingsStore from "../../settings/SettingsStore";
interface IProps {
space: Room;
@@ -173,7 +174,7 @@ const SpaceLandingAddButton: React.FC<{ space: Room }> = ({ space }) => {
showAddExistingRooms(space);
}}
/>
- {canCreateSpace && (
+ {canCreateSpace && SettingsStore.getValue(UIFeature.ShowCreateSpaceButton) && (
= ({ space, hideHeader, onFinished, ...
)}
- {canAddSubSpaces && (
+ {SettingsStore.getValue(UIFeature.AddSubSpace) && canAddSubSpaces && (
{
// align the context menu's icons with the icon which opened the context menu
@@ -269,7 +270,7 @@ const RoomListHeader: React.FC = ({ onVisibilityChange }) => {
disabled={!canAddSubRooms}
title={!canAddSubRooms ? _t("spaces|error_no_permission_add_room") : undefined}
/>
- {canCreateSpaces && (
+ {SettingsStore.getValue(UIFeature.AddSpace) && canCreateSpaces && (
(
))}
{children}
- {shouldShowComponent(UIComponent.CreateSpaces) && (
-
+ {SettingsStore.getValue(UIFeature.ShowCreateSpaceButton) && (
+ <>
+ {shouldShowComponent(UIComponent.CreateSpaces) && (
+
+ )}
+ >
)}
);
diff --git a/src/settings/Settings.tsx b/src/settings/Settings.tsx
index 4846d8db907..e35fa4fd895 100644
--- a/src/settings/Settings.tsx
+++ b/src/settings/Settings.tsx
@@ -1239,6 +1239,18 @@ export const SETTINGS: { [setting: string]: ISetting } = {
supportedLevels: LEVELS_UI_FEATURE,
default: true,
},
+ [UIFeature.ShowCreateSpaceButton]: {
+ supportedLevels: LEVELS_UI_FEATURE,
+ default: true,
+ },
+ [UIFeature.AddSubSpace]: {
+ supportedLevels: LEVELS_UI_FEATURE,
+ default: true,
+ },
+ [UIFeature.AddSpace]: {
+ supportedLevels: LEVELS_UI_FEATURE,
+ default: true,
+ },
[UIFeature.ShowStickersButtonSetting]: {
supportedLevels: LEVELS_UI_FEATURE,
default: true,
diff --git a/src/settings/UIFeature.ts b/src/settings/UIFeature.ts
index 5e9a18cafcd..6c1b9a6588e 100644
--- a/src/settings/UIFeature.ts
+++ b/src/settings/UIFeature.ts
@@ -33,6 +33,9 @@ export const enum UIFeature {
RoomHistorySettings = "UIFeature.roomHistorySettings",
TimelineEnableRelativeDates = "UIFeature.timelineEnableRelativeDates",
BulkUnverifiedSessionsReminder = "UIFeature.BulkUnverifiedSessionsReminder",
+ ShowCreateSpaceButton = "UIFeature.showCreateSpaceButton",
+ AddSubSpace = "UIFeature.addSubSpace",
+ AddSpace = "UIFeature.addSpace",
ShowStickersButtonSetting = "UIFeature.showStickersButtonSetting",
InsertTrailingColonSetting = "UIFeature.insertTrailingColonSetting",
ShowJoinLeavesSetting = "UIFeature.showJoinLeavesSetting",
diff --git a/test/components/views/context_menus/SpaceContextMenu-test.tsx b/test/components/views/context_menus/SpaceContextMenu-test.tsx
index 9e787b8bd0f..3c4677d8b9c 100644
--- a/test/components/views/context_menus/SpaceContextMenu-test.tsx
+++ b/test/components/views/context_menus/SpaceContextMenu-test.tsx
@@ -31,7 +31,8 @@ import {
} from "../../../../src/utils/space";
import { leaveSpace } from "../../../../src/utils/leave-behaviour";
import { shouldShowComponent } from "../../../../src/customisations/helpers/UIComponents";
-import { UIComponent } from "../../../../src/settings/UIFeature";
+import { UIComponent, UIFeature } from "../../../../src/settings/UIFeature";
+import SettingsStore from "../../../../src/settings/SettingsStore";
jest.mock("../../../../src/customisations/helpers/UIComponents", () => ({
shouldShowComponent: jest.fn(),
@@ -223,4 +224,41 @@ describe("", () => {
expect(onFinished).toHaveBeenCalled();
});
});
+
+ describe("UIFeature.AddSubSpace feature flag", () => {
+ const space = makeMockSpace();
+
+ beforeEach(() => {
+ // set space to allow adding children to space
+ mocked(space.currentState.maySendStateEvent).mockReturnValue(true);
+ mocked(shouldShowComponent).mockReturnValue(true);
+ jest.clearAllMocks();
+ });
+
+ it("UIFeature.AddSubSpace = true: renders create space button when UIFeature is true", () => {
+ jest.spyOn(SettingsStore, "getValue").mockImplementation((name) => {
+ if (name === UIFeature.AddSubSpace) return true;
+ else return "default";
+ });
+ renderComponent({ space });
+
+ screen.debug();
+
+ expect(screen.getByTestId("add-to-space-header")).toBeInTheDocument();
+ expect(screen.getByTestId("new-room-option")).toBeInTheDocument();
+ expect(screen.queryByTestId("new-subspace-option")).toBeInTheDocument();
+ });
+
+ it("UIFeature.AddSubSpace = false: does not render create space button when UIFeature is false", () => {
+ jest.spyOn(SettingsStore, "getValue").mockImplementation((name) => {
+ if (name === UIFeature.AddSubSpace) return false;
+ else return "default";
+ });
+ renderComponent({ space });
+
+ expect(screen.getByTestId("add-to-space-header")).toBeInTheDocument();
+ expect(screen.getByTestId("new-room-option")).toBeInTheDocument();
+ expect(screen.queryByTestId("new-subspace-option")).not.toBeInTheDocument();
+ });
+ });
});
diff --git a/test/components/views/rooms/RoomListHeader-test.tsx b/test/components/views/rooms/RoomListHeader-test.tsx
index 47cceedd202..46f00b54c85 100644
--- a/test/components/views/rooms/RoomListHeader-test.tsx
+++ b/test/components/views/rooms/RoomListHeader-test.tsx
@@ -29,7 +29,7 @@ import { MatrixClientPeg } from "../../../../src/MatrixClientPeg";
import SettingsStore from "../../../../src/settings/SettingsStore";
import { SettingLevel } from "../../../../src/settings/SettingLevel";
import { shouldShowComponent } from "../../../../src/customisations/helpers/UIComponents";
-import { UIComponent } from "../../../../src/settings/UIFeature";
+import { UIComponent, UIFeature } from "../../../../src/settings/UIFeature";
const RoomListHeader = testUtils.wrapInMatrixClientContext(_RoomListHeader);
@@ -285,4 +285,30 @@ describe("RoomListHeader", () => {
checkIsDisabled(items[3]);
});
});
+
+ describe("UIFeature.AddSpace", () => {
+ it("UIFeature.AddSpace = true: renders Add Space when user has permission to add spaces", async () => {
+ jest.spyOn(SettingsStore, "getValue").mockImplementation((name) => {
+ if (name === UIFeature.AddSpace) return true;
+ else return "default";
+ });
+
+ const testSpace = setupSpace(client);
+ await setupPlusMenu(client, testSpace);
+
+ expect(screen.getByText("Add space")).toBeInTheDocument();
+ });
+
+ it("UIFeature.AddSpace = false: does not render Add Space when user has permission to add spaces", async () => {
+ jest.spyOn(SettingsStore, "getValue").mockImplementation((name) => {
+ if (name === UIFeature.AddSpace) return false;
+ else return "default";
+ });
+
+ const testSpace = setupSpace(client);
+ await setupPlusMenu(client, testSpace);
+
+ expect(screen.queryByText("Add space")).not.toBeInTheDocument();
+ });
+ });
});
diff --git a/test/components/views/spaces/SpacePanel-test.tsx b/test/components/views/spaces/SpacePanel-test.tsx
index 828ad55280b..74a341363fb 100644
--- a/test/components/views/spaces/SpacePanel-test.tsx
+++ b/test/components/views/spaces/SpacePanel-test.tsx
@@ -26,7 +26,7 @@ import {
import { MatrixClientPeg } from "../../../../src/MatrixClientPeg";
import { MetaSpace, SpaceKey } from "../../../../src/stores/spaces";
import { shouldShowComponent } from "../../../../src/customisations/helpers/UIComponents";
-import { UIComponent } from "../../../../src/settings/UIFeature";
+import { UIComponent, UIFeature } from "../../../../src/settings/UIFeature";
import { mkStubRoom, wrapInMatrixClientContext, wrapInSdkContext } from "../../../test-utils";
import { SdkContextClass } from "../../../../src/contexts/SDKContext";
import SpaceStore from "../../../../src/stores/spaces/SpaceStore";
@@ -182,6 +182,24 @@ describe("", () => {
fireEvent.click(screen.getByTestId("create-space-button"));
screen.getByTestId("create-space-button");
});
+ it("renders create space button when UIFeature is true", () => {
+ jest.spyOn(SettingsStore, "getValue").mockImplementation((name) => {
+ if (name === UIFeature.ShowCreateSpaceButton) return true;
+ return true;
+ });
+ mocked(shouldShowComponent).mockReturnValue(true);
+ render();
+ expect(screen.queryByTestId("create-space-button")).not.toBeNull();
+ });
+ it("does not render create space button when UIFeature is false", () => {
+ jest.spyOn(SettingsStore, "getValue").mockImplementation((name) => {
+ if (name === UIFeature.ShowCreateSpaceButton) return false;
+ return true;
+ });
+ mocked(shouldShowComponent).mockReturnValue(true);
+ render();
+ expect(screen.queryByTestId("create-space-button")).toBeNull();
+ });
});
it("should allow rearranging via drag and drop", async () => {