Convert CreateRoomDialog to Typescript
parent
d10a45c6a3
commit
b3aade075d
|
@ -1,6 +1,6 @@
|
||||||
/*
|
/*
|
||||||
Copyright 2017 Michael Telatynski <7t3chguy@gmail.com>
|
Copyright 2017 Michael Telatynski <7t3chguy@gmail.com>
|
||||||
Copyright 2020 The Matrix.org Foundation C.I.C.
|
Copyright 2020, 2021 The Matrix.org Foundation C.I.C.
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
you may not use this file except in compliance with the License.
|
you may not use this file except in compliance with the License.
|
||||||
|
@ -15,27 +15,45 @@ See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import React from 'react';
|
import React, {ChangeEvent, createRef, KeyboardEvent, SyntheticEvent} from "react";
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
import {Room} from "matrix-js-sdk/src/models/room";
|
import {Room} from "matrix-js-sdk/src/models/room";
|
||||||
|
|
||||||
import * as sdk from '../../../index';
|
|
||||||
import SdkConfig from '../../../SdkConfig';
|
import SdkConfig from '../../../SdkConfig';
|
||||||
import withValidation from '../elements/Validation';
|
import withValidation, {IFieldState} from '../elements/Validation';
|
||||||
import {_t} from '../../../languageHandler';
|
import {_t} from '../../../languageHandler';
|
||||||
import {MatrixClientPeg} from '../../../MatrixClientPeg';
|
import {MatrixClientPeg} from '../../../MatrixClientPeg';
|
||||||
import {Key} from "../../../Keyboard";
|
import {Key} from "../../../Keyboard";
|
||||||
import {privateShouldBeEncrypted} from "../../../createRoom";
|
import {IOpts, Preset, privateShouldBeEncrypted, Visibility} from "../../../createRoom";
|
||||||
import {CommunityPrototypeStore} from "../../../stores/CommunityPrototypeStore";
|
import {CommunityPrototypeStore} from "../../../stores/CommunityPrototypeStore";
|
||||||
import {replaceableComponent} from "../../../utils/replaceableComponent";
|
import {replaceableComponent} from "../../../utils/replaceableComponent";
|
||||||
|
import Field from "../elements/Field";
|
||||||
|
import RoomAliasField from "../elements/RoomAliasField";
|
||||||
|
import LabelledToggleSwitch from "../elements/LabelledToggleSwitch";
|
||||||
|
import DialogButtons from "../elements/DialogButtons";
|
||||||
|
import BaseDialog from "../dialogs/BaseDialog";
|
||||||
|
|
||||||
|
interface IProps {
|
||||||
|
defaultPublic?: boolean;
|
||||||
|
parentSpace?: Room;
|
||||||
|
onFinished(proceed: boolean, opts?: IOpts): void;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface IState {
|
||||||
|
isPublic: boolean;
|
||||||
|
isEncrypted: boolean;
|
||||||
|
name: string;
|
||||||
|
topic: string;
|
||||||
|
alias: string;
|
||||||
|
detailsOpen: boolean;
|
||||||
|
noFederate: boolean;
|
||||||
|
nameIsValid: boolean;
|
||||||
|
canChangeEncryption: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
@replaceableComponent("views.dialogs.CreateRoomDialog")
|
@replaceableComponent("views.dialogs.CreateRoomDialog")
|
||||||
export default class CreateRoomDialog extends React.Component {
|
export default class CreateRoomDialog extends React.Component<IProps, IState> {
|
||||||
static propTypes = {
|
private nameField = createRef<Field>();
|
||||||
onFinished: PropTypes.func.isRequired,
|
private aliasField = createRef<RoomAliasField>();
|
||||||
defaultPublic: PropTypes.bool,
|
|
||||||
parentSpace: PropTypes.instanceOf(Room),
|
|
||||||
};
|
|
||||||
|
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
super(props);
|
super(props);
|
||||||
|
@ -57,17 +75,16 @@ export default class CreateRoomDialog extends React.Component {
|
||||||
.then(isForced => this.setState({ canChangeEncryption: !isForced }));
|
.then(isForced => this.setState({ canChangeEncryption: !isForced }));
|
||||||
}
|
}
|
||||||
|
|
||||||
_roomCreateOptions() {
|
private roomCreateOptions() {
|
||||||
const opts = {};
|
const opts: IOpts = {};
|
||||||
const createOpts = opts.createOpts = {};
|
const createOpts: IOpts["createOpts"] = opts.createOpts = {};
|
||||||
createOpts.name = this.state.name;
|
createOpts.name = this.state.name;
|
||||||
if (this.state.isPublic) {
|
if (this.state.isPublic) {
|
||||||
createOpts.visibility = "public";
|
createOpts.visibility = Visibility.Public;
|
||||||
createOpts.preset = "public_chat";
|
createOpts.preset = Preset.PublicChat;
|
||||||
opts.guestAccess = false;
|
opts.guestAccess = false;
|
||||||
const { alias } = this.state;
|
const { alias } = this.state;
|
||||||
const localPart = alias.substr(1, alias.indexOf(":") - 1);
|
createOpts.room_alias_name = alias.substr(1, alias.indexOf(":") - 1);
|
||||||
createOpts['room_alias_name'] = localPart;
|
|
||||||
}
|
}
|
||||||
if (this.state.topic) {
|
if (this.state.topic) {
|
||||||
createOpts.topic = this.state.topic;
|
createOpts.topic = this.state.topic;
|
||||||
|
@ -98,16 +115,14 @@ export default class CreateRoomDialog extends React.Component {
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
this._detailsRef.addEventListener("toggle", this.onDetailsToggled);
|
|
||||||
// move focus to first field when showing dialog
|
// move focus to first field when showing dialog
|
||||||
this._nameFieldRef.focus();
|
this.nameField.current.focus();
|
||||||
}
|
}
|
||||||
|
|
||||||
componentWillUnmount() {
|
componentWillUnmount() {
|
||||||
this._detailsRef.removeEventListener("toggle", this.onDetailsToggled);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
_onKeyDown = event => {
|
private onKeyDown = (event: KeyboardEvent) => {
|
||||||
if (event.key === Key.ENTER) {
|
if (event.key === Key.ENTER) {
|
||||||
this.onOk();
|
this.onOk();
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
|
@ -115,26 +130,26 @@ export default class CreateRoomDialog extends React.Component {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
onOk = async () => {
|
private onOk = async () => {
|
||||||
const activeElement = document.activeElement;
|
const activeElement = document.activeElement as HTMLElement;
|
||||||
if (activeElement) {
|
if (activeElement) {
|
||||||
activeElement.blur();
|
activeElement.blur();
|
||||||
}
|
}
|
||||||
await this._nameFieldRef.validate({allowEmpty: false});
|
await this.nameField.current.validate({allowEmpty: false});
|
||||||
if (this._aliasFieldRef) {
|
if (this.aliasField.current) {
|
||||||
await this._aliasFieldRef.validate({allowEmpty: false});
|
await this.aliasField.current.validate({allowEmpty: false});
|
||||||
}
|
}
|
||||||
// Validation and state updates are async, so we need to wait for them to complete
|
// 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.
|
// first. Queue a `setState` callback and wait for it to resolve.
|
||||||
await new Promise(resolve => this.setState({}, resolve));
|
await new Promise<void>(resolve => this.setState({}, resolve));
|
||||||
if (this.state.nameIsValid && (!this._aliasFieldRef || this._aliasFieldRef.isValid)) {
|
if (this.state.nameIsValid && (!this.aliasField.current || this.aliasField.current.isValid)) {
|
||||||
this.props.onFinished(true, this._roomCreateOptions());
|
this.props.onFinished(true, this.roomCreateOptions());
|
||||||
} else {
|
} else {
|
||||||
let field;
|
let field;
|
||||||
if (!this.state.nameIsValid) {
|
if (!this.state.nameIsValid) {
|
||||||
field = this._nameFieldRef;
|
field = this.nameField.current;
|
||||||
} else if (this._aliasFieldRef && !this._aliasFieldRef.isValid) {
|
} else if (this.aliasField.current && !this.aliasField.current.isValid) {
|
||||||
field = this._aliasFieldRef;
|
field = this.aliasField.current;
|
||||||
}
|
}
|
||||||
if (field) {
|
if (field) {
|
||||||
field.focus();
|
field.focus();
|
||||||
|
@ -143,49 +158,45 @@ export default class CreateRoomDialog extends React.Component {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
onCancel = () => {
|
private onCancel = () => {
|
||||||
this.props.onFinished(false);
|
this.props.onFinished(false);
|
||||||
};
|
};
|
||||||
|
|
||||||
onNameChange = ev => {
|
private onNameChange = (ev: ChangeEvent<HTMLInputElement>) => {
|
||||||
this.setState({ name: ev.target.value });
|
this.setState({ name: ev.target.value });
|
||||||
};
|
};
|
||||||
|
|
||||||
onTopicChange = ev => {
|
private onTopicChange = (ev: ChangeEvent<HTMLInputElement>) => {
|
||||||
this.setState({ topic: ev.target.value });
|
this.setState({ topic: ev.target.value });
|
||||||
};
|
};
|
||||||
|
|
||||||
onPublicChange = isPublic => {
|
private onPublicChange = (isPublic: boolean) => {
|
||||||
this.setState({ isPublic });
|
this.setState({ isPublic });
|
||||||
};
|
};
|
||||||
|
|
||||||
onEncryptedChange = isEncrypted => {
|
private onEncryptedChange = (isEncrypted: boolean) => {
|
||||||
this.setState({ isEncrypted });
|
this.setState({ isEncrypted });
|
||||||
};
|
};
|
||||||
|
|
||||||
onAliasChange = alias => {
|
private onAliasChange = (alias: string) => {
|
||||||
this.setState({ alias });
|
this.setState({ alias });
|
||||||
};
|
};
|
||||||
|
|
||||||
onDetailsToggled = ev => {
|
private onDetailsToggled = (ev: SyntheticEvent<HTMLDetailsElement>) => {
|
||||||
this.setState({detailsOpen: ev.target.open});
|
this.setState({ detailsOpen: (ev.target as HTMLDetailsElement).open });
|
||||||
};
|
};
|
||||||
|
|
||||||
onNoFederateChange = noFederate => {
|
private onNoFederateChange = (noFederate: boolean) => {
|
||||||
this.setState({ noFederate });
|
this.setState({ noFederate });
|
||||||
};
|
};
|
||||||
|
|
||||||
collectDetailsRef = ref => {
|
private onNameValidate = async (fieldState: IFieldState) => {
|
||||||
this._detailsRef = ref;
|
const result = await CreateRoomDialog.validateRoomName(fieldState);
|
||||||
};
|
|
||||||
|
|
||||||
onNameValidate = async fieldState => {
|
|
||||||
const result = await CreateRoomDialog._validateRoomName(fieldState);
|
|
||||||
this.setState({nameIsValid: result.valid});
|
this.setState({nameIsValid: result.valid});
|
||||||
return result;
|
return result;
|
||||||
};
|
};
|
||||||
|
|
||||||
static _validateRoomName = withValidation({
|
private static validateRoomName = withValidation({
|
||||||
rules: [
|
rules: [
|
||||||
{
|
{
|
||||||
key: "required",
|
key: "required",
|
||||||
|
@ -196,18 +207,17 @@ export default class CreateRoomDialog extends React.Component {
|
||||||
});
|
});
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const BaseDialog = sdk.getComponent('views.dialogs.BaseDialog');
|
|
||||||
const DialogButtons = sdk.getComponent('views.elements.DialogButtons');
|
|
||||||
const Field = sdk.getComponent('views.elements.Field');
|
|
||||||
const LabelledToggleSwitch = sdk.getComponent('views.elements.LabelledToggleSwitch');
|
|
||||||
const RoomAliasField = sdk.getComponent('views.elements.RoomAliasField');
|
|
||||||
|
|
||||||
let aliasField;
|
let aliasField;
|
||||||
if (this.state.isPublic) {
|
if (this.state.isPublic) {
|
||||||
const domain = MatrixClientPeg.get().getDomain();
|
const domain = MatrixClientPeg.get().getDomain();
|
||||||
aliasField = (
|
aliasField = (
|
||||||
<div className="mx_CreateRoomDialog_aliasContainer">
|
<div className="mx_CreateRoomDialog_aliasContainer">
|
||||||
<RoomAliasField ref={ref => this._aliasFieldRef = ref} onChange={this.onAliasChange} domain={domain} value={this.state.alias} />
|
<RoomAliasField
|
||||||
|
ref={this.aliasField}
|
||||||
|
onChange={this.onAliasChange}
|
||||||
|
domain={domain}
|
||||||
|
value={this.state.alias}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -270,16 +280,34 @@ export default class CreateRoomDialog extends React.Component {
|
||||||
<BaseDialog className="mx_CreateRoomDialog" onFinished={this.props.onFinished}
|
<BaseDialog className="mx_CreateRoomDialog" onFinished={this.props.onFinished}
|
||||||
title={title}
|
title={title}
|
||||||
>
|
>
|
||||||
<form onSubmit={this.onOk} onKeyDown={this._onKeyDown}>
|
<form onSubmit={this.onOk} onKeyDown={this.onKeyDown}>
|
||||||
<div className="mx_Dialog_content">
|
<div className="mx_Dialog_content">
|
||||||
<Field ref={ref => this._nameFieldRef = ref} label={ _t('Name') } onChange={this.onNameChange} onValidate={this.onNameValidate} value={this.state.name} className="mx_CreateRoomDialog_name" />
|
<Field
|
||||||
<Field label={ _t('Topic (optional)') } onChange={this.onTopicChange} value={this.state.topic} className="mx_CreateRoomDialog_topic" />
|
ref={this.nameField}
|
||||||
<LabelledToggleSwitch label={ _t("Make this room public")} onChange={this.onPublicChange} value={this.state.isPublic} />
|
label={_t('Name')}
|
||||||
|
onChange={this.onNameChange}
|
||||||
|
onValidate={this.onNameValidate}
|
||||||
|
value={this.state.name}
|
||||||
|
className="mx_CreateRoomDialog_name"
|
||||||
|
/>
|
||||||
|
<Field
|
||||||
|
label={_t('Topic (optional)')}
|
||||||
|
onChange={this.onTopicChange}
|
||||||
|
value={this.state.topic}
|
||||||
|
className="mx_CreateRoomDialog_topic"
|
||||||
|
/>
|
||||||
|
<LabelledToggleSwitch
|
||||||
|
label={_t("Make this room public")}
|
||||||
|
onChange={this.onPublicChange}
|
||||||
|
value={this.state.isPublic}
|
||||||
|
/>
|
||||||
{ publicPrivateLabel }
|
{ publicPrivateLabel }
|
||||||
{ e2eeSection }
|
{ e2eeSection }
|
||||||
{ aliasField }
|
{ aliasField }
|
||||||
<details ref={this.collectDetailsRef} className="mx_CreateRoomDialog_details">
|
<details onToggle={this.onDetailsToggled} className="mx_CreateRoomDialog_details">
|
||||||
<summary className="mx_CreateRoomDialog_details_summary">{ this.state.detailsOpen ? _t('Hide advanced') : _t('Show advanced') }</summary>
|
<summary className="mx_CreateRoomDialog_details_summary">
|
||||||
|
{ this.state.detailsOpen ? _t('Hide advanced') : _t('Show advanced') }
|
||||||
|
</summary>
|
||||||
<LabelledToggleSwitch
|
<LabelledToggleSwitch
|
||||||
label={_t(
|
label={_t(
|
||||||
"Block anyone not part of %(serverName)s from ever joining this room.",
|
"Block anyone not part of %(serverName)s from ever joining this room.",
|
|
@ -39,7 +39,7 @@ import { makeSpaceParentEvent } from "./utils/space";
|
||||||
/* eslint-disable camelcase */
|
/* eslint-disable camelcase */
|
||||||
|
|
||||||
// TODO move these interfaces over to js-sdk once it has been typescripted enough to accept them
|
// TODO move these interfaces over to js-sdk once it has been typescripted enough to accept them
|
||||||
enum Visibility {
|
export enum Visibility {
|
||||||
Public = "public",
|
Public = "public",
|
||||||
Private = "private",
|
Private = "private",
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue