From 0059c7c1eec37ed16dd5b26f717b076257d2456c Mon Sep 17 00:00:00 2001 From: Charly Nguyen <1422657+charlynguyen@users.noreply.github.com> Date: Fri, 8 Sep 2023 03:00:48 +0200 Subject: [PATCH] Allow setting knock room directory visibility (#11529) * Allow setting knock room directory visibility Signed-off-by: Charly Nguyen * Apply PR feedback Signed-off-by: Charly Nguyen --------- Signed-off-by: Charly Nguyen --- res/css/views/settings/_JoinRuleSettings.pcss | 5 ++ .../views/settings/JoinRuleSettings.tsx | 45 ++++++++++- src/i18n/strings/en_EN.json | 3 +- .../views/settings/JoinRuleSettings-test.tsx | 75 ++++++++++++++++++- 4 files changed, 122 insertions(+), 6 deletions(-) diff --git a/res/css/views/settings/_JoinRuleSettings.pcss b/res/css/views/settings/_JoinRuleSettings.pcss index 62debe28a1..fcd89f7bd9 100644 --- a/res/css/views/settings/_JoinRuleSettings.pcss +++ b/res/css/views/settings/_JoinRuleSettings.pcss @@ -74,3 +74,8 @@ limitations under the License. } } } + +.mx_JoinRuleSettings_labelledCheckbox { + font: var(--cpd-font-body-md-regular); + margin-top: var(--cpd-space-2x); +} diff --git a/src/components/views/settings/JoinRuleSettings.tsx b/src/components/views/settings/JoinRuleSettings.tsx index b17f177c13..3c5fd814a5 100644 --- a/src/components/views/settings/JoinRuleSettings.tsx +++ b/src/components/views/settings/JoinRuleSettings.tsx @@ -14,8 +14,15 @@ See the License for the specific language governing permissions and limitations under the License. */ -import React, { ReactNode } from "react"; -import { IJoinRuleEventContent, JoinRule, RestrictedAllowType, Room, EventType } from "matrix-js-sdk/src/matrix"; +import React, { ReactNode, useEffect, useState } from "react"; +import { + IJoinRuleEventContent, + JoinRule, + RestrictedAllowType, + Room, + EventType, + Visibility, +} from "matrix-js-sdk/src/matrix"; import StyledRadioGroup, { IDefinition } from "../elements/StyledRadioGroup"; import { _t } from "../../../languageHandler"; @@ -34,6 +41,7 @@ import { Action } from "../../../dispatcher/actions"; import { ViewRoomPayload } from "../../../dispatcher/payloads/ViewRoomPayload"; import { doesRoomVersionSupport, PreferredRoomVersions } from "../../../utils/PreferredRoomVersions"; import SettingsStore from "../../../settings/SettingsStore"; +import LabelledCheckbox from "../elements/LabelledCheckbox"; export interface JoinRuleSettingsProps { room: Room; @@ -76,6 +84,22 @@ const JoinRuleSettings: React.FC = ({ ? content?.allow?.filter((o) => o.type === RestrictedAllowType.RoomMembership).map((o) => o.room_id) : 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 => { let selected = restrictedAllowRoomIds; if (!selected?.length && SpaceStore.instance.activeSpaceRoom) { @@ -297,7 +321,22 @@ const JoinRuleSettings: React.FC = ({ {preferredKnockVersion && upgradeRequiredPill} ), - description: _t("People cannot join unless access is granted."), + description: ( + <> + {_t("People cannot join unless access is granted.")} + + + ), }); } diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index 5be75775b8..b9126e6ac8 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -1492,6 +1492,8 @@ "Space members": "Space members", "Ask to join": "Ask to join", "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 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", @@ -2767,7 +2769,6 @@ "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.", "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. 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.", diff --git a/test/components/views/settings/JoinRuleSettings-test.tsx b/test/components/views/settings/JoinRuleSettings-test.tsx index 2b25da3711..0095bfa03d 100644 --- a/test/components/views/settings/JoinRuleSettings-test.tsx +++ b/test/components/views/settings/JoinRuleSettings-test.tsx @@ -15,7 +15,7 @@ limitations under the License. */ import React from "react"; -import { fireEvent, render, screen, within } from "@testing-library/react"; +import { act, fireEvent, render, screen, within } from "@testing-library/react"; import { EventType, GuestAccess, @@ -25,6 +25,8 @@ import { Room, ClientEvent, RoomMember, + MatrixError, + Visibility, } from "matrix-js-sdk/src/matrix"; import { defer, IDeferred } from "matrix-js-sdk/src/utils"; @@ -51,6 +53,8 @@ describe("", () => { getProfileInfo: jest.fn(), invite: jest.fn().mockResolvedValue(undefined), isRoomEncrypted: jest.fn().mockReturnValue(false), + getRoomDirectoryVisibility: jest.fn(), + setRoomDirectoryVisibility: jest.fn(), }); const roomId = "!room:server.org"; const newRoomId = "!roomUpgraded:server.org"; @@ -270,10 +274,77 @@ describe("", () => { }); }); + 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 () => { jest.spyOn(SettingsStore, "getValue").mockReturnValue(false); const room = new Room(newRoomId, client, userId); - getComponent({ room: room }); + setRoomStateEvents(room, PreferredRoomVersions.KnockRooms); + getComponent({ room }); expect(screen.queryByText("Ask to join")).not.toBeInTheDocument(); }); });