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

WIP: Implement Knocking #9093

Closed
wants to merge 8 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
79 changes: 79 additions & 0 deletions cypress/e2e/knocking/knocking.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
/*
Copyright 2022 The Matrix.org Foundation C.I.C.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

/// <reference types="cypress" />

import { SynapseInstance } from "../../plugins/synapsedocker";
import Chainable = Cypress.Chainable;

function openCreateRoomDialog(): Chainable<JQuery<HTMLElement>> {
cy.get('[aria-label="Add room"]').click();
cy.get('.mx_ContextualMenu [aria-label="New room"]').click();
return cy.get(".mx_CreateRoomDialog");
}

describe("Knocking", () => {
let synapse: SynapseInstance;

beforeEach(() => {
cy.startSynapse("default").then(data => {
synapse = data;

cy.initTestUser(synapse, "Tom");
});
});

afterEach(() => {
cy.stopSynapse(synapse);
});

it("should be able to create a room with knock JoinRule", () => {
// Enables labs flag feature
cy.enableLabsFeature("feature_knocking");
const name = "Test room 1";
const topic = "This is a test room";

// Create a room with knock JoinRule
openCreateRoomDialog().within(() => {
cy.get('[label="Name"]').type(name);
cy.get('[label="Topic (optional)"]').type(topic);
Comment on lines +48 to +49
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we tend to use aria-label for selecting usually but it probably doesn't matter much

cy.get(".mx_JoinRuleDropdown").click();
cy.get(".mx_JoinRuleDropdown_knock").click();
cy.startMeasuring("from-submit-to-room");
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure what is the purpose of this? I am not sure if it makes sense to have a performance test specific to knocking?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure what is the purpose of this? I am not sure if it makes sense to have a performance test specific to knocking?

I thought since we are using it in createRoom.spec.ts it has some importance or something like that

cy.get(".mx_Dialog_primary").click();
});

// The room settings initially are set to Ask to join
cy.openRoomSettings("Security & Privacy");
cy.closeDialog();

//Check if the room settings are visible if labs flag is disabled
cy.openUserSettings("Labs").within(() => {
//disables labs flag feature
cy.get("[aria-label='Knocking']").click();
// cy.disableLabsFeature("feature_knocking");
});
cy.closeDialog();

//the default joinRule is set to Private (invite only) when the labs flag is disabled
cy.openRoomSettings("Security & Privacy");
cy.closeDialog();

// Click the expand link button to get more detailed view
cy.get(".mx_GenericEventListSummary_toggle[aria-expanded=false]").click();

cy.stopMeasuring("from-submit-to-room");
cy.get(".mx_RoomHeader_nametext").contains(name);
cy.get(".mx_RoomHeader_topic").contains(topic);
});
});
8 changes: 8 additions & 0 deletions cypress/support/labs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ declare global {
* @param feature labsFeature to enable (e.g. "feature_spotlight")
*/
enableLabsFeature(feature: string): Chainable<null>;
disableLabsFeature(feature: string): Chainable<null>;
}
}
}
Expand All @@ -38,5 +39,12 @@ Cypress.Commands.add("enableLabsFeature", (feature: string): Chainable<null> =>
}).then(() => null);
});

Cypress.Commands.add("disableLabsFeature", (feature: string): Chainable<null> => {
return cy.window({ log: false }).then(win => {
win.localStorage.removeItem(`mx_labs_feature_${feature}`);
win.localStorage.setItem(`mx_labs_feature_${feature}`, "false");
}).then(() => null);
});

// Needed to make this file a module
export { };
5 changes: 5 additions & 0 deletions res/css/views/dialogs/_JoinRuleDropdown.pcss
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,11 @@ limitations under the License.
mask-size: contain;
}

.mx_JoinRuleDropdown_knock::before {
mask-image: url('$(res)/img/element-icons/knocking.svg');
mask-size: contain;
}

