Replace MSC3244 support with in-client room version checking (#9018)
* Replace MSC3244 support with in-client room version checking * Fix irrelevant ternary * It helps to use Jest correctly
This commit is contained in:
parent
bd8949872d
commit
644b841591
7 changed files with 120 additions and 37 deletions
|
@ -30,7 +30,6 @@ import RoomAliasField from "../elements/RoomAliasField";
|
||||||
import LabelledToggleSwitch from "../elements/LabelledToggleSwitch";
|
import LabelledToggleSwitch from "../elements/LabelledToggleSwitch";
|
||||||
import DialogButtons from "../elements/DialogButtons";
|
import DialogButtons from "../elements/DialogButtons";
|
||||||
import BaseDialog from "../dialogs/BaseDialog";
|
import BaseDialog from "../dialogs/BaseDialog";
|
||||||
import SpaceStore from "../../../stores/spaces/SpaceStore";
|
|
||||||
import JoinRuleDropdown from "../elements/JoinRuleDropdown";
|
import JoinRuleDropdown from "../elements/JoinRuleDropdown";
|
||||||
import { getKeyBindingsManager } from "../../../KeyBindingsManager";
|
import { getKeyBindingsManager } from "../../../KeyBindingsManager";
|
||||||
import { KeyBindingAction } from "../../../accessibility/KeyboardShortcuts";
|
import { KeyBindingAction } from "../../../accessibility/KeyboardShortcuts";
|
||||||
|
@ -66,7 +65,7 @@ export default class CreateRoomDialog extends React.Component<IProps, IState> {
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
super(props);
|
super(props);
|
||||||
|
|
||||||
this.supportsRestricted = this.props.parentSpace && !!SpaceStore.instance.restrictedJoinRuleSupport?.preferred;
|
this.supportsRestricted = !!this.props.parentSpace;
|
||||||
|
|
||||||
let joinRule = JoinRule.Invite;
|
let joinRule = JoinRule.Invite;
|
||||||
if (this.props.defaultPublic) {
|
if (this.props.defaultPublic) {
|
||||||
|
|
|
@ -26,7 +26,6 @@ import MatrixClientContext from "../../../contexts/MatrixClientContext";
|
||||||
import { BetaPill } from "../beta/BetaCard";
|
import { BetaPill } from "../beta/BetaCard";
|
||||||
import Field from "../elements/Field";
|
import Field from "../elements/Field";
|
||||||
import RoomAliasField from "../elements/RoomAliasField";
|
import RoomAliasField from "../elements/RoomAliasField";
|
||||||
import SpaceStore from "../../../stores/spaces/SpaceStore";
|
|
||||||
import { createSpace, SpaceCreateForm } from "../spaces/SpaceCreateMenu";
|
import { createSpace, SpaceCreateForm } from "../spaces/SpaceCreateMenu";
|
||||||
import { SubspaceSelector } from "./AddExistingToSpaceDialog";
|
import { SubspaceSelector } from "./AddExistingToSpaceDialog";
|
||||||
import JoinRuleDropdown from "../elements/JoinRuleDropdown";
|
import JoinRuleDropdown from "../elements/JoinRuleDropdown";
|
||||||
|
@ -48,14 +47,10 @@ const CreateSubspaceDialog: React.FC<IProps> = ({ space, onAddExistingSpaceClick
|
||||||
const [avatar, setAvatar] = useState<File>(null);
|
const [avatar, setAvatar] = useState<File>(null);
|
||||||
const [topic, setTopic] = useState<string>("");
|
const [topic, setTopic] = useState<string>("");
|
||||||
|
|
||||||
const supportsRestricted = !!SpaceStore.instance.restrictedJoinRuleSupport?.preferred;
|
|
||||||
|
|
||||||
const spaceJoinRule = space.getJoinRule();
|
const spaceJoinRule = space.getJoinRule();
|
||||||
let defaultJoinRule = JoinRule.Invite;
|
let defaultJoinRule = JoinRule.Restricted;
|
||||||
if (spaceJoinRule === JoinRule.Public) {
|
if (spaceJoinRule === JoinRule.Public) {
|
||||||
defaultJoinRule = JoinRule.Public;
|
defaultJoinRule = JoinRule.Public;
|
||||||
} else if (supportsRestricted) {
|
|
||||||
defaultJoinRule = JoinRule.Restricted;
|
|
||||||
}
|
}
|
||||||
const [joinRule, setJoinRule] = useState<JoinRule>(defaultJoinRule);
|
const [joinRule, setJoinRule] = useState<JoinRule>(defaultJoinRule);
|
||||||
|
|
||||||
|
@ -150,7 +145,7 @@ const CreateSubspaceDialog: React.FC<IProps> = ({ space, onAddExistingSpaceClick
|
||||||
label={_t("Space visibility")}
|
label={_t("Space visibility")}
|
||||||
labelInvite={_t("Private space (invite only)")}
|
labelInvite={_t("Private space (invite only)")}
|
||||||
labelPublic={_t("Public space")}
|
labelPublic={_t("Public space")}
|
||||||
labelRestricted={supportsRestricted ? _t("Visible to space members") : undefined}
|
labelRestricted={_t("Visible to space members")}
|
||||||
width={478}
|
width={478}
|
||||||
value={joinRule}
|
value={joinRule}
|
||||||
onChange={setJoinRule}
|
onChange={setJoinRule}
|
||||||
|
|
|
@ -35,6 +35,7 @@ import dis from "../../../dispatcher/dispatcher";
|
||||||
import { ROOM_SECURITY_TAB } from "../dialogs/RoomSettingsDialog";
|
import { ROOM_SECURITY_TAB } from "../dialogs/RoomSettingsDialog";
|
||||||
import { Action } from "../../../dispatcher/actions";
|
import { Action } from "../../../dispatcher/actions";
|
||||||
import { ViewRoomPayload } from "../../../dispatcher/payloads/ViewRoomPayload";
|
import { ViewRoomPayload } from "../../../dispatcher/payloads/ViewRoomPayload";
|
||||||
|
import { doesRoomVersionSupport, PreferredRoomVersions } from "../../../utils/PreferredRoomVersions";
|
||||||
|
|
||||||
interface IProps {
|
interface IProps {
|
||||||
room: Room;
|
room: Room;
|
||||||
|
@ -48,11 +49,9 @@ interface IProps {
|
||||||
const JoinRuleSettings = ({ room, promptUpgrade, aliasWarning, onError, beforeChange, closeSettingsFn }: IProps) => {
|
const JoinRuleSettings = ({ room, promptUpgrade, aliasWarning, onError, beforeChange, closeSettingsFn }: IProps) => {
|
||||||
const cli = room.client;
|
const cli = room.client;
|
||||||
|
|
||||||
const restrictedRoomCapabilities = SpaceStore.instance.restrictedJoinRuleSupport;
|
const roomSupportsRestricted = doesRoomVersionSupport(room.getVersion(), PreferredRoomVersions.RestrictedRooms);
|
||||||
const roomSupportsRestricted = Array.isArray(restrictedRoomCapabilities?.support)
|
|
||||||
&& restrictedRoomCapabilities.support.includes(room.getVersion());
|
|
||||||
const preferredRestrictionVersion = !roomSupportsRestricted && promptUpgrade
|
const preferredRestrictionVersion = !roomSupportsRestricted && promptUpgrade
|
||||||
? restrictedRoomCapabilities?.preferred
|
? PreferredRoomVersions.RestrictedRooms
|
||||||
: undefined;
|
: undefined;
|
||||||
|
|
||||||
const disabled = !room.currentState.mayClientSendStateEvent(EventType.RoomJoinRules, cli);
|
const disabled = !room.currentState.mayClientSendStateEvent(EventType.RoomJoinRules, cli);
|
||||||
|
|
|
@ -45,6 +45,7 @@ import { ViewRoomPayload } from "./dispatcher/payloads/ViewRoomPayload";
|
||||||
import { findDMForUser } from "./utils/direct-messages";
|
import { findDMForUser } from "./utils/direct-messages";
|
||||||
import { privateShouldBeEncrypted } from "./utils/rooms";
|
import { privateShouldBeEncrypted } from "./utils/rooms";
|
||||||
import { waitForMember } from "./utils/membership";
|
import { waitForMember } from "./utils/membership";
|
||||||
|
import { PreferredRoomVersions } from "./utils/PreferredRoomVersions";
|
||||||
|
|
||||||
// we define a number of interfaces which take their names from the js-sdk
|
// we define a number of interfaces which take their names from the js-sdk
|
||||||
/* eslint-disable camelcase */
|
/* eslint-disable camelcase */
|
||||||
|
@ -191,20 +192,18 @@ export default async function createRoom(opts: IOpts): Promise<string | null> {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (opts.joinRule === JoinRule.Restricted) {
|
if (opts.joinRule === JoinRule.Restricted) {
|
||||||
if (SpaceStore.instance.restrictedJoinRuleSupport?.preferred) {
|
createOpts.room_version = PreferredRoomVersions.RestrictedRooms;
|
||||||
createOpts.room_version = SpaceStore.instance.restrictedJoinRuleSupport.preferred;
|
|
||||||
|
|
||||||
createOpts.initial_state.push({
|
createOpts.initial_state.push({
|
||||||
type: EventType.RoomJoinRules,
|
type: EventType.RoomJoinRules,
|
||||||
content: {
|
content: {
|
||||||
"join_rule": JoinRule.Restricted,
|
"join_rule": JoinRule.Restricted,
|
||||||
"allow": [{
|
"allow": [{
|
||||||
"type": RestrictedAllowType.RoomMembership,
|
"type": RestrictedAllowType.RoomMembership,
|
||||||
"room_id": opts.parentSpace.roomId,
|
"room_id": opts.parentSpace.roomId,
|
||||||
}],
|
}],
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -18,7 +18,7 @@ import { ListIteratee, Many, sortBy } from "lodash";
|
||||||
import { EventType, RoomType } from "matrix-js-sdk/src/@types/event";
|
import { EventType, RoomType } from "matrix-js-sdk/src/@types/event";
|
||||||
import { Room, RoomEvent } from "matrix-js-sdk/src/models/room";
|
import { Room, RoomEvent } from "matrix-js-sdk/src/models/room";
|
||||||
import { MatrixEvent } from "matrix-js-sdk/src/models/event";
|
import { MatrixEvent } from "matrix-js-sdk/src/models/event";
|
||||||
import { ClientEvent, IRoomCapability } from "matrix-js-sdk/src/client";
|
import { ClientEvent } from "matrix-js-sdk/src/client";
|
||||||
import { logger } from "matrix-js-sdk/src/logger";
|
import { logger } from "matrix-js-sdk/src/logger";
|
||||||
import { RoomMember } from "matrix-js-sdk/src/models/room-member";
|
import { RoomMember } from "matrix-js-sdk/src/models/room-member";
|
||||||
import { RoomStateEvent } from "matrix-js-sdk/src/models/room-state";
|
import { RoomStateEvent } from "matrix-js-sdk/src/models/room-state";
|
||||||
|
@ -132,7 +132,6 @@ export class SpaceStoreClass extends AsyncStoreWithClient<IState> {
|
||||||
private _suggestedRooms: ISuggestedRoom[] = [];
|
private _suggestedRooms: ISuggestedRoom[] = [];
|
||||||
private _invitedSpaces = new Set<Room>();
|
private _invitedSpaces = new Set<Room>();
|
||||||
private spaceOrderLocalEchoMap = new Map<string, string>();
|
private spaceOrderLocalEchoMap = new Map<string, string>();
|
||||||
private _restrictedJoinRuleSupport?: IRoomCapability;
|
|
||||||
// The following properties are set by onReady as they live in account_data
|
// The following properties are set by onReady as they live in account_data
|
||||||
private _allRoomsInHome = false;
|
private _allRoomsInHome = false;
|
||||||
private _enabledMetaSpaces: MetaSpace[] = [];
|
private _enabledMetaSpaces: MetaSpace[] = [];
|
||||||
|
@ -210,10 +209,6 @@ export class SpaceStoreClass extends AsyncStoreWithClient<IState> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public get restrictedJoinRuleSupport(): IRoomCapability {
|
|
||||||
return this._restrictedJoinRuleSupport;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the active space, updates room list filters,
|
* Sets the active space, updates room list filters,
|
||||||
* optionally switches the user's room back to where they were when they last viewed that space.
|
* optionally switches the user's room back to where they were when they last viewed that space.
|
||||||
|
@ -1066,11 +1061,6 @@ export class SpaceStoreClass extends AsyncStoreWithClient<IState> {
|
||||||
this.matrixClient.on(RoomStateEvent.Members, this.onRoomStateMembers);
|
this.matrixClient.on(RoomStateEvent.Members, this.onRoomStateMembers);
|
||||||
this.matrixClient.on(ClientEvent.AccountData, this.onAccountData);
|
this.matrixClient.on(ClientEvent.AccountData, this.onAccountData);
|
||||||
|
|
||||||
this.matrixClient.getCapabilities().then(capabilities => {
|
|
||||||
this._restrictedJoinRuleSupport = capabilities
|
|
||||||
?.["m.room_versions"]?.["org.matrix.msc3244.room_capabilities"]?.["restricted"];
|
|
||||||
});
|
|
||||||
|
|
||||||
const oldMetaSpaces = this._enabledMetaSpaces;
|
const oldMetaSpaces = this._enabledMetaSpaces;
|
||||||
const enabledMetaSpaces = SettingsStore.getValue("Spaces.enabledMetaSpaces");
|
const enabledMetaSpaces = SettingsStore.getValue("Spaces.enabledMetaSpaces");
|
||||||
this._enabledMetaSpaces = metaSpaceOrder.filter(k => enabledMetaSpaces[k]);
|
this._enabledMetaSpaces = metaSpaceOrder.filter(k => enabledMetaSpaces[k]);
|
||||||
|
|
55
src/utils/PreferredRoomVersions.ts
Normal file
55
src/utils/PreferredRoomVersions.ts
Normal file
|
@ -0,0 +1,55 @@
|
||||||
|
/*
|
||||||
|
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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The preferred room versions for various features within the app. The
|
||||||
|
* room versions here are selected based on the client's support for the
|
||||||
|
* possible room versions in combination with server support in the
|
||||||
|
* ecosystem.
|
||||||
|
*
|
||||||
|
* Loosely follows https://spec.matrix.org/latest/rooms/#feature-matrix
|
||||||
|
*/
|
||||||
|
export class PreferredRoomVersions {
|
||||||
|
/**
|
||||||
|
* The room version to use when creating "restricted" rooms.
|
||||||
|
*/
|
||||||
|
public static readonly RestrictedRooms = "9";
|
||||||
|
|
||||||
|
private constructor() {
|
||||||
|
// readonly, static, class
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determines if a room version supports the given feature using heuristics
|
||||||
|
* for how Matrix works.
|
||||||
|
* @param roomVer The room version to check support within.
|
||||||
|
* @param featureVer The room version of the feature. Should be from PreferredRoomVersions.
|
||||||
|
* @see PreferredRoomVersions
|
||||||
|
*/
|
||||||
|
export function doesRoomVersionSupport(roomVer: string, featureVer: string): boolean {
|
||||||
|
// Assumption: all unstable room versions don't support the feature. Calling code can check for unstable
|
||||||
|
// room versions explicitly if it wants to. The spec reserves [0-9] and `.` for its room versions.
|
||||||
|
if (!roomVer.match(/[\d.]+/)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Dev note: While the spec says room versions are not linear, we can make reasonable assumptions
|
||||||
|
// until the room versions prove themselves to be non-linear in the spec. We should see this coming
|
||||||
|
// from a mile away and can course-correct this function if needed.
|
||||||
|
return Number(roomVer) >= Number(featureVer);
|
||||||
|
}
|
||||||
|
|
46
test/PreferredRoomVersions-test.ts
Normal file
46
test/PreferredRoomVersions-test.ts
Normal file
|
@ -0,0 +1,46 @@
|
||||||
|
/*
|
||||||
|
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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { doesRoomVersionSupport, PreferredRoomVersions } from "../src/utils/PreferredRoomVersions";
|
||||||
|
|
||||||
|
describe("doesRoomVersionSupport", () => {
|
||||||
|
it("should detect unstable as unsupported", () => {
|
||||||
|
expect(doesRoomVersionSupport("org.example.unstable", "1")).toBe(false);
|
||||||
|
expect(doesRoomVersionSupport("1.2-beta", "1")).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should detect support properly", () => {
|
||||||
|
expect(doesRoomVersionSupport("1", "2")).toBe(false); // older
|
||||||
|
expect(doesRoomVersionSupport("2", "2")).toBe(true); // exact
|
||||||
|
expect(doesRoomVersionSupport("3", "2")).toBe(true); // newer
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should handle decimal versions", () => {
|
||||||
|
expect(doesRoomVersionSupport("1.1", "2.2")).toBe(false); // older
|
||||||
|
expect(doesRoomVersionSupport("2.1", "2.2")).toBe(false); // exact-ish
|
||||||
|
expect(doesRoomVersionSupport("2.2", "2.2")).toBe(true); // exact
|
||||||
|
expect(doesRoomVersionSupport("2.3", "2.2")).toBe(true); // exact-ish
|
||||||
|
expect(doesRoomVersionSupport("3.1", "2.2")).toBe(true); // newer
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should detect restricted rooms in v9 and v10", () => {
|
||||||
|
// Dev note: we consider it a feature that v8 rooms have to upgrade considering the bug in v8.
|
||||||
|
// https://spec.matrix.org/v1.3/rooms/v8/#redactions
|
||||||
|
expect(doesRoomVersionSupport("8", PreferredRoomVersions.RestrictedRooms)).toBe(false);
|
||||||
|
expect(doesRoomVersionSupport("9", PreferredRoomVersions.RestrictedRooms)).toBe(true);
|
||||||
|
expect(doesRoomVersionSupport("10", PreferredRoomVersions.RestrictedRooms)).toBe(true);
|
||||||
|
});
|
||||||
|
});
|
Loading…
Reference in a new issue