diff --git a/src/components/views/dialogs/DevtoolsDialog.tsx b/src/components/views/dialogs/DevtoolsDialog.tsx
index 1d544af315..81d3a77327 100644
--- a/src/components/views/dialogs/DevtoolsDialog.tsx
+++ b/src/components/views/dialogs/DevtoolsDialog.tsx
@@ -1,5 +1,6 @@
/*
Copyright 2017 Michael Telatynski <7t3chguy@gmail.com>
+Copyright 2018-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.
@@ -14,8 +15,7 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
-import React, {useState, useEffect} from 'react';
-import PropTypes from 'prop-types';
+import React, {useState, useEffect, ChangeEvent, MouseEvent} from 'react';
import * as sdk from '../../../index';
import SyntaxHighlight from '../elements/SyntaxHighlight';
import { _t } from '../../../languageHandler';
@@ -30,8 +30,9 @@ import {
PHASE_DONE,
PHASE_STARTED,
PHASE_CANCELLED,
+ VerificationRequest,
} from "matrix-js-sdk/src/crypto/verification/request/VerificationRequest";
-import WidgetStore from "../../../stores/WidgetStore";
+import WidgetStore, { IApp } from "../../../stores/WidgetStore";
import {UPDATE_EVENT} from "../../../stores/AsyncStore";
import {SETTINGS} from "../../../settings/Settings";
import SettingsStore, {LEVEL_ORDER} from "../../../settings/SettingsStore";
@@ -40,17 +41,22 @@ import ErrorDialog from "./ErrorDialog";
import {replaceableComponent} from "../../../utils/replaceableComponent";
import {Room} from "matrix-js-sdk/src/models/room";
import {MatrixEvent} from "matrix-js-sdk/src/models/event";
+import { SettingLevel } from '../../../settings/SettingLevel';
-class GenericEditor extends React.PureComponent {
- // static propTypes = {onBack: PropTypes.func.isRequired};
+interface IGenericEditorProps {
+ onBack: () => void;
+}
- constructor(props) {
- super(props);
- this._onChange = this._onChange.bind(this);
- this.onBack = this.onBack.bind(this);
- }
+interface IGenericEditorState {
+ message?: string;
+ [inputId: string]: boolean | string;
+}
- onBack() {
+abstract class GenericEditor<
+ P extends IGenericEditorProps = IGenericEditorProps,
+ S extends IGenericEditorState = IGenericEditorState,
+> extends React.PureComponent
{
+ protected onBack = () => {
if (this.state.message) {
this.setState({ message: null });
} else {
@@ -58,47 +64,60 @@ class GenericEditor extends React.PureComponent {
}
}
- _onChange(e) {
+ protected onChange = (e: ChangeEvent) => {
+ // @ts-ignore: Unsure how to convince TS this is okay when the state
+ // type can be extended.
this.setState({[e.target.id]: e.target.type === 'checkbox' ? e.target.checked : e.target.value});
}
- _buttons() {
- return
+ protected abstract send();
+
+ protected buttons(): React.ReactNode {
+ return
- { !this.state.message && }
+ { !this.state.message && }
;
}
- textInput(id, label) {
+ protected textInput(id: string, label: string): React.ReactNode {
return
;
}
}
-export class SendCustomEvent extends GenericEditor {
- static getLabel() { return _t('Send Custom Event'); }
-
- static propTypes = {
- onBack: PropTypes.func.isRequired,
- room: PropTypes.instanceOf(Room).isRequired,
- forceStateEvent: PropTypes.bool,
- forceGeneralEvent: PropTypes.bool,
- inputs: PropTypes.object,
+interface ISendCustomEventProps extends IGenericEditorProps {
+ room: Room;
+ forceStateEvent?: boolean;
+ forceGeneralEvent?: boolean;
+ inputs?: {
+ eventType?: string;
+ stateKey?: string;
+ evContent?: string;
};
+}
+
+interface ISendCustomEventState extends IGenericEditorState {
+ isStateEvent: boolean;
+ eventType: string;
+ stateKey: string;
+ evContent: string;
+}
+
+export class SendCustomEvent extends GenericEditor
{
+ static getLabel() { return _t('Send Custom Event'); }
static contextType = MatrixClientContext;
constructor(props) {
super(props);
- this._send = this._send.bind(this);
const {eventType, stateKey, evContent} = Object.assign({
eventType: '',
@@ -115,7 +134,7 @@ export class SendCustomEvent extends GenericEditor {
};
}
- send(content) {
+ private doSend(content: object): Promise {
const cli = this.context;
if (this.state.isStateEvent) {
return cli.sendStateEvent(this.props.room.roomId, this.state.eventType, content, this.state.stateKey);
@@ -124,7 +143,7 @@ export class SendCustomEvent extends GenericEditor {
}
}
- async _send() {
+ protected send = async () => {
if (this.state.eventType === '') {
this.setState({ message: _t('You must specify an event type!') });
return;
@@ -133,7 +152,7 @@ export class SendCustomEvent extends GenericEditor {
let message;
try {
const content = JSON.parse(this.state.evContent);
- await this.send(content);
+ await this.doSend(content);
message = _t('Event sent!');
} catch (e) {
message = _t('Failed to send custom event.') + ' (' + e.toString() + ')';
@@ -147,7 +166,7 @@ export class SendCustomEvent extends GenericEditor {
{ this.state.message }
- { this._buttons() }
+ { this.buttons() }
;
}
@@ -163,16 +182,16 @@ export class SendCustomEvent extends GenericEditor {
+ autoComplete="off" value={this.state.evContent} onChange={this.onChange} element="textarea" />
-
+
- { !this.state.message &&
}
+ { !this.state.message &&
}
{ showTglFlip &&
;
}
@@ -255,17 +282,17 @@ class SendAccountData extends GenericEditor {
+ autoComplete="off" value={this.state.evContent} onChange={this.onChange} element="textarea" />
-
+
- { !this.state.message &&
}
+ { !this.state.message &&
}
{ !this.state.message &&
-
+
@@ -482,31 +517,29 @@ class RoomStateExplorer extends React.PureComponent {
{ list }
-
;
}
}
-class AccountDataExplorer extends React.PureComponent {
- static getLabel() { return _t('Explore Account Data'); }
+interface IAccountDataExplorerState {
+ isRoomAccountData: boolean;
+ event?: MatrixEvent;
+ editing: boolean;
+ queryEventType: string;
+ [inputId: string]: boolean | string;
+}
- static propTypes = {
- onBack: PropTypes.func.isRequired,
- room: PropTypes.instanceOf(Room).isRequired,
- };
+class AccountDataExplorer extends React.PureComponent
{
+ static getLabel() { return _t('Explore Account Data'); }
static contextType = MatrixClientContext;
constructor(props) {
super(props);
- this.onBack = this.onBack.bind(this);
- this.editEv = this.editEv.bind(this);
- this._onChange = this._onChange.bind(this);
- this.onQueryEventType = this.onQueryEventType.bind(this);
-
this.state = {
isRoomAccountData: false,
event: null,
@@ -516,20 +549,20 @@ class AccountDataExplorer extends React.PureComponent {
};
}
- getData() {
+ private getData(): Record {
if (this.state.isRoomAccountData) {
return this.props.room.accountData;
}
return this.context.store.accountData;
}
- onViewSourceClick(event) {
+ private onViewSourceClick(event: MatrixEvent) {
return () => {
this.setState({ event });
};
}
- onBack() {
+ private onBack = () => {
if (this.state.editing) {
this.setState({ editing: false });
} else if (this.state.event) {
@@ -539,15 +572,15 @@ class AccountDataExplorer extends React.PureComponent {
}
}
- _onChange(e) {
+ private onChange = (e: ChangeEvent) => {
this.setState({[e.target.id]: e.target.type === 'checkbox' ? e.target.checked : e.target.value});
}
- editEv() {
+ private editEv = () => {
this.setState({ editing: true });
}
- onQueryEventType(queryEventType) {
+ private onQueryEventType = (queryEventType: string) => {
this.setState({ queryEventType });
}
@@ -570,7 +603,7 @@ class AccountDataExplorer extends React.PureComponent {
{ JSON.stringify(this.state.event.event, null, 2) }
-
+
@@ -595,40 +628,41 @@ class AccountDataExplorer extends React.PureComponent {
{ rows }
-
+
- { !this.state.message &&
;
}
}
-class ServersInRoomList extends React.PureComponent {
+interface IServersInRoomListState {
+ query: string;
+}
+
+class ServersInRoomList extends React.PureComponent
{
static getLabel() { return _t('View Servers in Room'); }
- static propTypes = {
- onBack: PropTypes.func.isRequired,
- room: PropTypes.instanceOf(Room).isRequired,
- };
-
static contextType = MatrixClientContext;
+ private servers: React.ReactElement[];
+
constructor(props) {
super(props);
const room = this.props.room;
- const servers = new Set();
+ const servers: Set = new Set();
room.currentState.getStateEvents("m.room.member").forEach(ev => servers.add(ev.getSender().split(":")[1]));
this.servers = Array.from(servers).map(s =>
-
;
@@ -667,7 +701,10 @@ const PHASE_MAP = {
[PHASE_CANCELLED]: "cancelled",
};
-function VerificationRequest({txnId, request}) {
+const VerificationRequest: React.FC<{
+ txnId: string;
+ request: VerificationRequest;
+}> = ({txnId, request}) => {
const [, updateState] = useState();
const [timeout, setRequestTimeout] = useState(request.timeout);
@@ -704,7 +741,7 @@ function VerificationRequest({txnId, request}) {
);
}
-class VerificationExplorer extends React.Component {
+class VerificationExplorer extends React.PureComponent
{
static getLabel() {
return _t("Verification Requests");
}
@@ -712,7 +749,7 @@ class VerificationExplorer extends React.Component {
/* Ensure this.context is the cli */
static contextType = MatrixClientContext;
- onNewRequest = () => {
+ private onNewRequest = () => {
this.forceUpdate();
}
@@ -738,14 +775,19 @@ class VerificationExplorer extends React.Component {
,
)}
- );
}
}
-class WidgetExplorer extends React.Component {
+interface IWidgetExplorerState {
+ query: string;
+ editWidget?: IApp;
+}
+
+class WidgetExplorer extends React.Component {
static getLabel() {
return _t("Active Widgets");
}
@@ -759,19 +801,19 @@ class WidgetExplorer extends React.Component {
};
}
- onWidgetStoreUpdate = () => {
+ private onWidgetStoreUpdate = () => {
this.forceUpdate();
};
- onQueryChange = (query) => {
+ private onQueryChange = (query: string) => {
this.setState({query});
};
- onEditWidget = (widget) => {
+ private onEditWidget = (widget: IApp) => {
this.setState({editWidget: widget});
};
- onBack = () => {
+ private onBack = () => {
const widgets = WidgetStore.instance.getApps(this.props.room.roomId);
if (this.state.editWidget && widgets.includes(this.state.editWidget)) {
this.setState({editWidget: null});
@@ -794,13 +836,16 @@ class WidgetExplorer extends React.Component {
const editWidget = this.state.editWidget;
const widgets = WidgetStore.instance.getApps(room.roomId);
if (editWidget && widgets.includes(editWidget)) {
- const allState = Array.from(Array.from(room.currentState.events.values()).map(e => e.values()))
- .reduce((p, c) => {p.push(...c); return p;}, []);
+ const allState = Array.from(
+ Array.from(room.currentState.events.values()).map((e: Map) => {
+ return e.values();
+ }),
+ ).reduce((p, c) => { p.push(...c); return p; }, []);
const stateEv = allState.find(ev => ev.getId() === editWidget.eventId);
if (!stateEv) { // "should never happen"
return
{_t("There was an error finding this widget.")}
-
;
@@ -829,14 +874,22 @@ class WidgetExplorer extends React.Component {
})}
- );
}
}
-class SettingsExplorer extends React.Component {
+interface ISettingsExplorerState {
+ query: string;
+ editSetting?: string;
+ viewSetting?: string;
+ explicitValues?: string;
+ explicitRoomValues?: string;
+ }
+
+class SettingsExplorer extends React.PureComponent {
static getLabel() {
return _t("Settings Explorer");
}
@@ -854,19 +907,19 @@ class SettingsExplorer extends React.Component {
};
}
- onQueryChange = (ev) => {
+ private onQueryChange = (ev: ChangeEvent) => {
this.setState({query: ev.target.value});
};
- onExplValuesEdit = (ev) => {
+ private onExplValuesEdit = (ev: ChangeEvent) => {
this.setState({explicitValues: ev.target.value});
};
- onExplRoomValuesEdit = (ev) => {
+ private onExplRoomValuesEdit = (ev: ChangeEvent) => {
this.setState({explicitRoomValues: ev.target.value});
};
- onBack = () => {
+ private onBack = () => {
if (this.state.editSetting) {
this.setState({editSetting: null});
} else if (this.state.viewSetting) {
@@ -876,12 +929,12 @@ class SettingsExplorer extends React.Component {
}
};
- onViewClick = (ev, settingId) => {
+ private onViewClick = (ev: MouseEvent, settingId: string) => {
ev.preventDefault();
this.setState({viewSetting: settingId});
};
- onEditClick = (ev, settingId) => {
+ private onEditClick = (ev: MouseEvent, settingId: string) => {
ev.preventDefault();
this.setState({
editSetting: settingId,
@@ -890,7 +943,7 @@ class SettingsExplorer extends React.Component {
});
};
- onSaveClick = async () => {
+ private onSaveClick = async () => {
try {
const settingId = this.state.editSetting;
const parsedExplicit = JSON.parse(this.state.explicitValues);
@@ -899,7 +952,7 @@ class SettingsExplorer extends React.Component {
console.log(`[Devtools] Setting value of ${settingId} at ${level} from user input`);
try {
const val = parsedExplicit[level];
- await SettingsStore.setValue(settingId, null, level, val);
+ await SettingsStore.setValue(settingId, null, level as SettingLevel, val);
} catch (e) {
console.warn(e);
}
@@ -909,7 +962,7 @@ class SettingsExplorer extends React.Component {
console.log(`[Devtools] Setting value of ${settingId} at ${level} in ${roomId} from user input`);
try {
const val = parsedExplicitRoom[level];
- await SettingsStore.setValue(settingId, roomId, level, val);
+ await SettingsStore.setValue(settingId, roomId, level as SettingLevel, val);
} catch (e) {
console.warn(e);
}
@@ -926,7 +979,7 @@ class SettingsExplorer extends React.Component {
}
};
- renderSettingValue(val) {
+ private renderSettingValue(val: any): string {
// Note: we don't .toString() a string because we want JSON.stringify to inject quotes for us
const toStringTypes = ['boolean', 'number'];
if (toStringTypes.includes(typeof(val))) {
@@ -936,7 +989,7 @@ class SettingsExplorer extends React.Component {
}
}
- renderExplicitSettingValues(setting, roomId) {
+ private renderExplicitSettingValues(setting: string, roomId: string): string {
const vals = {};
for (const level of LEVEL_ORDER) {
try {
@@ -951,7 +1004,7 @@ class SettingsExplorer extends React.Component {
return JSON.stringify(vals, null, 4);
}
- renderCanEditLevel(roomId, level) {
+ private renderCanEditLevel(roomId: string, level: SettingLevel): React.ReactNode {
const canEdit = SettingsStore.canSetValue(this.state.editSetting, roomId, level);
const className = canEdit ? 'mx_DevTools_SettingsExplorer_mutable' : 'mx_DevTools_SettingsExplorer_immutable';
return {canEdit.toString()} | ;
@@ -1006,7 +1059,7 @@ class SettingsExplorer extends React.Component {
-
@@ -1068,7 +1121,7 @@ class SettingsExplorer extends React.Component {
-
+
@@ -1114,7 +1167,7 @@ class SettingsExplorer extends React.Component {
-
+
@@ -1126,7 +1179,11 @@ class SettingsExplorer extends React.Component {
}
}
-const Entries = [
+type DevtoolsDialogEntry = React.JSXElementConstructor
& {
+ getLabel: () => string;
+};
+
+const Entries: DevtoolsDialogEntry[] = [
SendCustomEvent,
RoomStateExplorer,
SendAccountData,
@@ -1137,43 +1194,36 @@ const Entries = [
SettingsExplorer,
];
-@replaceableComponent("views.dialogs.DevtoolsDialog")
-export default class DevtoolsDialog extends React.PureComponent {
- static propTypes = {
- roomId: PropTypes.string.isRequired,
- onFinished: PropTypes.func.isRequired,
- };
+interface IProps {
+ roomId: string;
+ onFinished: (finished: boolean) => void;
+}
+interface IState {
+ mode?: DevtoolsDialogEntry;
+}
+
+@replaceableComponent("views.dialogs.DevtoolsDialog")
+export default class DevtoolsDialog extends React.PureComponent {
constructor(props) {
super(props);
- this.onBack = this.onBack.bind(this);
- this.onCancel = this.onCancel.bind(this);
this.state = {
mode: null,
};
}
- componentWillUnmount() {
- this._unmounted = true;
- }
-
- _setMode(mode) {
+ private setMode(mode: DevtoolsDialogEntry) {
return () => {
this.setState({ mode });
};
}
- onBack() {
- if (this.prevMode) {
- this.setState({ mode: this.prevMode });
- this.prevMode = null;
- } else {
- this.setState({ mode: null });
- }
+ private onBack = () => {
+ this.setState({ mode: null });
}
- onCancel() {
+ private onCancel = () => {
this.props.onFinished(false);
}
@@ -1200,12 +1250,12 @@ export default class DevtoolsDialog extends React.PureComponent {
{ Entries.map((Entry) => {
const label = Entry.getLabel();
- const onClick = this._setMode(Entry);
+ const onClick = this.setMode(Entry);
return ;
}) }
-