.mx_JoinRuleDropdown_public::before {
mask-image: url('$(res)/img/globe.svg');
mask-size: 12px;
Expand Down
3 changes: 3 additions & 0 deletions res/img/element-icons/knocking.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 4 additions & 0 deletions src/TextForEvent.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -235,6 +235,10 @@ function textForJoinRulesEvent(ev: MatrixEvent, allowJSX: boolean): () => Render
return () => _t('%(senderDisplayName)s made the room invite only.', {
senderDisplayName,
});
case JoinRule.Knock:
return () => _t('%(senderDisplayName)s made the room knock only.', {
senderDisplayName,
});
case JoinRule.Restricted:
if (allowJSX) {
return () => <span>
Expand Down
17 changes: 14 additions & 3 deletions src/components/views/dialogs/CreateRoomDialog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import { Room } from "matrix-js-sdk/src/models/room";
import { RoomType } from "matrix-js-sdk/src/@types/event";
import { JoinRule, Preset, Visibility } from "matrix-js-sdk/src/@types/partials";

import SettingsStore from '../../../settings/SettingsStore';
import SdkConfig from '../../../SdkConfig';
import withValidation, { IFieldState } from '../elements/Validation';
import { _t } from '../../../languageHandler';
Expand Down Expand Up @@ -59,13 +60,15 @@ interface IState {

export default class CreateRoomDialog extends React.Component<IProps, IState> {
private readonly supportsRestricted: boolean;
private readonly knockingEnabled: boolean;
private nameField = createRef<Field>();
private aliasField = createRef<RoomAliasField>();

constructor(props) {
super(props);

this.supportsRestricted = !!this.props.parentSpace;
this.knockingEnabled = SettingsStore.getValue("feature_knocking");

let joinRule = JoinRule.Invite;
if (this.props.defaultPublic) {
Expand Down Expand Up @@ -120,6 +123,9 @@ export default class CreateRoomDialog extends React.Component<IProps, IState> {
opts.joinRule = JoinRule.Restricted;
}

if (this.state.joinRule === JoinRule.Knock) {
opts.joinRule = JoinRule.Knock;
}
return opts;
}

Expand Down Expand Up @@ -265,9 +271,13 @@ export default class CreateRoomDialog extends React.Component<IProps, IState> {
</p>;
} else if (this.state.joinRule === JoinRule.Invite) {
publicPrivateLabel = <p>
{ _t(
"Only people invited will be able to find and join this room.",
) }
{ _t("Only people invited will be able to find and join this room.") }
&nbsp;
{ _t("You can change this at any time from room settings.") }
turt2live marked this conversation as resolved.
Show resolved Hide resolved
</p>;
} else if (this.state.joinRule === JoinRule.Knock && this.knockingEnabled) {
publicPrivateLabel = <p>
{ _t("This room type requires users to be granted access in order to join.") }
&nbsp;
{ _t("You can change this at any time from room settings.") }
</p>;
Expand Down Expand Up @@ -349,6 +359,7 @@ export default class CreateRoomDialog extends React.Component<IProps, IState> {
<JoinRuleDropdown
label={_t("Room visibility")}
labelInvite={_t("Private room (invite only)")}
labelKnock={this.knockingEnabled ? _t("Ask to join (Anyone can knock to join)") : undefined}
labelPublic={_t("Public room")}
labelRestricted={this.supportsRestricted ? _t("Visible to space members") : undefined}
value={this.state.joinRule}
Expand Down
6 changes: 6 additions & 0 deletions src/components/views/elements/JoinRuleDropdown.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ interface IProps {
label: string;
width?: number;
labelInvite: string;
labelKnock?: string;
labelPublic: string;
labelRestricted?: string; // if omitted then this option will be hidden, e.g if unsupported
onChange(value: JoinRule): void;
Expand All @@ -32,6 +33,7 @@ interface IProps {
const JoinRuleDropdown = ({
label,
labelInvite,
labelKnock,
labelPublic,
labelRestricted,
value,
Expand All @@ -51,6 +53,10 @@ const JoinRuleDropdown = ({
options.unshift(<div key={JoinRule.Restricted} className="mx_JoinRuleDropdown_restricted">
{ labelRestricted }
</div>);
} else if (labelKnock) {
options.unshift(<div key={JoinRule.Knock} className="mx_JoinRuleDropdown_knock">
{ labelKnock }
</div>);
}

return <Dropdown
Expand Down
21 changes: 21 additions & 0 deletions src/components/views/settings/JoinRuleSettings.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import { _t } from "../../../languageHandler";
import AccessibleButton from "../elements/AccessibleButton";
import RoomAvatar from "../avatars/RoomAvatar";
import SpaceStore from "../../../stores/spaces/SpaceStore";
import SettingsStore from "../../../settings/SettingsStore";
import { MatrixClientPeg } from "../../../MatrixClientPeg";
import Modal from "../../../Modal";
import ManageRestrictedJoinRuleDialog from "../dialogs/ManageRestrictedJoinRuleDialog";
Expand All @@ -49,13 +50,17 @@ interface IProps {
const JoinRuleSettings = ({ room, promptUpgrade, aliasWarning, onError, beforeChange, closeSettingsFn }: IProps) => {
const cli = room.client;

const roomSupportsKnocking = doesRoomVersionSupport(room.getVersion(), PreferredRoomVersions.KnockingRooms);

const roomSupportsRestricted = doesRoomVersionSupport(room.getVersion(), PreferredRoomVersions.RestrictedRooms);
const preferredRestrictionVersion = !roomSupportsRestricted && promptUpgrade
? PreferredRoomVersions.RestrictedRooms
: undefined;

const disabled = !room.currentState.mayClientSendStateEvent(EventType.RoomJoinRules, cli);

const knockingEnabled = SettingsStore.getValue("feature_knocking");

const [content, setContent] = useLocalEcho<IJoinRuleEventContent>(
() => room.currentState.getStateEvents(EventType.RoomJoinRules, "")?.getContent(),
content => cli.sendStateEvent(room.roomId, EventType.RoomJoinRules, content, ""),
Expand Down Expand Up @@ -89,6 +94,11 @@ const JoinRuleSettings = ({ room, promptUpgrade, aliasWarning, onError, beforeCh
label: _t("Private (invite only)"),
description: _t("Only invited people can join."),
checked: joinRule === JoinRule.Invite || (joinRule === JoinRule.Restricted && !restrictedAllowRoomIds?.length),
}, {
value: JoinRule.Knock,
label: _t("Ask to join"),
description: _t("Requires users to be granted access in order to join"),
checked: joinRule === JoinRule.Knock && knockingEnabled && roomSupportsKnocking,
}, {
value: JoinRule.Public,
label: _t("Public"),
Expand All @@ -98,6 +108,17 @@ const JoinRuleSettings = ({ room, promptUpgrade, aliasWarning, onError, beforeCh
</>,
}];

if (!knockingEnabled || !roomSupportsKnocking) {
// removes the knock option if the room isn't compatible for the same
for (let i = 0; i < definitions.length; i++) {
if (definitions[i].value === JoinRule.Knock) {
definitions.splice(i, 1);
break;
}
}
definitions[0].checked = true; //makes invite only room as default
}

if (roomSupportsRestricted || preferredRestrictionVersion || joinRule === JoinRule.Restricted) {
let upgradeRequiredPill;
if (preferredRestrictionVersion) {
Expand Down
7 changes: 7 additions & 0 deletions src/i18n/strings/en_EN.json
Original file line number Diff line number Diff line change
Expand Up @@ -504,6 +504,7 @@
"%(senderDisplayName)s upgraded this room.": "%(senderDisplayName)s upgraded this room.",
"%(senderDisplayName)s made the room public to whoever knows the link.": "%(senderDisplayName)s made the room public to whoever knows the link.",
"%(senderDisplayName)s made the room invite only.": "%(senderDisplayName)s made the room invite only.",
"%(senderDisplayName)s made the room knock only.": "%(senderDisplayName)s made the room knock only.",
"%(senderDisplayName)s changed who can join this room. <a>View settings</a>.": "%(senderDisplayName)s changed who can join this room. <a>View settings</a>.",
"%(senderDisplayName)s changed who can join this room.": "%(senderDisplayName)s changed who can join this room.",
"%(senderDisplayName)s changed the join rule to %(rule)s": "%(senderDisplayName)s changed the join rule to %(rule)s",
Expand Down Expand Up @@ -884,6 +885,8 @@
"Report to moderators prototype. In rooms that support moderation, the `report` button will let you report abuse to room moderators": "Report to moderators prototype. In rooms that support moderation, the `report` button will let you report abuse to room moderators",
"Render LaTeX maths in messages": "Render LaTeX maths in messages",
"Message Pinning": "Message Pinning",
"Knocking": "Knocking",
"Let users knock on a room to join it.": "Let users knock on a room to join it.",
"Threaded messaging": "Threaded messaging",
"Keep discussions organised with threads.": "Keep discussions organised with threads.",
"Threads help keep conversations on-topic and easy to track. <a>Learn more</a>.": "Threads help keep conversations on-topic and easy to track. <a>Learn more</a>.",
Expand Down Expand Up @@ -1326,6 +1329,8 @@
"Integration manager": "Integration manager",
"Private (invite only)": "Private (invite only)",
"Only invited people can join.": "Only invited people can join.",
"Ask to join": "Ask to join",
"Requires users to be granted access in order to join": "Requires users to be granted access in order to join",
"Anyone can find and join.": "Anyone can find and join.",
"Upgrade required": "Upgrade required",
"& %(count)s more|other": "& %(count)s more",
Expand Down Expand Up @@ -2555,6 +2560,7 @@
"Anyone will be able to find and join this room, not just members of <SpaceName/>.": "Anyone will be able to find and join this room, not just members of <SpaceName/>.",
"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.",
"This room type requires users to be granted access in order to join.": "This room type requires users to be granted access in order to join.",
"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.",
Expand All @@ -2568,6 +2574,7 @@
"Topic (optional)": "Topic (optional)",
"Room visibility": "Room visibility",
"Private room (invite only)": "Private room (invite only)",
"Ask to join (Anyone can knock to join)": "Ask to join (Anyone can knock to join)",
"Visible to space members": "Visible to space members",
"Block anyone not part of %(serverName)s from ever joining this room.": "Block anyone not part of %(serverName)s from ever joining this room.",
"Create video room": "Create video room",
Expand Down
8 changes: 8 additions & 0 deletions src/settings/Settings.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -256,6 +256,14 @@ export const SETTINGS: {[setting: string]: ISetting} = {
supportedLevels: LEVELS_FEATURE,
default: false,
},
"feature_knocking": {
isFeature: true,
labsGroup: LabGroup.Rooms,
supportedLevels: LEVELS_FEATURE,
displayName: _td("Knocking"),
description: _td("Let users knock on a room to join it."),
default: false,
},
"feature_thread": {
isFeature: true,
labsGroup: LabGroup.Messaging,
Expand Down
5 changes: 5 additions & 0 deletions src/utils/PreferredRoomVersions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,11 @@ export class PreferredRoomVersions {
*/
public static readonly RestrictedRooms = "9";

/**
* The room version to use when creating "knocking" rooms.
*/
public static readonly KnockingRooms = "9";

private constructor() {
// readonly, static, class
}
Expand Down