2017-05-08 13:18:31 +03:00
|
|
|
|
/*
|
2017-08-17 15:33:07 +03:00
|
|
|
|
Copyright 2017 Michael Telatynski <7t3chguy@gmail.com>
|
2020-01-23 16:54:28 +03:00
|
|
|
|
Copyright 2020 The Matrix.org Foundation C.I.C.
|
2017-05-08 13:18:31 +03:00
|
|
|
|
|
|
|
|
|
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';
|
2017-12-26 04:03:18 +03:00
|
|
|
|
import PropTypes from 'prop-types';
|
2021-03-02 16:28:05 +03:00
|
|
|
|
import {Room} from "matrix-js-sdk/src/models/room";
|
|
|
|
|
|
2019-12-20 04:19:56 +03:00
|
|
|
|
import * as sdk from '../../../index';
|
2017-08-17 15:33:07 +03:00
|
|
|
|
import SdkConfig from '../../../SdkConfig';
|
2019-09-20 18:39:46 +03:00
|
|
|
|
import withValidation from '../elements/Validation';
|
2017-08-02 15:41:26 +03:00
|
|
|
|
import { _t } from '../../../languageHandler';
|
2019-12-21 00:13:46 +03:00
|
|
|
|
import {MatrixClientPeg} from '../../../MatrixClientPeg';
|
2019-10-09 21:59:11 +03:00
|
|
|
|
import {Key} from "../../../Keyboard";
|
2020-05-19 13:36:44 +03:00
|
|
|
|
import {privateShouldBeEncrypted} from "../../../createRoom";
|
2020-08-28 23:56:59 +03:00
|
|
|
|
import {CommunityPrototypeStore} from "../../../stores/CommunityPrototypeStore";
|
2017-05-08 13:18:31 +03:00
|
|
|
|
|
2020-08-29 14:14:16 +03:00
|
|
|
|
export default class CreateRoomDialog extends React.Component {
|
|
|
|
|
static propTypes = {
|
2017-12-26 04:03:18 +03:00
|
|
|
|
onFinished: PropTypes.func.isRequired,
|
2020-05-12 12:55:46 +03:00
|
|
|
|
defaultPublic: PropTypes.bool,
|
2021-03-02 16:28:05 +03:00
|
|
|
|
parentSpace: PropTypes.instanceOf(Room),
|
2020-08-29 14:14:16 +03:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
constructor(props) {
|
|
|
|
|
super(props);
|
2017-05-08 13:18:31 +03:00
|
|
|
|
|
2017-08-17 15:33:07 +03:00
|
|
|
|
const config = SdkConfig.get();
|
2020-08-29 14:14:16 +03:00
|
|
|
|
this.state = {
|
2020-05-12 12:55:46 +03:00
|
|
|
|
isPublic: this.props.defaultPublic || false,
|
2020-05-19 13:36:44 +03:00
|
|
|
|
isEncrypted: privateShouldBeEncrypted(),
|
2019-09-20 18:39:46 +03:00
|
|
|
|
name: "",
|
2019-09-20 18:41:03 +03:00
|
|
|
|
topic: "",
|
2019-09-20 18:46:14 +03:00
|
|
|
|
alias: "",
|
2019-09-20 18:44:07 +03:00
|
|
|
|
detailsOpen: false,
|
2019-09-20 18:39:46 +03:00
|
|
|
|
noFederate: config.default_federate === false,
|
|
|
|
|
nameIsValid: false,
|
2020-09-18 07:46:01 +03:00
|
|
|
|
canChangeEncryption: true,
|
2019-09-20 18:39:46 +03:00
|
|
|
|
};
|
2020-09-18 07:46:01 +03:00
|
|
|
|
|
|
|
|
|
MatrixClientPeg.get().doesServerForceEncryptionForPreset("private")
|
|
|
|
|
.then(isForced => this.setState({canChangeEncryption: !isForced}));
|
2020-08-29 14:14:16 +03:00
|
|
|
|
}
|
2017-05-08 13:18:31 +03:00
|
|
|
|
|
2019-09-20 18:39:46 +03:00
|
|
|
|
_roomCreateOptions() {
|
2020-01-23 16:54:28 +03:00
|
|
|
|
const opts = {};
|
|
|
|
|
const createOpts = opts.createOpts = {};
|
2019-09-20 18:39:46 +03:00
|
|
|
|
createOpts.name = this.state.name;
|
2019-09-20 18:43:14 +03:00
|
|
|
|
if (this.state.isPublic) {
|
|
|
|
|
createOpts.visibility = "public";
|
|
|
|
|
createOpts.preset = "public_chat";
|
2020-01-23 16:54:28 +03:00
|
|
|
|
opts.guestAccess = false;
|
2019-09-20 18:46:14 +03:00
|
|
|
|
const {alias} = this.state;
|
|
|
|
|
const localPart = alias.substr(1, alias.indexOf(":") - 1);
|
|
|
|
|
createOpts['room_alias_name'] = localPart;
|
|
|
|
|
}
|
2019-09-20 18:41:03 +03:00
|
|
|
|
if (this.state.topic) {
|
|
|
|
|
createOpts.topic = this.state.topic;
|
|
|
|
|
}
|
2019-09-20 18:39:46 +03:00
|
|
|
|
if (this.state.noFederate) {
|
|
|
|
|
createOpts.creation_content = {'m.federate': false};
|
|
|
|
|
}
|
2020-04-08 12:50:22 +03:00
|
|
|
|
|
2020-05-27 12:28:25 +03:00
|
|
|
|
if (!this.state.isPublic) {
|
2020-09-18 07:46:01 +03:00
|
|
|
|
if (this.state.canChangeEncryption) {
|
|
|
|
|
opts.encryption = this.state.isEncrypted;
|
|
|
|
|
} else {
|
|
|
|
|
// the server should automatically do this for us, but for safety
|
|
|
|
|
// we'll demand it too.
|
|
|
|
|
opts.encryption = true;
|
|
|
|
|
}
|
2020-04-08 12:50:22 +03:00
|
|
|
|
}
|
|
|
|
|
|
2020-08-31 19:19:05 +03:00
|
|
|
|
if (CommunityPrototypeStore.instance.getSelectedCommunityId()) {
|
|
|
|
|
opts.associatedWithCommunity = CommunityPrototypeStore.instance.getSelectedCommunityId();
|
2020-08-26 19:53:06 +03:00
|
|
|
|
}
|
|
|
|
|
|
2021-03-02 16:28:05 +03:00
|
|
|
|
if (this.props.parentSpace) {
|
|
|
|
|
opts.parentSpace = this.props.parentSpace;
|
|
|
|
|
}
|
|
|
|
|
|
2020-01-23 16:54:28 +03:00
|
|
|
|
return opts;
|
2020-08-29 14:14:16 +03:00
|
|
|
|
}
|
2019-09-20 18:39:46 +03:00
|
|
|
|
|
|
|
|
|
componentDidMount() {
|
|
|
|
|
this._detailsRef.addEventListener("toggle", this.onDetailsToggled);
|
2019-09-25 16:08:48 +03:00
|
|
|
|
// move focus to first field when showing dialog
|
|
|
|
|
this._nameFieldRef.focus();
|
2020-08-29 14:14:16 +03:00
|
|
|
|
}
|
2019-09-20 18:39:46 +03:00
|
|
|
|
|
|
|
|
|
componentWillUnmount() {
|
|
|
|
|
this._detailsRef.removeEventListener("toggle", this.onDetailsToggled);
|
2020-08-29 14:14:16 +03:00
|
|
|
|
}
|
2019-09-20 18:39:46 +03:00
|
|
|
|
|
2020-08-29 14:14:16 +03:00
|
|
|
|
_onKeyDown = event => {
|
2019-10-09 21:59:11 +03:00
|
|
|
|
if (event.key === Key.ENTER) {
|
2019-10-02 17:26:52 +03:00
|
|
|
|
this.onOk();
|
|
|
|
|
event.preventDefault();
|
|
|
|
|
event.stopPropagation();
|
|
|
|
|
}
|
2020-08-29 14:14:16 +03:00
|
|
|
|
};
|
2019-10-02 17:26:52 +03:00
|
|
|
|
|
2020-08-29 14:14:16 +03:00
|
|
|
|
onOk = async () => {
|
2019-09-20 18:46:47 +03:00
|
|
|
|
const activeElement = document.activeElement;
|
|
|
|
|
if (activeElement) {
|
|
|
|
|
activeElement.blur();
|
|
|
|
|
}
|
|
|
|
|
await this._nameFieldRef.validate({allowEmpty: false});
|
|
|
|
|
if (this._aliasFieldRef) {
|
|
|
|
|
await this._aliasFieldRef.validate({allowEmpty: false});
|
|
|
|
|
}
|
|
|
|
|
// Validation and state updates are async, so we need to wait for them to complete
|
|
|
|
|
// first. Queue a `setState` callback and wait for it to resolve.
|
|
|
|
|
await new Promise(resolve => this.setState({}, resolve));
|
|
|
|
|
if (this.state.nameIsValid && (!this._aliasFieldRef || this._aliasFieldRef.isValid)) {
|
2019-09-20 18:39:46 +03:00
|
|
|
|
this.props.onFinished(true, this._roomCreateOptions());
|
2019-09-20 18:46:47 +03:00
|
|
|
|
} else {
|
|
|
|
|
let field;
|
|
|
|
|
if (!this.state.nameIsValid) {
|
|
|
|
|
field = this._nameFieldRef;
|
|
|
|
|
} else if (this._aliasFieldRef && !this._aliasFieldRef.isValid) {
|
|
|
|
|
field = this._aliasFieldRef;
|
|
|
|
|
}
|
|
|
|
|
if (field) {
|
|
|
|
|
field.focus();
|
|
|
|
|
field.validate({ allowEmpty: false, focused: true });
|
|
|
|
|
}
|
|
|
|
|
}
|
2020-08-29 14:14:16 +03:00
|
|
|
|
};
|
2017-05-08 13:18:31 +03:00
|
|
|
|
|
2020-08-29 14:14:16 +03:00
|
|
|
|
onCancel = () => {
|
2017-05-08 13:18:31 +03:00
|
|
|
|
this.props.onFinished(false);
|
2020-08-29 14:14:16 +03:00
|
|
|
|
};
|
2017-05-08 13:18:31 +03:00
|
|
|
|
|
2020-08-29 14:14:16 +03:00
|
|
|
|
onNameChange = ev => {
|
2019-09-20 18:39:46 +03:00
|
|
|
|
this.setState({name: ev.target.value});
|
2020-08-29 14:14:16 +03:00
|
|
|
|
};
|
2019-09-20 18:39:46 +03:00
|
|
|
|
|
2020-08-29 14:14:16 +03:00
|
|
|
|
onTopicChange = ev => {
|
2019-09-20 18:41:03 +03:00
|
|
|
|
this.setState({topic: ev.target.value});
|
2020-08-29 14:14:16 +03:00
|
|
|
|
};
|
2019-09-20 18:41:03 +03:00
|
|
|
|
|
2020-08-29 14:14:16 +03:00
|
|
|
|
onPublicChange = isPublic => {
|
2019-09-20 18:43:14 +03:00
|
|
|
|
this.setState({isPublic});
|
2020-08-29 14:14:16 +03:00
|
|
|
|
};
|
2019-09-20 18:43:14 +03:00
|
|
|
|
|
2020-08-29 14:14:16 +03:00
|
|
|
|
onEncryptedChange = isEncrypted => {
|
2020-04-08 12:50:22 +03:00
|
|
|
|
this.setState({isEncrypted});
|
2020-08-29 14:14:16 +03:00
|
|
|
|
};
|
2020-04-08 12:50:22 +03:00
|
|
|
|
|
2020-08-29 14:14:16 +03:00
|
|
|
|
onAliasChange = alias => {
|
2019-09-20 18:46:14 +03:00
|
|
|
|
this.setState({alias});
|
2020-08-29 14:14:16 +03:00
|
|
|
|
};
|
2019-09-20 18:39:46 +03:00
|
|
|
|
|
2020-08-29 14:14:16 +03:00
|
|
|
|
onDetailsToggled = ev => {
|
2019-09-20 18:39:46 +03:00
|
|
|
|
this.setState({detailsOpen: ev.target.open});
|
2020-08-29 14:14:16 +03:00
|
|
|
|
};
|
2019-09-20 18:39:46 +03:00
|
|
|
|
|
2020-08-29 14:14:16 +03:00
|
|
|
|
onNoFederateChange = noFederate => {
|
2019-09-20 18:39:46 +03:00
|
|
|
|
this.setState({noFederate});
|
2020-08-29 14:14:16 +03:00
|
|
|
|
};
|
2019-09-20 18:39:46 +03:00
|
|
|
|
|
2020-08-29 14:14:16 +03:00
|
|
|
|
collectDetailsRef = ref => {
|
2019-09-20 18:39:46 +03:00
|
|
|
|
this._detailsRef = ref;
|
2020-08-29 14:14:16 +03:00
|
|
|
|
};
|
2019-09-20 18:39:46 +03:00
|
|
|
|
|
2020-08-29 14:14:16 +03:00
|
|
|
|
onNameValidate = async fieldState => {
|
|
|
|
|
const result = await CreateRoomDialog._validateRoomName(fieldState);
|
2019-09-20 18:39:46 +03:00
|
|
|
|
this.setState({nameIsValid: result.valid});
|
|
|
|
|
return result;
|
2020-08-29 14:14:16 +03:00
|
|
|
|
};
|
2019-09-20 18:39:46 +03:00
|
|
|
|
|
2020-08-29 14:14:16 +03:00
|
|
|
|
static _validateRoomName = withValidation({
|
2019-09-20 18:39:46 +03:00
|
|
|
|
rules: [
|
|
|
|
|
{
|
|
|
|
|
key: "required",
|
|
|
|
|
test: async ({ value }) => !!value,
|
|
|
|
|
invalid: () => _t("Please enter a name for the room"),
|
|
|
|
|
},
|
|
|
|
|
],
|
2020-08-29 14:14:16 +03:00
|
|
|
|
});
|
2019-09-20 18:39:46 +03:00
|
|
|
|
|
2020-08-29 14:14:16 +03:00
|
|
|
|
render() {
|
2017-05-08 13:18:31 +03:00
|
|
|
|
const BaseDialog = sdk.getComponent('views.dialogs.BaseDialog');
|
2017-12-23 03:42:44 +03:00
|
|
|
|
const DialogButtons = sdk.getComponent('views.elements.DialogButtons');
|
2019-09-20 18:39:46 +03:00
|
|
|
|
const Field = sdk.getComponent('views.elements.Field');
|
|
|
|
|
const LabelledToggleSwitch = sdk.getComponent('views.elements.LabelledToggleSwitch');
|
2019-09-20 18:46:14 +03:00
|
|
|
|
const RoomAliasField = sdk.getComponent('views.elements.RoomAliasField');
|
|
|
|
|
|
|
|
|
|
let aliasField;
|
2019-09-20 18:43:14 +03:00
|
|
|
|
if (this.state.isPublic) {
|
2019-09-20 18:46:14 +03:00
|
|
|
|
const domain = MatrixClientPeg.get().getDomain();
|
|
|
|
|
aliasField = (
|
|
|
|
|
<div className="mx_CreateRoomDialog_aliasContainer">
|
2020-03-30 00:59:15 +03:00
|
|
|
|
<RoomAliasField ref={ref => this._aliasFieldRef = ref} onChange={this.onAliasChange} domain={domain} value={this.state.alias} />
|
2019-09-20 18:46:14 +03:00
|
|
|
|
</div>
|
|
|
|
|
);
|
2020-08-27 22:49:40 +03:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let publicPrivateLabel = <p>{_t(
|
|
|
|
|
"Private rooms can be found and joined by invitation only. Public rooms can be " +
|
|
|
|
|
"found and joined by anyone.",
|
|
|
|
|
)}</p>;
|
2020-08-31 19:19:05 +03:00
|
|
|
|
if (CommunityPrototypeStore.instance.getSelectedCommunityId()) {
|
2020-08-27 22:49:40 +03:00
|
|
|
|
publicPrivateLabel = <p>{_t(
|
|
|
|
|
"Private rooms can be found and joined by invitation only. Public rooms can be " +
|
|
|
|
|
"found and joined by anyone in this community.",
|
|
|
|
|
)}</p>;
|
2019-09-20 18:43:14 +03:00
|
|
|
|
}
|
|
|
|
|
|
2020-04-08 12:50:22 +03:00
|
|
|
|
let e2eeSection;
|
2020-05-27 12:28:25 +03:00
|
|
|
|
if (!this.state.isPublic) {
|
2020-06-02 00:59:14 +03:00
|
|
|
|
let microcopy;
|
|
|
|
|
if (privateShouldBeEncrypted()) {
|
2020-09-18 07:46:01 +03:00
|
|
|
|
if (this.state.canChangeEncryption) {
|
|
|
|
|
microcopy = _t("You can’t disable this later. Bridges & most bots won’t work yet.");
|
|
|
|
|
} else {
|
|
|
|
|
microcopy = _t("Your server requires encryption to be enabled in private rooms.");
|
|
|
|
|
}
|
2020-06-02 00:59:14 +03:00
|
|
|
|
} else {
|
|
|
|
|
microcopy = _t("Your server admin has disabled end-to-end encryption by default " +
|
|
|
|
|
"in private rooms & Direct Messages.");
|
|
|
|
|
}
|
2020-04-08 12:50:22 +03:00
|
|
|
|
e2eeSection = <React.Fragment>
|
2020-04-17 01:27:43 +03:00
|
|
|
|
<LabelledToggleSwitch
|
|
|
|
|
label={ _t("Enable end-to-end encryption")}
|
|
|
|
|
onChange={this.onEncryptedChange}
|
|
|
|
|
value={this.state.isEncrypted}
|
|
|
|
|
className='mx_CreateRoomDialog_e2eSwitch' // for end-to-end tests
|
2020-09-18 07:46:01 +03:00
|
|
|
|
disabled={!this.state.canChangeEncryption}
|
2020-04-17 01:27:43 +03:00
|
|
|
|
/>
|
2020-06-02 00:59:14 +03:00
|
|
|
|
<p>{ microcopy }</p>
|
2020-04-08 12:50:22 +03:00
|
|
|
|
</React.Fragment>;
|
|
|
|
|
}
|
|
|
|
|
|
2020-08-27 22:49:40 +03:00
|
|
|
|
let federateLabel = _t(
|
2020-08-28 18:47:36 +03:00
|
|
|
|
"You might enable this if the room will only be used for collaborating with internal " +
|
|
|
|
|
"teams on your homeserver. This cannot be changed later.",
|
2020-08-27 22:49:40 +03:00
|
|
|
|
);
|
|
|
|
|
if (SdkConfig.get().default_federate === false) {
|
|
|
|
|
// We only change the label if the default setting is different to avoid jarring text changes to the
|
|
|
|
|
// user. They will have read the implications of turning this off/on, so no need to rephrase for them.
|
|
|
|
|
federateLabel = _t(
|
|
|
|
|
"You might disable this if the room will be used for collaborating with external " +
|
2020-08-28 18:47:36 +03:00
|
|
|
|
"teams who have their own homeserver. This cannot be changed later.",
|
2020-08-27 22:49:40 +03:00
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
2020-08-26 19:53:06 +03:00
|
|
|
|
let title = this.state.isPublic ? _t('Create a public room') : _t('Create a private room');
|
2020-08-31 19:19:05 +03:00
|
|
|
|
if (CommunityPrototypeStore.instance.getSelectedCommunityId()) {
|
2020-08-28 23:56:59 +03:00
|
|
|
|
const name = CommunityPrototypeStore.instance.getSelectedCommunityName();
|
2020-08-26 19:53:06 +03:00
|
|
|
|
title = _t("Create a room in %(communityName)s", {communityName: name});
|
|
|
|
|
}
|
2017-05-08 13:18:31 +03:00
|
|
|
|
return (
|
2017-08-17 15:33:07 +03:00
|
|
|
|
<BaseDialog className="mx_CreateRoomDialog" onFinished={this.props.onFinished}
|
2019-09-20 18:43:14 +03:00
|
|
|
|
title={title}
|
2017-05-08 13:18:31 +03:00
|
|
|
|
>
|
2019-10-02 17:26:52 +03:00
|
|
|
|
<form onSubmit={this.onOk} onKeyDown={this._onKeyDown}>
|
2017-12-03 23:38:21 +03:00
|
|
|
|
<div className="mx_Dialog_content">
|
2020-03-30 00:59:15 +03:00
|
|
|
|
<Field ref={ref => this._nameFieldRef = ref} label={ _t('Name') } onChange={this.onNameChange} onValidate={this.onNameValidate} value={this.state.name} className="mx_CreateRoomDialog_name" />
|
2020-04-08 15:47:15 +03:00
|
|
|
|
<Field label={ _t('Topic (optional)') } onChange={this.onTopicChange} value={this.state.topic} className="mx_CreateRoomDialog_topic" />
|
2019-09-20 18:43:14 +03:00
|
|
|
|
<LabelledToggleSwitch label={ _t("Make this room public")} onChange={this.onPublicChange} value={this.state.isPublic} />
|
2020-04-08 15:47:15 +03:00
|
|
|
|
{ publicPrivateLabel }
|
2020-04-08 12:50:22 +03:00
|
|
|
|
{ e2eeSection }
|
2019-09-20 18:46:14 +03:00
|
|
|
|
{ aliasField }
|
2019-09-20 18:39:46 +03:00
|
|
|
|
<details ref={this.collectDetailsRef} className="mx_CreateRoomDialog_details">
|
|
|
|
|
<summary className="mx_CreateRoomDialog_details_summary">{ this.state.detailsOpen ? _t('Hide advanced') : _t('Show advanced') }</summary>
|
2020-08-27 22:49:40 +03:00
|
|
|
|
<LabelledToggleSwitch
|
|
|
|
|
label={_t(
|
|
|
|
|
"Block anyone not part of %(serverName)s from ever joining this room.",
|
|
|
|
|
{serverName: MatrixClientPeg.getHomeserverName()},
|
|
|
|
|
)}
|
|
|
|
|
onChange={this.onNoFederateChange}
|
|
|
|
|
value={this.state.noFederate}
|
|
|
|
|
/>
|
|
|
|
|
<p>{federateLabel}</p>
|
2018-02-08 23:16:57 +03:00
|
|
|
|
</details>
|
2017-12-03 23:38:21 +03:00
|
|
|
|
</div>
|
|
|
|
|
</form>
|
2017-12-23 03:42:44 +03:00
|
|
|
|
<DialogButtons primaryButton={_t('Create Room')}
|
|
|
|
|
onPrimaryButtonClick={this.onOk}
|
|
|
|
|
onCancel={this.onCancel} />
|
2017-05-08 13:18:31 +03:00
|
|
|
|
</BaseDialog>
|
|
|
|
|
);
|
2020-08-29 14:14:16 +03:00
|
|
|
|
}
|
|
|
|
|
}
|