diff --git a/src/components/views/dialogs/SpaceSettingsDialog.tsx b/src/components/views/dialogs/SpaceSettingsDialog.tsx index fe836ebc5c7..bf73a2edb15 100644 --- a/src/components/views/dialogs/SpaceSettingsDialog.tsx +++ b/src/components/views/dialogs/SpaceSettingsDialog.tsx @@ -60,7 +60,7 @@ const SpaceSettingsDialog: React.FC = ({ matrixClient: cli, space, onFin SpaceSettingsTab.Visibility, _td("Visibility"), "mx_SpaceSettingsDialog_visibilityIcon", - , + , ), SettingsStore.getValue(UIFeature.AdvancedSettings) ? new Tab( diff --git a/src/components/views/settings/JoinRuleSettings.tsx b/src/components/views/settings/JoinRuleSettings.tsx new file mode 100644 index 00000000000..94c70f861ee --- /dev/null +++ b/src/components/views/settings/JoinRuleSettings.tsx @@ -0,0 +1,269 @@ +/* +Copyright 2021 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 React from "react"; +import { IJoinRuleEventContent, JoinRule, RestrictedAllowType } from "matrix-js-sdk/src/@types/partials"; +import { Room } from "matrix-js-sdk/src/models/room"; +import { EventType } from "matrix-js-sdk/src/@types/event"; + +import StyledRadioGroup, { IDefinition } from "../elements/StyledRadioGroup"; +import { _t } from "../../../languageHandler"; +import AccessibleButton from "../elements/AccessibleButton"; +import RoomAvatar from "../avatars/RoomAvatar"; +import SpaceStore from "../../../stores/SpaceStore"; +import { MatrixClientPeg } from "../../../MatrixClientPeg"; +import Modal from "../../../Modal"; +import ManageRestrictedJoinRuleDialog from "../dialogs/ManageRestrictedJoinRuleDialog"; +import RoomUpgradeWarningDialog from "../dialogs/RoomUpgradeWarningDialog"; +import { upgradeRoom } from "../../../utils/RoomUpgrade"; +import { arrayHasDiff } from "../../../utils/arrays"; +import { useLocalEcho } from "../../../hooks/useLocalEcho"; +import dis from "../../../dispatcher/dispatcher"; +import { ROOM_SECURITY_TAB } from "../dialogs/RoomSettingsDialog"; + +interface IProps { + room: Room; + promptUpgrade?: boolean; + closeSettingsFn(): void; + onError(error: Error): void; + beforeChange?(joinRule: JoinRule): Promise; // if returns false then aborts the change +} + +const JoinRuleSettings = ({ room, promptUpgrade, onError, beforeChange, closeSettingsFn }: IProps) => { + const cli = room.client; + + const restrictedRoomCapabilities = SpaceStore.instance.restrictedJoinRuleSupport; + const roomSupportsRestricted = Array.isArray(restrictedRoomCapabilities?.support) + && restrictedRoomCapabilities.support.includes(room.getVersion()); + const preferredRestrictionVersion = !roomSupportsRestricted && promptUpgrade + ? restrictedRoomCapabilities?.preferred + : undefined; + + const disabled = !room.currentState.mayClientSendStateEvent(EventType.RoomJoinRules, cli); + + const [content, setContent] = useLocalEcho( + () => room.currentState.getStateEvents(EventType.RoomJoinRules, "")?.getContent(), + content => cli.sendStateEvent(room.roomId, EventType.RoomJoinRules, content, ""), + onError, + ); + + const { join_rule: joinRule } = content; + const restrictedAllowRoomIds = joinRule === JoinRule.Restricted + ? content.allow.filter(o => o.type === RestrictedAllowType.RoomMembership).map(o => o.room_id) + : undefined; + + const editRestrictedRoomIds = async (): Promise => { + let selected = restrictedAllowRoomIds; + if (!selected?.length && SpaceStore.instance.activeSpace) { + selected = [SpaceStore.instance.activeSpace.roomId]; + } + + const matrixClient = MatrixClientPeg.get(); + const { finished } = Modal.createTrackedDialog('Edit restricted', '', ManageRestrictedJoinRuleDialog, { + matrixClient, + room, + selected, + }, "mx_ManageRestrictedJoinRuleDialog_wrapper"); + + const [roomIds] = await finished; + return roomIds; + }; + + const definitions: IDefinition[] = [{ + value: JoinRule.Invite, + label: _t("Private (invite only)"), + description: _t("Only invited people can join."), + checked: joinRule === JoinRule.Invite || (joinRule === JoinRule.Restricted && !restrictedAllowRoomIds?.length), + }, { + value: JoinRule.Public, + label: _t("Public"), + description: _t("Anyone can find and join."), + }]; + + if (roomSupportsRestricted || preferredRestrictionVersion || joinRule === JoinRule.Restricted) { + let upgradeRequiredPill; + if (preferredRestrictionVersion) { + upgradeRequiredPill = + { _t("Upgrade required") } + ; + } + + let description; + if (joinRule === JoinRule.Restricted && restrictedAllowRoomIds?.length) { + // only show the first 4 spaces we know about, so that the UI doesn't grow out of proportion there are lots. + const shownSpaces = restrictedAllowRoomIds + .map(roomId => cli.getRoom(roomId)) + .filter(room => room?.isSpaceRoom()) + .slice(0, 4); + + let moreText; + if (shownSpaces.length < restrictedAllowRoomIds.length) { + if (shownSpaces.length > 0) { + moreText = _t("& %(count)s more", { + count: restrictedAllowRoomIds.length - shownSpaces.length, + }); + } else { + moreText = _t("Currently, %(count)s spaces have access", { + count: restrictedAllowRoomIds.length, + }); + } + } + + const onRestrictedRoomIdsChange = (newAllowRoomIds: string[]) => { + if (!arrayHasDiff(restrictedAllowRoomIds || [], newAllowRoomIds)) return; + + if (!newAllowRoomIds.length) { + setContent({ + join_rule: JoinRule.Invite, + }); + return; + } + + setContent({ + join_rule: JoinRule.Restricted, + allow: newAllowRoomIds.map(roomId => ({ + "type": RestrictedAllowType.RoomMembership, + "room_id": roomId, + })), + }); + }; + + const onEditRestrictedClick = async () => { + const restrictedAllowRoomIds = await editRestrictedRoomIds(); + if (!Array.isArray(restrictedAllowRoomIds)) return; + if (restrictedAllowRoomIds.length > 0) { + onRestrictedRoomIdsChange(restrictedAllowRoomIds); + } else { + onChange(JoinRule.Invite); + } + }; + + description =
+ + { _t("Anyone in a space can find and join. Edit which spaces can access here.", {}, { + a: sub => + { sub } + , + }) } + + +
+

{ _t("Spaces with access") }

+ { shownSpaces.map(room => { + return + + { room.name } + ; + }) } + { moreText && { moreText } } +
+
; + } else if (SpaceStore.instance.activeSpace) { + description = _t("Anyone in can find and join. You can select other spaces too.", {}, { + spaceName: () => { SpaceStore.instance.activeSpace.name }, + }); + } else { + description = _t("Anyone in a space can find and join. You can select multiple spaces."); + } + + definitions.splice(1, 0, { + value: JoinRule.Restricted, + label: <> + { _t("Space members") } + { upgradeRequiredPill } + , + description, + // if there are 0 allowed spaces then render it as invite only instead + checked: joinRule === JoinRule.Restricted && !!restrictedAllowRoomIds?.length, + }); + } + + const onChange = async (joinRule: JoinRule) => { + const beforeJoinRule = content.join_rule; + + let restrictedAllowRoomIds: string[]; + if (joinRule === JoinRule.Restricted) { + if (beforeJoinRule === JoinRule.Restricted || roomSupportsRestricted) { + // Have the user pick which spaces to allow joins from + restrictedAllowRoomIds = await editRestrictedRoomIds(); + if (!Array.isArray(restrictedAllowRoomIds)) return; + } else if (preferredRestrictionVersion) { + // Block this action on a room upgrade otherwise it'd make their room unjoinable + const targetVersion = preferredRestrictionVersion; + Modal.createTrackedDialog('Restricted join rule upgrade', '', RoomUpgradeWarningDialog, { + roomId: room.roomId, + targetVersion, + description: _t("This upgrade will allow members of selected spaces " + + "access to this room without an invite."), + onFinished: async (resp) => { + if (!resp?.continue) return; + const roomId = await upgradeRoom(room, targetVersion, resp.invite, true, true, true); + closeSettingsFn(); + // switch to the new room in the background + dis.dispatch({ + action: "view_room", + room_id: roomId, + }); + // open new settings on this tab + dis.dispatch({ + action: "open_room_settings", + initial_tab_id: ROOM_SECURITY_TAB, + }); + }, + }); + return; + } + + // when setting to 0 allowed rooms/spaces set to invite only instead as per the note + if (!restrictedAllowRoomIds.length) { + joinRule = JoinRule.Invite; + } + } + + if (beforeJoinRule === joinRule && !restrictedAllowRoomIds) return; + if (beforeChange && !await beforeChange(joinRule)) return; + + const newContent: IJoinRuleEventContent = { + join_rule: joinRule, + }; + + // pre-set the accepted spaces with the currently viewed one as per the microcopy + if (joinRule === JoinRule.Restricted) { + newContent.allow = restrictedAllowRoomIds.map(roomId => ({ + "type": RestrictedAllowType.RoomMembership, + "room_id": roomId, + })); + } + + setContent(newContent); + }; + + return ( + + ); +}; + +export default JoinRuleSettings; diff --git a/src/components/views/settings/tabs/room/SecurityRoomSettingsTab.tsx b/src/components/views/settings/tabs/room/SecurityRoomSettingsTab.tsx index 5cb76ebc252..5ebfa92acf6 100644 --- a/src/components/views/settings/tabs/room/SecurityRoomSettingsTab.tsx +++ b/src/components/views/settings/tabs/room/SecurityRoomSettingsTab.tsx @@ -16,7 +16,7 @@ limitations under the License. import React from 'react'; import { GuestAccess, HistoryVisibility, JoinRule, RestrictedAllowType } from "matrix-js-sdk/src/@types/partials"; -import { IContent, MatrixEvent } from "matrix-js-sdk/src/models/event"; +import { MatrixEvent } from "matrix-js-sdk/src/models/event"; import { EventType } from 'matrix-js-sdk/src/@types/event'; import { _t } from "../../../../../languageHandler"; @@ -24,23 +24,17 @@ import { MatrixClientPeg } from "../../../../../MatrixClientPeg"; import LabelledToggleSwitch from "../../../elements/LabelledToggleSwitch"; import Modal from "../../../../../Modal"; import QuestionDialog from "../../../dialogs/QuestionDialog"; -import StyledRadioGroup, { IDefinition } from '../../../elements/StyledRadioGroup'; +import StyledRadioGroup from '../../../elements/StyledRadioGroup'; import { SettingLevel } from "../../../../../settings/SettingLevel"; import SettingsStore from "../../../../../settings/SettingsStore"; import { UIFeature } from "../../../../../settings/UIFeature"; import { replaceableComponent } from "../../../../../utils/replaceableComponent"; import AccessibleButton from "../../../elements/AccessibleButton"; -import SpaceStore from "../../../../../stores/SpaceStore"; -import RoomAvatar from "../../../avatars/RoomAvatar"; -import ManageRestrictedJoinRuleDialog from '../../../dialogs/ManageRestrictedJoinRuleDialog'; -import RoomUpgradeWarningDialog from '../../../dialogs/RoomUpgradeWarningDialog'; -import { upgradeRoom } from "../../../../../utils/RoomUpgrade"; -import { arrayHasDiff } from "../../../../../utils/arrays"; import SettingsFlag from '../../../elements/SettingsFlag'; import createRoom, { IOpts } from '../../../../../createRoom'; import CreateRoomDialog from '../../../dialogs/CreateRoomDialog'; -import dis from "../../../../../dispatcher/dispatcher"; -import { ROOM_SECURITY_TAB } from "../../../dialogs/RoomSettingsDialog"; +import JoinRuleSettings from "../../JoinRuleSettings"; +import ErrorDialog from "../../../dialogs/ErrorDialog"; interface IProps { roomId: string; @@ -48,14 +42,11 @@ interface IProps { } interface IState { - joinRule: JoinRule; restrictedAllowRoomIds?: string[]; guestAccess: GuestAccess; history: HistoryVisibility; hasAliases: boolean; encrypted: boolean; - roomSupportsRestricted?: boolean; - preferredRestrictionVersion?: string; showAdvancedSection: boolean; } @@ -65,7 +56,6 @@ export default class SecurityRoomSettingsTab extends React.Component this.setState({ hasAliases })); } @@ -135,7 +120,7 @@ export default class SecurityRoomSettingsTab extends React.Component { - if (this.state.joinRule == "public") { + if (MatrixClientPeg.get().getRoom(this.props.roomId)?.getJoinRule() === JoinRule.Public) { const dialog = Modal.createTrackedDialog('Confirm Public Encrypted Room', '', QuestionDialog, { title: _t('Are you sure you want to add encryption to this public room?'), description:
@@ -202,128 +187,6 @@ export default class SecurityRoomSettingsTab extends React.Component { - const beforeJoinRule = this.state.joinRule; - - let restrictedAllowRoomIds: string[]; - if (joinRule === JoinRule.Restricted) { - const matrixClient = MatrixClientPeg.get(); - const roomId = this.props.roomId; - const room = matrixClient.getRoom(roomId); - - if (beforeJoinRule === JoinRule.Restricted || this.state.roomSupportsRestricted) { - // Have the user pick which spaces to allow joins from - restrictedAllowRoomIds = await this.editRestrictedRoomIds(); - if (!Array.isArray(restrictedAllowRoomIds)) return; - } else if (this.state.preferredRestrictionVersion) { - // Block this action on a room upgrade otherwise it'd make their room unjoinable - const targetVersion = this.state.preferredRestrictionVersion; - Modal.createTrackedDialog('Restricted join rule upgrade', '', RoomUpgradeWarningDialog, { - roomId, - targetVersion, - description: _t("This upgrade will allow members of selected spaces " + - "access to this room without an invite."), - onFinished: async (resp) => { - if (!resp?.continue) return; - const roomId = await upgradeRoom(room, targetVersion, resp.invite, true, true, true); - this.props.closeSettingsFn(); - // switch to the new room in the background - dis.dispatch({ - action: "view_room", - room_id: roomId, - }); - // open new settings on this tab - dis.dispatch({ - action: "open_room_settings", - initial_tab_id: ROOM_SECURITY_TAB, - }); - }, - }); - return; - } - } - - if ( - this.state.encrypted && - this.state.joinRule !== JoinRule.Public && - joinRule === JoinRule.Public - ) { - const dialog = Modal.createTrackedDialog('Confirm Public Encrypted Room', '', QuestionDialog, { - title: _t("Are you sure you want to make this encrypted room public?"), - description:
-

{ _t( - "It's not recommended to make encrypted rooms public. " + - "It will mean anyone can find and join the room, so anyone can read messages. " + - "You'll get none of the benefits of encryption. Encrypting messages in a public " + - "room will make receiving and sending messages slower.", - null, - { "b": (sub) => { sub } }, - ) }

-

{ _t( - "To avoid these issues, create a new public room for the conversation " + - "you plan to have.", - null, - { - "a": (sub) => { - dialog.close(); - this.createNewRoom(true, false); - }}> { sub } , - }, - ) }

-
, - }); - - const { finished } = dialog; - const [confirm] = await finished; - if (!confirm) return; - } - - if (beforeJoinRule === joinRule && !restrictedAllowRoomIds) return; - - const content: IContent = { - join_rule: joinRule, - }; - - // pre-set the accepted spaces with the currently viewed one as per the microcopy - if (joinRule === JoinRule.Restricted) { - content.allow = restrictedAllowRoomIds.map(roomId => ({ - "type": RestrictedAllowType.RoomMembership, - "room_id": roomId, - })); - } - - this.setState({ joinRule, restrictedAllowRoomIds }); - - const client = MatrixClientPeg.get(); - client.sendStateEvent(this.props.roomId, EventType.RoomJoinRules, content, "").catch((e) => { - console.error(e); - this.setState({ - joinRule: beforeJoinRule, - restrictedAllowRoomIds: undefined, - }); - }); - }; - - private onRestrictedRoomIdsChange = (restrictedAllowRoomIds: string[]) => { - const beforeRestrictedAllowRoomIds = this.state.restrictedAllowRoomIds; - if (!arrayHasDiff(beforeRestrictedAllowRoomIds || [], restrictedAllowRoomIds)) return; - this.setState({ restrictedAllowRoomIds }); - - const client = MatrixClientPeg.get(); - client.sendStateEvent(this.props.roomId, EventType.RoomJoinRules, { - join_rule: JoinRule.Restricted, - allow: restrictedAllowRoomIds.map(roomId => ({ - "type": RestrictedAllowType.RoomMembership, - "room_id": roomId, - })), - }, "").catch((e) => { - console.error(e); - this.setState({ restrictedAllowRoomIds: beforeRestrictedAllowRoomIds }); - }); - }; - private onGuestAccessChange = (allowed: boolean) => { const guestAccess = allowed ? GuestAccess.CanJoin : GuestAccess.Forbidden; const beforeGuestAccess = this.state.guestAccess; @@ -385,42 +248,12 @@ export default class SecurityRoomSettingsTab extends React.Component => { - let selected = this.state.restrictedAllowRoomIds; - if (!selected?.length && SpaceStore.instance.activeSpace) { - selected = [SpaceStore.instance.activeSpace.roomId]; - } - - const matrixClient = MatrixClientPeg.get(); - const { finished } = Modal.createTrackedDialog('Edit restricted', '', ManageRestrictedJoinRuleDialog, { - matrixClient, - room: matrixClient.getRoom(this.props.roomId), - selected, - }, "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() { const client = MatrixClientPeg.get(); const room = client.getRoom(this.props.roomId); - const joinRule = this.state.joinRule; - - const canChangeJoinRule = room.currentState.mayClientSendStateEvent(EventType.RoomJoinRules, client); let aliasWarning = null; - if (joinRule === JoinRule.Public && !this.state.hasAliases) { + if (room.getJoinRule() === JoinRule.Public && !this.state.hasAliases) { aliasWarning = (
@@ -431,111 +264,68 @@ export default class SecurityRoomSettingsTab extends React.Component[] = [{ - value: JoinRule.Invite, - label: _t("Private (invite only)"), - 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, - label: _t("Public"), - description: _t("Anyone can find and join."), - }]; - - if (this.state.roomSupportsRestricted || - this.state.preferredRestrictionVersion || - joinRule === JoinRule.Restricted - ) { - let upgradeRequiredPill; - if (this.state.preferredRestrictionVersion) { - upgradeRequiredPill = - { _t("Upgrade required") } - ; - } - - let description; - if (joinRule === JoinRule.Restricted && this.state.restrictedAllowRoomIds?.length) { - const shownSpaces = this.state.restrictedAllowRoomIds - .map(roomId => client.getRoom(roomId)) - .filter(room => room?.isSpaceRoom()) - .slice(0, 4); - - let moreText; - if (shownSpaces.length < this.state.restrictedAllowRoomIds.length) { - if (shownSpaces.length > 0) { - moreText = _t("& %(count)s more", { - count: this.state.restrictedAllowRoomIds.length - shownSpaces.length, - }); - } else { - moreText = _t("Currently, %(count)s spaces have access", { - count: this.state.restrictedAllowRoomIds.length, - }); - } - } + return
+
+ { _t("Decide who can join %(roomName)s.", { + roomName: room?.name, + }) } +
- description =
- - { _t("Anyone in a space can find and join. Edit which spaces can access here.", {}, { - a: sub => - { sub } - , - }) } - + { aliasWarning } -
-

{ _t("Spaces with access") }

- { shownSpaces.map(room => { - return - - { room.name } - ; - }) } - { moreText && { moreText } } -
-
; - } else if (SpaceStore.instance.activeSpace) { - description = _t("Anyone in %(spaceName)s can find and join. You can select other spaces too.", { - spaceName: SpaceStore.instance.activeSpace.name, - }); - } else { - description = _t("Anyone in a space can find and join. You can select multiple spaces."); - } - - radioDefinitions.splice(1, 0, { - value: JoinRule.Restricted, - label: <> - { _t("Space members") } - { upgradeRequiredPill } - , - description, - // if there are 0 allowed spaces then render it as invite only instead - checked: this.state.joinRule === JoinRule.Restricted && !!this.state.restrictedAllowRoomIds?.length, + +
; + } + + private onJoinRuleChangeError = (error: Error) => { + Modal.createTrackedDialog('Room not found', '', ErrorDialog, { + title: _t("Failed to update the join rules"), + description: error.message ?? _t("Unknown failure"), + }); + }; + + private onBeforeJoinRuleChange = async (joinRule: JoinRule): Promise => { + if (this.state.encrypted && joinRule === JoinRule.Public) { + const dialog = Modal.createTrackedDialog('Confirm Public Encrypted Room', '', QuestionDialog, { + title: _t("Are you sure you want to make this encrypted room public?"), + description:
+

{ _t( + "It's not recommended to make encrypted rooms public. " + + "It will mean anyone can find and join the room, so anyone can read messages. " + + "You'll get none of the benefits of encryption. Encrypting messages in a public " + + "room will make receiving and sending messages slower.", + null, + { "b": (sub) => { sub } }, + ) }

+

{ _t( + "To avoid these issues, create a new public room for the conversation " + + "you plan to have.", + null, + { + "a": (sub) => { + dialog.close(); + this.createNewRoom(true, false); + }}> { sub } , + }, + ) }

+
, }); + + const { finished } = dialog; + const [confirm] = await finished; + if (!confirm) return false; } - return ( -
-
- { _t("Decide who can join %(roomName)s.", { - roomName: client.getRoom(this.props.roomId)?.name, - }) } -
- { aliasWarning } - -
- ); - } + return true; + }; private renderHistory() { const client = MatrixClientPeg.get(); diff --git a/src/components/views/spaces/SpaceSettingsVisibilityTab.tsx b/src/components/views/spaces/SpaceSettingsVisibilityTab.tsx index b48f5c79c68..2e9ec773456 100644 --- a/src/components/views/spaces/SpaceSettingsVisibilityTab.tsx +++ b/src/components/views/spaces/SpaceSettingsVisibilityTab.tsx @@ -25,49 +25,20 @@ import AccessibleButton from "../elements/AccessibleButton"; import AliasSettings from "../room_settings/AliasSettings"; import { useStateToggle } from "../../../hooks/useStateToggle"; import LabelledToggleSwitch from "../elements/LabelledToggleSwitch"; -import StyledRadioGroup from "../elements/StyledRadioGroup"; +import { useLocalEcho } from "../../../hooks/useLocalEcho"; +import JoinRuleSettings from "../settings/JoinRuleSettings"; interface IProps { matrixClient: MatrixClient; space: Room; + closeSettingsFn(): void; } -enum SpaceVisibility { - Unlisted = "unlisted", - Private = "private", -} - -const useLocalEcho = ( - currentFactory: () => T, - setterFn: (value: T) => Promise, - errorFn: (error: Error) => void, -): [value: T, handler: (value: T) => void] => { - const [value, setValue] = useState(currentFactory); - const handler = async (value: T) => { - setValue(value); - try { - await setterFn(value); - } catch (e) { - setValue(currentFactory()); - errorFn(e); - } - }; - - return [value, handler]; -}; - -const SpaceSettingsVisibilityTab = ({ matrixClient: cli, space }: IProps) => { +const SpaceSettingsVisibilityTab = ({ matrixClient: cli, space, closeSettingsFn }: IProps) => { const [error, setError] = useState(""); const userId = cli.getUserId(); - const [visibility, setVisibility] = useLocalEcho( - () => space.getJoinRule() === JoinRule.Invite ? SpaceVisibility.Private : SpaceVisibility.Unlisted, - visibility => cli.sendStateEvent(space.roomId, EventType.RoomJoinRules, { - join_rule: visibility === SpaceVisibility.Unlisted ? JoinRule.Public : JoinRule.Invite, - }, ""), - () => setError(_t("Failed to update the visibility of this space")), - ); const [guestAccessEnabled, setGuestAccessEnabled] = useLocalEcho( () => space.currentState.getStateEvents(EventType.RoomGuestAccess, "") ?.getContent()?.guest_access === GuestAccess.CanJoin, @@ -87,7 +58,6 @@ const SpaceSettingsVisibilityTab = ({ matrixClient: cli, space }: IProps) => { const [showAdvancedSection, toggleAdvancedSection] = useStateToggle(); - const canSetJoinRule = space.currentState.maySendStateEvent(EventType.RoomJoinRules, userId); const canSetGuestAccess = space.currentState.maySendStateEvent(EventType.RoomGuestAccess, userId); const canSetHistoryVisibility = space.currentState.maySendStateEvent(EventType.RoomHistoryVisibility, userId); const canSetCanonical = space.currentState.mayClientSendStateEvent(EventType.RoomCanonicalAlias, cli); @@ -121,7 +91,7 @@ const SpaceSettingsVisibilityTab = ({ matrixClient: cli, space }: IProps) => { } let addressesSection; - if (visibility !== SpaceVisibility.Private) { + if (space.getJoinRule() === JoinRule.Public) { addressesSection = <> { _t("Address") }
@@ -147,22 +117,10 @@ const SpaceSettingsVisibilityTab = ({ matrixClient: cli, space }: IProps) => {
- setError(_t("Failed to update the visibility of this space"))} + closeSettingsFn={closeSettingsFn} />
diff --git a/src/components/views/spaces/SpaceTreeLevel.tsx b/src/components/views/spaces/SpaceTreeLevel.tsx index dda58ae9444..35c02752406 100644 --- a/src/components/views/spaces/SpaceTreeLevel.tsx +++ b/src/components/views/spaces/SpaceTreeLevel.tsx @@ -260,7 +260,7 @@ export class SpaceItem extends React.PureComponent { render() { // eslint-disable-next-line @typescript-eslint/no-unused-vars - const { space, activeSpaces, isNested, isPanelCollapsed, onExpand, parents, innerRef, + const { space, activeSpaces, isNested, isPanelCollapsed, onExpand, parents, innerRef, dragHandleProps, ...otherProps } = this.props; const collapsed = this.isCollapsed; @@ -299,7 +299,7 @@ export class SpaceItem extends React.PureComponent { /> : null; // eslint-disable-next-line @typescript-eslint/no-unused-vars - const { tabIndex, ...dragHandleProps } = this.props.dragHandleProps || {}; + const { tabIndex, ...restDragHandleProps } = dragHandleProps || {}; return (
  • { role="treeitem" > ( + currentFactory: () => T, + setterFn: (value: T) => Promise, + errorFn: (error: Error) => void, +): [value: T, handler: (value: T) => void] => { + const [value, setValue] = useState(currentFactory); + const handler = async (value: T) => { + setValue(value); + try { + await setterFn(value); + } catch (e) { + setValue(currentFactory()); + errorFn(e); + } + }; + + return [value, handler]; +}; diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index deb854868fd..a381f7ff723 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -1065,7 +1065,6 @@ "Saving...": "Saving...", "Save Changes": "Save Changes", "Leave Space": "Leave Space", - "Failed to update the visibility of this space": "Failed to update the visibility of this space", "Failed to update the guest access of this space": "Failed to update the guest access of this space", "Failed to update the history visibility of this space": "Failed to update the history visibility of this space", "Hide advanced": "Hide advanced", @@ -1075,9 +1074,7 @@ "Show advanced": "Show advanced", "Visibility": "Visibility", "Decide who can view and join %(spaceName)s.": "Decide who can view and join %(spaceName)s.", - "anyone with the link can view and join": "anyone with the link can view and join", - "Invite only": "Invite only", - "only invited people can view and join": "only invited people can view and join", + "Failed to update the visibility of this space": "Failed to update the visibility of this space", "Preview Space": "Preview Space", "Allow people to preview your space before they join.": "Allow people to preview your space before they join.", "Recommended for public spaces.": "Recommended for public spaces.", @@ -1151,6 +1148,20 @@ "Connecting to integration manager...": "Connecting to integration manager...", "Cannot connect to integration manager": "Cannot connect to integration manager", "The integration manager is offline or it cannot reach your homeserver.": "The integration manager is offline or it cannot reach your homeserver.", + "Private (invite only)": "Private (invite only)", + "Only invited people can join.": "Only invited people can join.", + "Anyone can find and join.": "Anyone can find and join.", + "Upgrade required": "Upgrade required", + "& %(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, a space has access", + "Anyone in a space can find and join. Edit which spaces can access here.": "Anyone in a space can find and join. Edit which spaces can access here.", + "Spaces with access": "Spaces with access", + "Anyone in can find and join. You can select other spaces too.": "Anyone in 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.", + "Space members": "Space members", + "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.", "Message layout": "Message layout", "IRC": "IRC", "Modern": "Modern", @@ -1459,25 +1470,13 @@ "To avoid these issues, create a new encrypted room for the conversation you plan to have.": "To avoid these issues, create a new encrypted room for the conversation you plan to have.", "Enable encryption?": "Enable encryption?", "Once enabled, encryption for a room cannot be disabled. Messages sent in an encrypted room cannot be seen by the server, only by the participants of the room. Enabling encryption may prevent many bots and bridges from working correctly. Learn more about encryption.": "Once enabled, encryption for a room cannot be disabled. Messages sent in an encrypted room cannot be seen by the server, only by the participants of the room. Enabling encryption may prevent many bots and bridges from working correctly. Learn more about encryption.", - "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.", + "To link to this room, please add an address.": "To link to this room, please add an address.", + "Decide who can join %(roomName)s.": "Decide who can join %(roomName)s.", + "Failed to update the join rules": "Failed to update the join rules", + "Unknown failure": "Unknown failure", "Are you sure you want to make this encrypted room public?": "Are you sure you want to make this encrypted room public?", "It's not recommended to make encrypted rooms public. It will mean anyone can find and join the room, so anyone can read messages. You'll get none of the benefits of encryption. Encrypting messages in a public room will make receiving and sending messages slower.": "It's not recommended to make encrypted rooms public. It will mean anyone can find and join the room, so anyone can read messages. You'll get none of the benefits of encryption. Encrypting messages in a public room will make receiving and sending messages slower.", "To avoid these issues, create a new public room for the conversation you plan to have.": "To avoid these issues, create a new public room for the conversation you plan to have.", - "To link to this room, please add an address.": "To link to this room, please add an address.", - "Private (invite only)": "Private (invite only)", - "Only invited people can join.": "Only invited people can join.", - "Anyone can find and join.": "Anyone can find and join.", - "Upgrade required": "Upgrade required", - "& %(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, a space has access", - "Anyone in a space can find and join. Edit which spaces can access here.": "Anyone in a space can find and join. Edit which spaces can access here.", - "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 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", - "Decide who can join %(roomName)s.": "Decide who can join %(roomName)s.", "Members only (since the point in time of selecting this option)": "Members only (since the point in time of selecting this option)", "Members only (since they were invited)": "Members only (since they were invited)", "Members only (since they joined)": "Members only (since they joined)",