Iterate SecurityRoomSettingsTab and ManageRestrictedJoinRuleDialog
This commit is contained in:
parent
692347843d
commit
c5ca98a3ad
4 changed files with 106 additions and 76 deletions
|
@ -104,7 +104,7 @@ limitations under the License.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_ManageRestrictedJoinRuleDialog_section_experimental {
|
.mx_ManageRestrictedJoinRuleDialog_section_info {
|
||||||
position: relative;
|
position: relative;
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
margin: 12px 0;
|
margin: 12px 0;
|
||||||
|
@ -131,16 +131,19 @@ limitations under the License.
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_ManageRestrictedJoinRuleDialog_footer {
|
.mx_ManageRestrictedJoinRuleDialog_footer {
|
||||||
display: flex;
|
|
||||||
margin-top: 20px;
|
margin-top: 20px;
|
||||||
align-self: end;
|
|
||||||
|
|
||||||
.mx_AccessibleButton {
|
.mx_ManageRestrictedJoinRuleDialog_footer_buttons {
|
||||||
display: inline-block;
|
display: flex;
|
||||||
align-self: center;
|
width: max-content;
|
||||||
|
margin-left: auto;
|
||||||
|
|
||||||
& + .mx_AccessibleButton {
|
.mx_AccessibleButton {
|
||||||
margin-left: 24px;
|
display: inline-block;
|
||||||
|
|
||||||
|
& + .mx_AccessibleButton {
|
||||||
|
margin-left: 24px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -102,6 +102,13 @@ const ManageRestrictedJoinRuleDialog: React.FC<IProps> = ({ room, selected = [],
|
||||||
setNewSelected(new Set(newSelected));
|
setNewSelected(new Set(newSelected));
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let inviteOnlyWarning;
|
||||||
|
if (newSelected.size < 1) {
|
||||||
|
inviteOnlyWarning = <div className="mx_ManageRestrictedJoinRuleDialog_section_info">
|
||||||
|
{ _t("You're removing all spaces. Access will default to invite only") }
|
||||||
|
</div>;
|
||||||
|
}
|
||||||
|
|
||||||
return <BaseDialog
|
return <BaseDialog
|
||||||
title={_t("Select spaces")}
|
title={_t("Select spaces")}
|
||||||
className="mx_ManageRestrictedJoinRuleDialog"
|
className="mx_ManageRestrictedJoinRuleDialog"
|
||||||
|
@ -142,7 +149,7 @@ const ManageRestrictedJoinRuleDialog: React.FC<IProps> = ({ room, selected = [],
|
||||||
{ filteredOtherEntries.length > 0 ? (
|
{ filteredOtherEntries.length > 0 ? (
|
||||||
<div className="mx_ManageRestrictedJoinRuleDialog_section">
|
<div className="mx_ManageRestrictedJoinRuleDialog_section">
|
||||||
<h3>{ _t("Other spaces or rooms you might not know") }</h3>
|
<h3>{ _t("Other spaces or rooms you might not know") }</h3>
|
||||||
<div className="mx_ManageRestrictedJoinRuleDialog_section_experimental">
|
<div className="mx_ManageRestrictedJoinRuleDialog_section_info">
|
||||||
<div>{ _t("These are likely ones other room admins are a part of.") }</div>
|
<div>{ _t("These are likely ones other room admins are a part of.") }</div>
|
||||||
</div>
|
</div>
|
||||||
{ filteredOtherEntries.map(space => {
|
{ filteredOtherEntries.map(space => {
|
||||||
|
@ -167,12 +174,15 @@ const ManageRestrictedJoinRuleDialog: React.FC<IProps> = ({ room, selected = [],
|
||||||
</AutoHideScrollbar>
|
</AutoHideScrollbar>
|
||||||
|
|
||||||
<div className="mx_ManageRestrictedJoinRuleDialog_footer">
|
<div className="mx_ManageRestrictedJoinRuleDialog_footer">
|
||||||
<AccessibleButton kind="primary_outline" onClick={() => onFinished()}>
|
{ inviteOnlyWarning }
|
||||||
{ _t("Cancel") }
|
<div className="mx_ManageRestrictedJoinRuleDialog_footer_buttons">
|
||||||
</AccessibleButton>
|
<AccessibleButton kind="primary_outline" onClick={() => onFinished()}>
|
||||||
<AccessibleButton kind="primary" onClick={() => onFinished(Array.from(newSelected))}>
|
{ _t("Cancel") }
|
||||||
{ _t("Confirm") }
|
</AccessibleButton>
|
||||||
</AccessibleButton>
|
<AccessibleButton kind="primary" onClick={() => onFinished(Array.from(newSelected))}>
|
||||||
|
{ _t("Confirm") }
|
||||||
|
</AccessibleButton>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</MatrixClientContext.Provider>
|
</MatrixClientContext.Provider>
|
||||||
</BaseDialog>;
|
</BaseDialog>;
|
||||||
|
|
|
@ -101,19 +101,14 @@ export default class SecurityRoomSettingsTab extends React.Component<IProps, ISt
|
||||||
);
|
);
|
||||||
|
|
||||||
const encrypted = MatrixClientPeg.get().isRoomEncrypted(this.props.roomId);
|
const encrypted = MatrixClientPeg.get().isRoomEncrypted(this.props.roomId);
|
||||||
this.setState({ joinRule, restrictedAllowRoomIds, guestAccess, history, encrypted });
|
const restrictedRoomCapabilities = SpaceStore.instance.restrictedJoinRuleSupport;
|
||||||
|
const roomSupportsRestricted = Array.isArray(restrictedRoomCapabilities?.support)
|
||||||
|
&& restrictedRoomCapabilities.support.includes(room.getVersion());
|
||||||
|
const preferredRestrictionVersion = roomSupportsRestricted ? null : restrictedRoomCapabilities.preferred;
|
||||||
|
this.setState({ joinRule, restrictedAllowRoomIds, guestAccess, history, encrypted,
|
||||||
|
roomSupportsRestricted, preferredRestrictionVersion });
|
||||||
|
|
||||||
this.hasAliases().then(hasAliases => this.setState({ hasAliases }));
|
this.hasAliases().then(hasAliases => this.setState({ hasAliases }));
|
||||||
cli.getCapabilities().then(capabilities => {
|
|
||||||
const roomCapabilities = capabilities["org.matrix.msc3244.room_capabilities"];
|
|
||||||
const roomSupportsRestricted = roomCapabilities && Array.isArray(roomCapabilities["restricted"]?.support) &&
|
|
||||||
roomCapabilities["restricted"].support.includes(room.getVersion());
|
|
||||||
const preferredRestrictionVersion = roomSupportsRestricted
|
|
||||||
? roomCapabilities?.["restricted"].preferred
|
|
||||||
: undefined;
|
|
||||||
|
|
||||||
this.setState({ roomSupportsRestricted, preferredRestrictionVersion });
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private pullContentPropertyFromEvent<T>(event: MatrixEvent, key: string, defaultValue: T): T {
|
private pullContentPropertyFromEvent<T>(event: MatrixEvent, key: string, defaultValue: T): T {
|
||||||
|
@ -169,23 +164,16 @@ export default class SecurityRoomSettingsTab extends React.Component<IProps, ISt
|
||||||
|
|
||||||
private onJoinRuleChange = async (joinRule: JoinRule) => {
|
private onJoinRuleChange = async (joinRule: JoinRule) => {
|
||||||
const beforeJoinRule = this.state.joinRule;
|
const beforeJoinRule = this.state.joinRule;
|
||||||
if (beforeJoinRule === joinRule) return;
|
|
||||||
|
|
||||||
|
let restrictedAllowRoomIds: string[];
|
||||||
if (joinRule === JoinRule.Restricted) {
|
if (joinRule === JoinRule.Restricted) {
|
||||||
const matrixClient = MatrixClientPeg.get();
|
const matrixClient = MatrixClientPeg.get();
|
||||||
const roomId = this.props.roomId;
|
const roomId = this.props.roomId;
|
||||||
const room = matrixClient.getRoom(roomId);
|
const room = matrixClient.getRoom(roomId);
|
||||||
|
|
||||||
if (this.state.roomSupportsRestricted) {
|
if (beforeJoinRule === JoinRule.Restricted || this.state.roomSupportsRestricted) {
|
||||||
// Have the user pick which spaces to allow joins from
|
// Have the user pick which spaces to allow joins from
|
||||||
const { finished } = Modal.createTrackedDialog('Set restricted', '', ManageRestrictedJoinRuleDialog, {
|
restrictedAllowRoomIds = await this.editRestrictedRoomIds();
|
||||||
matrixClient,
|
|
||||||
room,
|
|
||||||
// if they have are viewing this room from the context of a space then default to that
|
|
||||||
selected: SpaceStore.instance.activeSpace ? [SpaceStore.instance.activeSpace.roomId] : [],
|
|
||||||
}, "mx_ManageRestrictedJoinRuleDialog_wrapper");
|
|
||||||
|
|
||||||
const [restrictedAllowRoomIds] = await finished;
|
|
||||||
if (!Array.isArray(restrictedAllowRoomIds)) return;
|
if (!Array.isArray(restrictedAllowRoomIds)) return;
|
||||||
} else if (this.state.preferredRestrictionVersion) {
|
} else if (this.state.preferredRestrictionVersion) {
|
||||||
// Block this action on a room upgrade otherwise it'd make their room unjoinable
|
// Block this action on a room upgrade otherwise it'd make their room unjoinable
|
||||||
|
@ -204,19 +192,18 @@ export default class SecurityRoomSettingsTab extends React.Component<IProps, ISt
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (beforeJoinRule === joinRule && !restrictedAllowRoomIds) return;
|
||||||
|
|
||||||
const content: IContent = {
|
const content: IContent = {
|
||||||
join_rule: joinRule,
|
join_rule: joinRule,
|
||||||
};
|
};
|
||||||
|
|
||||||
let restrictedAllowRoomIds: string[];
|
|
||||||
// pre-set the accepted spaces with the currently viewed one as per the microcopy
|
// pre-set the accepted spaces with the currently viewed one as per the microcopy
|
||||||
if (joinRule === JoinRule.Restricted && SpaceStore.instance.activeSpace) {
|
if (joinRule === JoinRule.Restricted) {
|
||||||
const spaceRoomId = SpaceStore.instance.activeSpace.roomId;
|
content.allow = restrictedAllowRoomIds.map(roomId => ({
|
||||||
restrictedAllowRoomIds = [spaceRoomId];
|
|
||||||
content.allow = [{
|
|
||||||
"type": RestrictedAllowType.RoomMembership,
|
"type": RestrictedAllowType.RoomMembership,
|
||||||
"room_id": spaceRoomId,
|
"room_id": roomId,
|
||||||
}];
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
this.setState({ joinRule, restrictedAllowRoomIds });
|
this.setState({ joinRule, restrictedAllowRoomIds });
|
||||||
|
@ -296,17 +283,31 @@ export default class SecurityRoomSettingsTab extends React.Component<IProps, ISt
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private onEditRestrictedClick = () => {
|
private editRestrictedRoomIds = async (): Promise<string[] | undefined> => {
|
||||||
|
let selected = this.state.restrictedAllowRoomIds;
|
||||||
|
if (!selected?.length && SpaceStore.instance.activeSpace) {
|
||||||
|
selected = [SpaceStore.instance.activeSpace.roomId];
|
||||||
|
}
|
||||||
|
|
||||||
const matrixClient = MatrixClientPeg.get();
|
const matrixClient = MatrixClientPeg.get();
|
||||||
Modal.createTrackedDialog('Edit restricted', '', ManageRestrictedJoinRuleDialog, {
|
const { finished } = Modal.createTrackedDialog('Edit restricted', '', ManageRestrictedJoinRuleDialog, {
|
||||||
matrixClient,
|
matrixClient,
|
||||||
room: matrixClient.getRoom(this.props.roomId),
|
room: matrixClient.getRoom(this.props.roomId),
|
||||||
selected: this.state.restrictedAllowRoomIds,
|
selected,
|
||||||
onFinished: (restrictedAllowRoomIds?: string[]) => {
|
|
||||||
if (!Array.isArray(restrictedAllowRoomIds)) return;
|
|
||||||
this.onRestrictedRoomIdsChange(restrictedAllowRoomIds);
|
|
||||||
},
|
|
||||||
}, "mx_ManageRestrictedJoinRuleDialog_wrapper");
|
}, "mx_ManageRestrictedJoinRuleDialog_wrapper");
|
||||||
|
|
||||||
|
const [restrictedAllowRoomIds] = await finished;
|
||||||
|
return restrictedAllowRoomIds;
|
||||||
|
};
|
||||||
|
|
||||||
|
private onEditRestrictedClick = async () => {
|
||||||
|
const restrictedAllowRoomIds = await this.editRestrictedRoomIds();
|
||||||
|
if (!Array.isArray(restrictedAllowRoomIds)) return;
|
||||||
|
if (restrictedAllowRoomIds.length > 0) {
|
||||||
|
this.onRestrictedRoomIdsChange(restrictedAllowRoomIds);
|
||||||
|
} else {
|
||||||
|
this.onJoinRuleChange(JoinRule.Invite);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
private renderJoinRule() {
|
private renderJoinRule() {
|
||||||
|
@ -332,6 +333,8 @@ export default class SecurityRoomSettingsTab extends React.Component<IProps, ISt
|
||||||
value: JoinRule.Invite,
|
value: JoinRule.Invite,
|
||||||
label: _t("Private (invite only)"),
|
label: _t("Private (invite only)"),
|
||||||
description: _t("Only invited people can join."),
|
description: _t("Only invited people can join."),
|
||||||
|
checked: this.state.joinRule === JoinRule.Invite
|
||||||
|
|| (this.state.joinRule === JoinRule.Restricted && !this.state.restrictedAllowRoomIds?.length),
|
||||||
}, {
|
}, {
|
||||||
value: JoinRule.Public,
|
value: JoinRule.Public,
|
||||||
label: _t("Public (anyone)"),
|
label: _t("Public (anyone)"),
|
||||||
|
@ -350,28 +353,23 @@ export default class SecurityRoomSettingsTab extends React.Component<IProps, ISt
|
||||||
}
|
}
|
||||||
|
|
||||||
let description;
|
let description;
|
||||||
if (joinRule === JoinRule.Restricted) {
|
if (joinRule === JoinRule.Restricted && this.state.restrictedAllowRoomIds?.length) {
|
||||||
let spacesWhichCanAccess;
|
const shownSpaces = this.state.restrictedAllowRoomIds
|
||||||
if (this.state.restrictedAllowRoomIds?.length) {
|
.map(roomId => client.getRoom(roomId))
|
||||||
const shownSpaces = this.state.restrictedAllowRoomIds
|
.filter(room => room?.isSpaceRoom())
|
||||||
.map(roomId => client.getRoom(roomId))
|
.slice(0, 4);
|
||||||
.filter(Boolean)
|
|
||||||
.slice(0, 4);
|
|
||||||
|
|
||||||
spacesWhichCanAccess = <div className="mx_SecurityRoomSettingsTab_spacesWithAccess">
|
let moreText;
|
||||||
<h4>{ _t("Spaces with access") }</h4>
|
if (shownSpaces.length < this.state.restrictedAllowRoomIds.length) {
|
||||||
{ shownSpaces.map(room => {
|
if (shownSpaces.length > 0) {
|
||||||
return <span key={room.roomId}>
|
moreText = _t("& %(count)s more", {
|
||||||
<RoomAvatar room={room} height={32} width={32} />
|
count: this.state.restrictedAllowRoomIds.length - shownSpaces.length,
|
||||||
{ room.name }
|
});
|
||||||
</span>;
|
} else {
|
||||||
})}
|
moreText = _t("Currently, %(count)s spaces have access", {
|
||||||
{ shownSpaces.length < this.state.restrictedAllowRoomIds.length && <span>
|
count: this.state.restrictedAllowRoomIds.length,
|
||||||
{ _t("& %(count)s more", {
|
});
|
||||||
count: this.state.restrictedAllowRoomIds.length - shownSpaces.length,
|
}
|
||||||
}) }
|
|
||||||
</span> }
|
|
||||||
</div>;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
description = <div>
|
description = <div>
|
||||||
|
@ -386,7 +384,17 @@ export default class SecurityRoomSettingsTab extends React.Component<IProps, ISt
|
||||||
</AccessibleButton>,
|
</AccessibleButton>,
|
||||||
}) }
|
}) }
|
||||||
</span>
|
</span>
|
||||||
{ spacesWhichCanAccess }
|
|
||||||
|
<div className="mx_SecurityRoomSettingsTab_spacesWithAccess">
|
||||||
|
<h4>{ _t("Spaces with access") }</h4>
|
||||||
|
{ shownSpaces.map(room => {
|
||||||
|
return <span key={room.roomId}>
|
||||||
|
<RoomAvatar room={room} height={32} width={32} />
|
||||||
|
{ room.name }
|
||||||
|
</span>;
|
||||||
|
})}
|
||||||
|
{ moreText && <span>{ moreText }</span> }
|
||||||
|
</div>
|
||||||
</div>;
|
</div>;
|
||||||
} else if (SpaceStore.instance.activeSpace) {
|
} else if (SpaceStore.instance.activeSpace) {
|
||||||
description = _t("Anyone in %(spaceName)s can find and join. You can select other spaces too.", {
|
description = _t("Anyone in %(spaceName)s can find and join. You can select other spaces too.", {
|
||||||
|
@ -403,6 +411,8 @@ export default class SecurityRoomSettingsTab extends React.Component<IProps, ISt
|
||||||
{ upgradeRequiredPill }
|
{ upgradeRequiredPill }
|
||||||
</>,
|
</>,
|
||||||
description,
|
description,
|
||||||
|
// if there are 0 allowed spaces then render it as invite only instead
|
||||||
|
checked: this.state.joinRule === JoinRule.Restricted && !!this.state.restrictedAllowRoomIds?.length,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -478,7 +488,7 @@ export default class SecurityRoomSettingsTab extends React.Component<IProps, ISt
|
||||||
const state = client.getRoom(this.props.roomId).currentState;
|
const state = client.getRoom(this.props.roomId).currentState;
|
||||||
const canSetGuestAccess = state.mayClientSendStateEvent(EventType.RoomGuestAccess, client);
|
const canSetGuestAccess = state.mayClientSendStateEvent(EventType.RoomGuestAccess, client);
|
||||||
|
|
||||||
return <>
|
return <div className="mx_SettingsTab_section">
|
||||||
<LabelledToggleSwitch
|
<LabelledToggleSwitch
|
||||||
value={guestAccess === GuestAccess.CanJoin}
|
value={guestAccess === GuestAccess.CanJoin}
|
||||||
onChange={this.onGuestAccessChange}
|
onChange={this.onGuestAccessChange}
|
||||||
|
@ -489,7 +499,7 @@ export default class SecurityRoomSettingsTab extends React.Component<IProps, ISt
|
||||||
{ _t("People with supported clients will be able to join " +
|
{ _t("People with supported clients will be able to join " +
|
||||||
"the room without having a registered account.") }
|
"the room without having a registered account.") }
|
||||||
</p>
|
</p>
|
||||||
</>;
|
</div>;
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
|
|
|
@ -1455,9 +1455,12 @@
|
||||||
"Public (anyone)": "Public (anyone)",
|
"Public (anyone)": "Public (anyone)",
|
||||||
"Anyone can find and join.": "Anyone can find and join.",
|
"Anyone can find and join.": "Anyone can find and join.",
|
||||||
"Upgrade required": "Upgrade required",
|
"Upgrade required": "Upgrade required",
|
||||||
"Spaces with access": "Spaces with access",
|
|
||||||
"& %(count)s more|other": "& %(count)s more",
|
"& %(count)s more|other": "& %(count)s more",
|
||||||
|
"& %(count)s more|one": "& %(count)s more",
|
||||||
|
"Currently, %(count)s spaces have access|other": "Currently, %(count)s spaces have access",
|
||||||
|
"Currently, %(count)s spaces have access|one": "Currently, %(count)s space has access",
|
||||||
"Anyone in a space can find and join. <a>Edit which spaces can access here.</a>": "Anyone in a space can find and join. <a>Edit which spaces can access here.</a>",
|
"Anyone in a space can find and join. <a>Edit which spaces can access here.</a>": "Anyone in a space can find and join. <a>Edit which spaces can access here.</a>",
|
||||||
|
"Spaces with access": "Spaces with access",
|
||||||
"Anyone in %(spaceName)s can find and join. You can select other spaces too.": "Anyone in %(spaceName)s can find and join. You can select other spaces too.",
|
"Anyone in %(spaceName)s can find and join. You can select other spaces too.": "Anyone in %(spaceName)s can find and join. You can select other spaces too.",
|
||||||
"Anyone in a space can find and join. You can select multiple spaces.": "Anyone in a space can find and join. You can select multiple spaces.",
|
"Anyone in a space can find and join. You can select multiple spaces.": "Anyone in a space can find and join. You can select multiple spaces.",
|
||||||
"Space members": "Space members",
|
"Space members": "Space members",
|
||||||
|
@ -2187,8 +2190,11 @@
|
||||||
"Community ID": "Community ID",
|
"Community ID": "Community ID",
|
||||||
"example": "example",
|
"example": "example",
|
||||||
"Please enter a name for the room": "Please enter a name for the room",
|
"Please enter a name for the room": "Please enter a name for the room",
|
||||||
"Private rooms can be found and joined by invitation only. Public rooms can be found and joined by anyone.": "Private rooms can be found and joined by invitation only. Public rooms can be found and joined by anyone.",
|
|
||||||
"Private rooms can be found and joined by invitation only. Public rooms can be found and joined by anyone in this community.": "Private rooms can be found and joined by invitation only. Public rooms can be found and joined by anyone in this community.",
|
"Private rooms can be found and joined by invitation only. Public rooms can be found and joined by anyone in this community.": "Private rooms can be found and joined by invitation only. Public rooms can be found and joined by anyone in this community.",
|
||||||
|
"Everyone in <SpaceName/> will be able to find and join this room.": "Everyone in <SpaceName/> will be able to find and join this room.",
|
||||||
|
"You can change this at any time from room settings.": "You can change this at any time from room settings.",
|
||||||
|
"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/>.",
|
||||||
|
"Only people invited will be able to find and join this room.": "Only people invited will be able to find and join this room.",
|
||||||
"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.",
|
||||||
"Enable end-to-end encryption": "Enable end-to-end encryption",
|
"Enable end-to-end encryption": "Enable end-to-end encryption",
|
||||||
|
@ -2355,6 +2361,7 @@
|
||||||
"%(count)s members|one": "%(count)s member",
|
"%(count)s members|one": "%(count)s member",
|
||||||
"%(count)s rooms|other": "%(count)s rooms",
|
"%(count)s rooms|other": "%(count)s rooms",
|
||||||
"%(count)s rooms|one": "%(count)s room",
|
"%(count)s rooms|one": "%(count)s room",
|
||||||
|
"You're removing all spaces. Access will default to invite only": "You're removing all spaces. Access will default to invite only",
|
||||||
"Select spaces": "Select spaces",
|
"Select spaces": "Select spaces",
|
||||||
"Decide which spaces can access this room. If a space is selected its members will be able to find and join <RoomName/>.": "Decide which spaces can access this room. If a space is selected its members will be able to find and join <RoomName/>.",
|
"Decide which spaces can access this room. If a space is selected its members will be able to find and join <RoomName/>.": "Decide which spaces can access this room. If a space is selected its members will be able to find and join <RoomName/>.",
|
||||||
"Search spaces": "Search spaces",
|
"Search spaces": "Search spaces",
|
||||||
|
|
Loading…
Reference in a new issue