Add UIFeature to hide public space and room creation (#30708)

* Add settings to hide public room & space creation.

* Add space changes.

* Add room changes.

* lint

* Add playwright tests

* don't specialcase 1 join rule

* Ensure mocks get cleared

* Fixup test

* Add SpaceCreateMenu component unit-tests

* Fixup create room test asserts

* fix import
This commit is contained in:
Will Hunt
2025-09-08 13:53:13 +00:00
committed by GitHub
parent 9e7f583acc
commit 6d05bfc4c5
13 changed files with 264 additions and 22 deletions
@@ -85,6 +85,7 @@ interface IState {
export default class CreateRoomDialog extends React.Component<IProps, IState> {
private readonly askToJoinEnabled: boolean;
private readonly advancedSettingsEnabled: boolean;
private readonly allowCreatingPublicRooms: boolean;
private readonly supportsRestricted: boolean;
private nameField = createRef<Field>();
private aliasField = createRef<RoomAliasField>();
@@ -94,11 +95,13 @@ export default class CreateRoomDialog extends React.Component<IProps, IState> {
this.askToJoinEnabled = SettingsStore.getValue("feature_ask_to_join");
this.advancedSettingsEnabled = SettingsStore.getValue(UIFeature.AdvancedSettings);
this.allowCreatingPublicRooms = SettingsStore.getValue(UIFeature.AllowCreatingPublicRooms);
this.supportsRestricted = !!this.props.parentSpace;
const defaultPublic = this.allowCreatingPublicRooms && this.props.defaultPublic;
let joinRule = JoinRule.Invite;
if (this.props.defaultPublic) {
if (defaultPublic) {
joinRule = JoinRule.Public;
} else if (this.supportsRestricted) {
joinRule = JoinRule.Restricted;
@@ -106,7 +109,7 @@ export default class CreateRoomDialog extends React.Component<IProps, IState> {
const cli = MatrixClientPeg.safeGet();
this.state = {
isPublicKnockRoom: this.props.defaultPublic || false,
isPublicKnockRoom: defaultPublic || false,
isEncrypted: this.props.defaultEncrypted ?? privateShouldBeEncrypted(cli),
joinRule,
name: this.props.defaultName || "",
@@ -419,7 +422,7 @@ export default class CreateRoomDialog extends React.Component<IProps, IState> {
labelKnock={
this.askToJoinEnabled ? _t("room_settings|security|join_rule_knock") : undefined
}
labelPublic={_t("common|public_room")}
labelPublic={this.allowCreatingPublicRooms ? _t("common|public_room") : undefined}
labelRestricted={
this.supportsRestricted ? _t("create_room|join_rule_restricted") : undefined
}
@@ -19,7 +19,7 @@ interface IProps {
width?: number;
labelInvite: string;
labelKnock?: string;
labelPublic: string;
labelPublic?: string;
labelRestricted?: string; // if omitted then this option will be hidden, e.g if unsupported
onChange(value: JoinRule): void;
}
@@ -38,11 +38,18 @@ const JoinRuleDropdown: React.FC<IProps> = ({
<div key={JoinRule.Invite} className="mx_JoinRuleDropdown_invite">
{labelInvite}
</div>,
<div key={JoinRule.Public} className="mx_JoinRuleDropdown_public">
{labelPublic}
</div>,
] as NonEmptyArray<ReactElement & { key: string }>;
if (labelPublic) {
options.push(
(
<div key={JoinRule.Public} className="mx_JoinRuleDropdown_public">
{labelPublic}
</div>
) as ReactElement & { key: string },
);
}
if (labelKnock) {
options.unshift(
(
@@ -72,6 +79,7 @@ const JoinRuleDropdown: React.FC<IProps> = ({
menuWidth={width}
value={value}
label={label}
disabled={options.length === 1}
>
{options}
</Dropdown>
@@ -45,6 +45,8 @@ import defaultDispatcher from "../../../dispatcher/dispatcher";
import { Action } from "../../../dispatcher/actions";
import { Filter } from "../dialogs/spotlight/Filter";
import { type OpenSpotlightPayload } from "../../../dispatcher/payloads/OpenSpotlightPayload.ts";
import { useSettingValue } from "../../../hooks/useSettings.ts";
import { UIFeature } from "../../../settings/UIFeature.ts";
export const createSpace = async (
client: MatrixClient,
@@ -212,7 +214,10 @@ const SpaceCreateMenu: React.FC<{
onFinished(): void;
}> = ({ onFinished }) => {
const cli = useMatrixClientContext();
const [visibility, setVisibility] = useState<Visibility | null>(null);
const settingAllowPublicSpaces = useSettingValue(UIFeature.AllowCreatingPublicSpaces);
const [visibility, setVisibility] = useState<Visibility | null>(
settingAllowPublicSpaces === false ? Visibility.Private : null,
);
const [busy, setBusy] = useState<boolean>(false);
const [name, setName] = useState("");
@@ -303,16 +308,20 @@ const SpaceCreateMenu: React.FC<{
} else {
body = (
<React.Fragment>
<AccessibleButton
className="mx_SpaceCreateMenu_back"
onClick={() => setVisibility(null)}
title={_t("action|go_back")}
/>
{settingAllowPublicSpaces && (
<AccessibleButton
className="mx_SpaceCreateMenu_back"
onClick={() => setVisibility(null)}
title={_t("action|go_back")}
/>
)}
<h2>
{visibility === Visibility.Public
? _t("create_space|public_heading")
: _t("create_space|private_heading")}
: settingAllowPublicSpaces
? _t("create_space|private_heading")
: _t("create_space|private_only_heading")}
</h2>
<p>
{_t("create_space|add_details_prompt")} {_t("create_space|add_details_prompt_2")}