Skip to content
This repository was archived by the owner on Sep 11, 2024. It is now read-only.

Commit 68e0eba

Browse files
author
Charly Nguyen
committed
Allow creating public knock rooms
Signed-off-by: Charly Nguyen <[email protected]>
1 parent 200631f commit 68e0eba

File tree

6 files changed

+99
-32
lines changed

6 files changed

+99
-32
lines changed

res/css/views/dialogs/_CreateRoomDialog.pcss

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,3 +113,8 @@ limitations under the License.
113113
font-size: $font-12px;
114114
}
115115
}
116+
117+
.mx_CreateRoomDialog_labelledCheckbox {
118+
color: $muted-fg-color;
119+
margin-top: var(--cpd-space-6x);
120+
}

src/components/views/dialogs/CreateRoomDialog.tsx

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ import { getKeyBindingsManager } from "../../../KeyBindingsManager";
3333
import { KeyBindingAction } from "../../../accessibility/KeyboardShortcuts";
3434
import { privateShouldBeEncrypted } from "../../../utils/rooms";
3535
import SettingsStore from "../../../settings/SettingsStore";
36+
import LabelledCheckbox from "../elements/LabelledCheckbox";
3637

3738
interface IProps {
3839
type?: RoomType;
@@ -129,6 +130,7 @@ export default class CreateRoomDialog extends React.Component<IProps, IState> {
129130

130131
if (this.state.joinRule === JoinRule.Knock) {
131132
opts.joinRule = JoinRule.Knock;
133+
createOpts.visibility = this.state.isPublic ? Visibility.Public : Visibility.Private;
132134
}
133135

134136
return opts;
@@ -215,6 +217,10 @@ export default class CreateRoomDialog extends React.Component<IProps, IState> {
215217
return result;
216218
};
217219

220+
private onIsPublicChange = (isPublic: boolean): void => {
221+
this.setState({ isPublic });
222+
};
223+
218224
private static validateRoomName = withValidation({
219225
rules: [
220226
{
@@ -298,6 +304,18 @@ export default class CreateRoomDialog extends React.Component<IProps, IState> {
298304
);
299305
}
300306

307+
let visibilitySection: JSX.Element | undefined;
308+
if (this.state.joinRule === JoinRule.Knock) {
309+
visibilitySection = (
310+
<LabelledCheckbox
311+
className="mx_CreateRoomDialog_labelledCheckbox"
312+
label={_t("Make this room visible in the public room directory.")}
313+
onChange={this.onIsPublicChange}
314+
value={this.state.isPublic}
315+
/>
316+
);
317+
}
318+
301319
let e2eeSection: JSX.Element | undefined;
302320
if (this.state.joinRule !== JoinRule.Public) {
303321
let microcopy: string;
@@ -383,6 +401,7 @@ export default class CreateRoomDialog extends React.Component<IProps, IState> {
383401
/>
384402

385403
{publicPrivateLabel}
404+
{visibilitySection}
386405
{e2eeSection}
387406
{aliasField}
388407
<details onToggle={this.onDetailsToggled} className="mx_CreateRoomDialog_details">

src/components/views/elements/LabelledCheckbox.tsx

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ limitations under the License.
1515
*/
1616

1717
import React from "react";
18+
import classnames from "classnames";
1819

1920
import StyledCheckbox from "./StyledCheckbox";
2021

@@ -29,11 +30,13 @@ interface IProps {
2930
disabled?: boolean;
3031
// The function to call when the value changes
3132
onChange(checked: boolean): void;
33+
// Optional CSS class to apply to the label
34+
className?: string;
3235
}
3336

34-
const LabelledCheckbox: React.FC<IProps> = ({ value, label, byline, disabled, onChange }) => {
37+
const LabelledCheckbox: React.FC<IProps> = ({ value, label, byline, disabled, onChange, className }) => {
3538
return (
36-
<label className="mx_LabelledCheckbox">
39+
<label className={classnames("mx_LabelledCheckbox", className)}>
3740
<StyledCheckbox disabled={disabled} checked={value} onChange={(e) => onChange(e.target.checked)} />
3841
<div className="mx_LabelledCheckbox_labels">
3942
<span className="mx_LabelledCheckbox_label">{label}</span>

src/i18n/strings/en_EN.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2646,6 +2646,7 @@
26462646
"Anyone will be able to find and join this room.": "Anyone will be able to find and join this room.",
26472647
"Only people invited will be able to find and join this room.": "Only people invited will be able to find and join this room.",
26482648
"Anyone can request to join, but admins or moderators need to grant access. You can change this later.": "Anyone can request to join, but admins or moderators need to grant access. You can change this later.",
2649+
"Make this room visible in the public room directory.": "Make this room visible in the public room directory.",
26492650
"You can't disable this later. The room will be encrypted but the embedded call will not.": "You can't disable this later. The room will be encrypted but the embedded call will not.",
26502651
"You can't disable this later. Bridges & most bots won't work yet.": "You can't disable this later. Bridges & most bots won't work yet.",
26512652
"Your server requires encryption to be enabled in private rooms.": "Your server requires encryption to be enabled in private rooms.",

test/components/views/dialogs/CreateRoomDialog-test.tsx

Lines changed: 57 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -210,45 +210,72 @@ describe("<CreateRoomDialog />", () => {
210210
});
211211

212212
describe("for a knock room", () => {
213-
it("should not have the option to create a knock room", async () => {
214-
jest.spyOn(SettingsStore, "getValue").mockReturnValue(false);
215-
getComponent();
216-
fireEvent.click(screen.getByLabelText("Room visibility"));
217-
218-
expect(screen.queryByRole("option", { name: "Ask to join" })).not.toBeInTheDocument();
213+
describe("when disabling feature", () => {
214+
it("should not have the option to create a knock room", async () => {
215+
jest.spyOn(SettingsStore, "getValue").mockReturnValue(false);
216+
getComponent();
217+
fireEvent.click(screen.getByLabelText("Room visibility"));
218+
expect(screen.queryByRole("option", { name: "Ask to join" })).not.toBeInTheDocument();
219+
});
219220
});
220221

221-
it("should create a knock room", async () => {
222-
jest.spyOn(SettingsStore, "getValue").mockImplementation((setting) => setting === "feature_ask_to_join");
222+
describe("when enabling feature", () => {
223223
const onFinished = jest.fn();
224-
getComponent({ onFinished });
225-
await flushPromises();
226-
227224
const roomName = "Test Room Name";
228-
fireEvent.change(screen.getByLabelText("Name"), { target: { value: roomName } });
229225

230-
fireEvent.click(screen.getByLabelText("Room visibility"));
231-
fireEvent.click(screen.getByRole("option", { name: "Ask to join" }));
226+
beforeEach(async () => {
227+
jest.spyOn(SettingsStore, "getValue").mockImplementation(
228+
(setting) => setting === "feature_ask_to_join",
229+
);
230+
getComponent({ onFinished });
231+
fireEvent.change(screen.getByLabelText("Name"), { target: { value: roomName } });
232+
fireEvent.click(screen.getByLabelText("Room visibility"));
233+
fireEvent.click(screen.getByRole("option", { name: "Ask to join" }));
234+
});
232235

233-
fireEvent.click(screen.getByText("Create room"));
234-
await flushPromises();
236+
it("should have a heading", () => {
237+
expect(screen.getByRole("heading")).toHaveTextContent("Create a room");
238+
});
235239

236-
expect(screen.getByText("Create a room")).toBeInTheDocument();
240+
it("should have a hint", () => {
241+
expect(
242+
screen.getByText(
243+
"Anyone can request to join, but admins or moderators need to grant access. You can change this later.",
244+
),
245+
).toBeInTheDocument();
246+
});
237247

238-
expect(
239-
screen.getByText(
240-
"Anyone can request to join, but admins or moderators need to grant access. You can change this later.",
241-
),
242-
).toBeInTheDocument();
248+
it("should create a private knock room", async () => {
249+
fireEvent.click(screen.getByText("Create room"));
250+
await flushPromises();
251+
expect(onFinished).toHaveBeenCalledWith(true, {
252+
createOpts: {
253+
name: roomName,
254+
visibility: Visibility.Private,
255+
},
256+
encryption: true,
257+
joinRule: JoinRule.Knock,
258+
parentSpace: undefined,
259+
roomType: undefined,
260+
});
261+
});
243262

244-
expect(onFinished).toHaveBeenCalledWith(true, {
245-
createOpts: {
246-
name: roomName,
247-
},
248-
encryption: true,
249-
joinRule: JoinRule.Knock,
250-
parentSpace: undefined,
251-
roomType: undefined,
263+
it("should create a public knock room", async () => {
264+
fireEvent.click(
265+
screen.getByRole("checkbox", { name: "Make this room visible in the public room directory." }),
266+
);
267+
fireEvent.click(screen.getByText("Create room"));
268+
await flushPromises();
269+
expect(onFinished).toHaveBeenCalledWith(true, {
270+
createOpts: {
271+
name: roomName,
272+
visibility: Visibility.Public,
273+
},
274+
encryption: true,
275+
joinRule: JoinRule.Knock,
276+
parentSpace: undefined,
277+
roomType: undefined,
278+
});
252279
});
253280
});
254281
});

test/components/views/elements/LabelledCheckbox-test.tsx

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,4 +89,16 @@ describe("<LabelledCheckbox />", () => {
8989
expect(checkbox).toBeChecked();
9090
expect(checkbox).toBeDisabled();
9191
});
92+
93+
it("should render with a custom class name", () => {
94+
const className = "some class name";
95+
const props: CompProps = {
96+
label: "Hello world",
97+
value: false,
98+
onChange: jest.fn(),
99+
className,
100+
};
101+
const { container } = render(getComponent(props));
102+
expect(container.firstElementChild?.className).toContain(className);
103+
});
92104
});

0 commit comments

Comments
 (0)