Allow setting knock room directory visibility (#11529)

* Allow setting knock room directory visibility

Signed-off-by: Charly Nguyen <charly.nguyen@nordeck.net>

* Apply PR feedback

Signed-off-by: Charly Nguyen <charly.nguyen@nordeck.net>

---------

Signed-off-by: Charly Nguyen <charly.nguyen@nordeck.net>
This commit is contained in:
Charly Nguyen 2023-09-08 03:00:48 +02:00 committed by GitHub
parent 77b681eed9
commit 0059c7c1ee
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 122 additions and 6 deletions

View file

@ -74,3 +74,8 @@ limitations under the License.
} }
} }
} }
.mx_JoinRuleSettings_labelledCheckbox {
font: var(--cpd-font-body-md-regular);
margin-top: var(--cpd-space-2x);
}

View file

@ -14,8 +14,15 @@ See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
*/ */
import React, { ReactNode } from "react"; import React, { ReactNode, useEffect, useState } from "react";
import { IJoinRuleEventContent, JoinRule, RestrictedAllowType, Room, EventType } from "matrix-js-sdk/src/matrix"; import {
IJoinRuleEventContent,
JoinRule,
RestrictedAllowType,
Room,
EventType,
Visibility,
} from "matrix-js-sdk/src/matrix";
import StyledRadioGroup, { IDefinition } from "../elements/StyledRadioGroup"; import StyledRadioGroup, { IDefinition } from "../elements/StyledRadioGroup";
import { _t } from "../../../languageHandler"; import { _t } from "../../../languageHandler";
@ -34,6 +41,7 @@ import { Action } from "../../../dispatcher/actions";
import { ViewRoomPayload } from "../../../dispatcher/payloads/ViewRoomPayload"; import { ViewRoomPayload } from "../../../dispatcher/payloads/ViewRoomPayload";
import { doesRoomVersionSupport, PreferredRoomVersions } from "../../../utils/PreferredRoomVersions"; import { doesRoomVersionSupport, PreferredRoomVersions } from "../../../utils/PreferredRoomVersions";
import SettingsStore from "../../../settings/SettingsStore"; import SettingsStore from "../../../settings/SettingsStore";
import LabelledCheckbox from "../elements/LabelledCheckbox";
export interface JoinRuleSettingsProps { export interface JoinRuleSettingsProps {
room: Room; room: Room;
@ -76,6 +84,22 @@ const JoinRuleSettings: React.FC<JoinRuleSettingsProps> = ({
? content?.allow?.filter((o) => o.type === RestrictedAllowType.RoomMembership).map((o) => o.room_id) ? content?.allow?.filter((o) => o.type === RestrictedAllowType.RoomMembership).map((o) => o.room_id)
: undefined; : undefined;
const [isPublicKnockRoom, setIsPublicKnockRoom] = useState(false);
useEffect(() => {
if (joinRule === JoinRule.Knock) {
cli.getRoomDirectoryVisibility(room.roomId)
.then(({ visibility }) => setIsPublicKnockRoom(visibility === Visibility.Public))
.catch(onError);
}
}, [cli, joinRule, onError, room.roomId]);
const onIsPublicKnockRoomChange = (checked: boolean): void => {
cli.setRoomDirectoryVisibility(room.roomId, checked ? Visibility.Public : Visibility.Private)
.then(() => setIsPublicKnockRoom(checked))
.catch(onError);
};
const editRestrictedRoomIds = async (): Promise<string[] | undefined> => { const editRestrictedRoomIds = async (): Promise<string[] | undefined> => {
let selected = restrictedAllowRoomIds; let selected = restrictedAllowRoomIds;
if (!selected?.length && SpaceStore.instance.activeSpaceRoom) { if (!selected?.length && SpaceStore.instance.activeSpaceRoom) {
@ -297,7 +321,22 @@ const JoinRuleSettings: React.FC<JoinRuleSettingsProps> = ({
{preferredKnockVersion && upgradeRequiredPill} {preferredKnockVersion && upgradeRequiredPill}
</> </>
), ),
description: _t("People cannot join unless access is granted."), description: (
<>
{_t("People cannot join unless access is granted.")}
<LabelledCheckbox
className="mx_JoinRuleSettings_labelledCheckbox"
disabled={joinRule !== JoinRule.Knock}
label={
room.isSpaceRoom()
? _t("Make this space visible in the public room directory.")
: _t("Make this room visible in the public room directory.")
}
onChange={onIsPublicKnockRoomChange}
value={isPublicKnockRoom}
/>
</>
),
}); });
} }

View file

@ -1492,6 +1492,8 @@
"Space members": "Space members", "Space members": "Space members",
"Ask to join": "Ask to join", "Ask to join": "Ask to join",
"People cannot join unless access is granted.": "People cannot join unless access is granted.", "People cannot join unless access is granted.": "People cannot join unless access is granted.",
"Make this space visible in the public room directory.": "Make this space visible in the public room directory.",
"Make this room visible in the public room directory.": "Make this room visible in the public room directory.",
"This room is in some spaces you're not an admin of. In those spaces, the old room will still be shown, but people will be prompted to join the new one.": "This room is in some spaces you're not an admin of. In those spaces, the old room will still be shown, but people will be prompted to join the new one.", "This room is in some spaces you're not an admin of. In those spaces, the old room will still be shown, but people will be prompted to join the new one.": "This room is in some spaces you're not an admin of. In those spaces, the old room will still be shown, but people will be prompted to join the new one.",
"This upgrade will allow members of selected spaces access to this room without an invite.": "This upgrade will allow members of selected spaces access to this room without an invite.", "This upgrade will allow members of selected spaces access to this room without an invite.": "This upgrade will allow members of selected spaces access to this room without an invite.",
"Messages containing keywords": "Messages containing keywords", "Messages containing keywords": "Messages containing keywords",
@ -2767,7 +2769,6 @@
"Anyone will be able to find and join this room.": "Anyone will be able to find and join this room.", "Anyone will be able to find and join this room.": "Anyone will be able to find and join this room.",
"Only people invited will be able to find and join this room.": "Only people invited will be able to find and join this room.", "Only people invited will be able to find and join this room.": "Only people invited will be able to find and join this room.",
"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.", "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.",
"Make this room visible in the public room directory.": "Make this room visible in the public room directory.",
"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.", "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.",
"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.", "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.",
"Your server requires encryption to be enabled in private rooms.": "Your server requires encryption to be enabled in private rooms.", "Your server requires encryption to be enabled in private rooms.": "Your server requires encryption to be enabled in private rooms.",

View file

@ -15,7 +15,7 @@ limitations under the License.
*/ */
import React from "react"; import React from "react";
import { fireEvent, render, screen, within } from "@testing-library/react"; import { act, fireEvent, render, screen, within } from "@testing-library/react";
import { import {
EventType, EventType,
GuestAccess, GuestAccess,
@ -25,6 +25,8 @@ import {
Room, Room,
ClientEvent, ClientEvent,
RoomMember, RoomMember,
MatrixError,
Visibility,
} from "matrix-js-sdk/src/matrix"; } from "matrix-js-sdk/src/matrix";
import { defer, IDeferred } from "matrix-js-sdk/src/utils"; import { defer, IDeferred } from "matrix-js-sdk/src/utils";
@ -51,6 +53,8 @@ describe("<JoinRuleSettings />", () => {
getProfileInfo: jest.fn(), getProfileInfo: jest.fn(),
invite: jest.fn().mockResolvedValue(undefined), invite: jest.fn().mockResolvedValue(undefined),
isRoomEncrypted: jest.fn().mockReturnValue(false), isRoomEncrypted: jest.fn().mockReturnValue(false),
getRoomDirectoryVisibility: jest.fn(),
setRoomDirectoryVisibility: jest.fn(),
}); });
const roomId = "!room:server.org"; const roomId = "!room:server.org";
const newRoomId = "!roomUpgraded:server.org"; const newRoomId = "!roomUpgraded:server.org";
@ -270,10 +274,77 @@ describe("<JoinRuleSettings />", () => {
}); });
}); });
describe("knock rooms directory visibility", () => {
const getCheckbox = () => screen.getByRole("checkbox");
let room: Room;
beforeEach(() => (room = new Room(roomId, client, userId)));
describe("when join rule is knock", () => {
beforeEach(() => setRoomStateEvents(room, PreferredRoomVersions.KnockRooms, JoinRule.Knock));
it("should set the visibility to public", async () => {
jest.spyOn(client, "getRoomDirectoryVisibility").mockResolvedValue({ visibility: Visibility.Private });
jest.spyOn(client, "setRoomDirectoryVisibility").mockResolvedValue({});
getComponent({ room });
fireEvent.click(getCheckbox());
await act(async () => await flushPromises());
expect(client.setRoomDirectoryVisibility).toHaveBeenCalledWith(roomId, Visibility.Public);
expect(getCheckbox()).toBeChecked();
});
it("should set the visibility to private", async () => {
jest.spyOn(client, "getRoomDirectoryVisibility").mockResolvedValue({ visibility: Visibility.Public });
jest.spyOn(client, "setRoomDirectoryVisibility").mockResolvedValue({});
getComponent({ room });
await act(async () => await flushPromises());
fireEvent.click(getCheckbox());
await act(async () => await flushPromises());
expect(client.setRoomDirectoryVisibility).toHaveBeenCalledWith(roomId, Visibility.Private);
expect(getCheckbox()).not.toBeChecked();
});
it("should call onError if setting visibility fails", async () => {
const error = new MatrixError();
jest.spyOn(client, "getRoomDirectoryVisibility").mockResolvedValue({ visibility: Visibility.Private });
jest.spyOn(client, "setRoomDirectoryVisibility").mockRejectedValue(error);
getComponent({ room });
fireEvent.click(getCheckbox());
await act(async () => await flushPromises());
expect(getCheckbox()).not.toBeChecked();
expect(defaultProps.onError).toHaveBeenCalledWith(error);
});
});
describe("when the room version is unsupported and upgrade is enabled", () => {
it("should disable the checkbox", () => {
setRoomStateEvents(room, "6", JoinRule.Invite);
getComponent({ promptUpgrade: true, room });
expect(getCheckbox()).toBeDisabled();
});
});
describe("when join rule is not knock", () => {
beforeEach(() => {
setRoomStateEvents(room, PreferredRoomVersions.KnockRooms, JoinRule.Invite);
getComponent({ room });
});
it("should disable the checkbox", () => {
expect(getCheckbox()).toBeDisabled();
});
it("should set the visibility to private by default", () => {
expect(getCheckbox()).not.toBeChecked();
});
});
});
it("should not show knock room join rule", async () => { it("should not show knock room join rule", async () => {
jest.spyOn(SettingsStore, "getValue").mockReturnValue(false); jest.spyOn(SettingsStore, "getValue").mockReturnValue(false);
const room = new Room(newRoomId, client, userId); const room = new Room(newRoomId, client, userId);
getComponent({ room: room }); setRoomStateEvents(room, PreferredRoomVersions.KnockRooms);
getComponent({ room });
expect(screen.queryByText("Ask to join")).not.toBeInTheDocument(); expect(screen.queryByText("Ask to join")).not.toBeInTheDocument();
}); });
}); });