+ if (this.state.recordingPhase !== RecordingState.Started) {
+ // TODO: @@ TR: Should we disable this during upload? What does a failed upload look like?
+ return
;
+ }
+
+ // only other UI is the recording-in-progress UI
+ return
;
}
- public render() {
- const classes = classNames({
- 'mx_MessageComposer_button': !this.state.recorder,
- 'mx_MessageComposer_voiceMessage': !this.state.recorder,
- 'mx_VoiceRecordComposerTile_stop': !!this.state.recorder,
- });
+ public render(): ReactNode {
+ let recordingInfo;
+ let deleteButton;
+ if (!this.state.recordingPhase || this.state.recordingPhase === RecordingState.Started) {
+ const classes = classNames({
+ 'mx_MessageComposer_button': !this.state.recorder,
+ 'mx_MessageComposer_voiceMessage': !this.state.recorder,
+ 'mx_VoiceRecordComposerTile_stop': this.state.recorder?.isRecording,
+ });
- let tooltip = _t("Record a voice message");
- if (!!this.state.recorder) {
- // TODO: @@ TravisR: Change to match behaviour
- tooltip = _t("Stop & send recording");
+ let tooltip = _t("Record a voice message");
+ if (!!this.state.recorder) {
+ tooltip = _t("Stop the recording");
+ }
+
+ let stopOrRecordBtn =
;
+ if (this.state.recorder && !this.state.recorder?.isRecording) {
+ stopOrRecordBtn = null;
+ }
+
+ recordingInfo = stopOrRecordBtn;
+ }
+
+ if (this.state.recorder && this.state.recordingPhase !== RecordingState.Uploading) {
+ deleteButton =
;
}
return (<>
+ {deleteButton}
{this.renderWaveformArea()}
-
+ {recordingInfo}
>);
}
}
diff --git a/src/components/views/settings/E2eAdvancedPanel.js b/src/components/views/settings/E2eAdvancedPanel.tsx
similarity index 100%
rename from src/components/views/settings/E2eAdvancedPanel.js
rename to src/components/views/settings/E2eAdvancedPanel.tsx
diff --git a/src/components/views/settings/EventIndexPanel.js b/src/components/views/settings/EventIndexPanel.tsx
similarity index 65%
rename from src/components/views/settings/EventIndexPanel.js
rename to src/components/views/settings/EventIndexPanel.tsx
index d1a02de16d..fa84063ee8 100644
--- a/src/components/views/settings/EventIndexPanel.js
+++ b/src/components/views/settings/EventIndexPanel.tsx
@@ -1,5 +1,5 @@
/*
-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");
you may not use this file except in compliance with the License.
@@ -28,10 +28,17 @@ import {SettingLevel} from "../../../settings/SettingLevel";
import {replaceableComponent} from "../../../utils/replaceableComponent";
import SeshatResetDialog from '../dialogs/SeshatResetDialog';
+interface IState {
+ enabling: boolean;
+ eventIndexSize: number;
+ roomCount: number;
+ eventIndexingEnabled: boolean;
+}
+
@replaceableComponent("views.settings.EventIndexPanel")
-export default class EventIndexPanel extends React.Component {
- constructor() {
- super();
+export default class EventIndexPanel extends React.Component<{}, IState> {
+ constructor(props) {
+ super(props);
this.state = {
enabling: false,
@@ -68,7 +75,7 @@ export default class EventIndexPanel extends React.Component {
}
}
- async componentDidMount(): void {
+ componentDidMount(): void {
this.updateState();
}
@@ -102,8 +109,10 @@ export default class EventIndexPanel extends React.Component {
});
}
- _onManage = async () => {
+ private onManage = async () => {
Modal.createTrackedDialogAsync('Message search', 'Message search',
+ // @ts-ignore: TS doesn't seem to like the type of this now that it
+ // has also been converted to TS as well, but I can't figure out why...
import('../../../async-components/views/dialogs/eventindex/ManageEventIndexDialog'),
{
onFinished: () => {},
@@ -111,7 +120,7 @@ export default class EventIndexPanel extends React.Component {
);
}
- _onEnable = async () => {
+ private onEnable = async () => {
this.setState({
enabling: true,
});
@@ -123,14 +132,13 @@ export default class EventIndexPanel extends React.Component {
await this.updateState();
}
- _confirmEventStoreReset = () => {
- const self = this;
+ private confirmEventStoreReset = () => {
const { close } = Modal.createDialog(SeshatResetDialog, {
onFinished: async (success) => {
if (success) {
await SettingsStore.setValue('enableEventIndexing', null, SettingLevel.DEVICE, false);
await EventIndexPeg.deleteEventIndex();
- await self._onEnable();
+ await this.onEnable();
close();
}
},
@@ -145,20 +153,19 @@ export default class EventIndexPanel extends React.Component {
if (EventIndexPeg.get() !== null) {
eventIndexingSettings = (
-
- {_t("Securely cache encrypted messages locally for them " +
- "to appear in search results, using %(size)s to store messages from %(rooms)s rooms.",
- {
- size: formatBytes(this.state.eventIndexSize, 0),
- // This drives the singular / plural string
- // selection for "room" / "rooms" only.
- count: this.state.roomCount,
- rooms: formatCountLong(this.state.roomCount),
- },
- )}
-
+
{_t(
+ "Securely cache encrypted messages locally for them " +
+ "to appear in search results, using %(size)s to store messages from %(rooms)s rooms.",
+ {
+ size: formatBytes(this.state.eventIndexSize, 0),
+ // This drives the singular / plural string
+ // selection for "room" / "rooms" only.
+ count: this.state.roomCount,
+ rooms: formatCountLong(this.state.roomCount),
+ },
+ )}
@@ -167,13 +174,13 @@ export default class EventIndexPanel extends React.Component {
} else if (!this.state.eventIndexingEnabled && EventIndexPeg.supportIsInstalled()) {
eventIndexingSettings = (
-
- {_t( "Securely cache encrypted messages locally for them to " +
- "appear in search results.")}
-
+
{_t(
+ "Securely cache encrypted messages locally for them to " +
+ "appear in search results.",
+ )}
+ onClick={this.onEnable}>
{_t("Enable")}
{this.state.enabling ?
:
}
@@ -188,40 +195,36 @@ export default class EventIndexPanel extends React.Component {
);
eventIndexingSettings = (
-
+
{_t(
+ "%(brand)s is missing some components required for securely " +
+ "caching encrypted messages locally. If you'd like to " +
+ "experiment with this feature, build a custom %(brand)s Desktop " +
+ "with
search components added.",
{
- _t( "%(brand)s is missing some components required for securely " +
- "caching encrypted messages locally. If you'd like to " +
- "experiment with this feature, build a custom %(brand)s Desktop " +
- "with
search components added.",
- {
- brand,
- },
- {
- 'nativeLink': (sub) =>
{sub},
- },
- )
- }
-
+ brand,
+ },
+ {
+ nativeLink: sub =>
{sub},
+ },
+ )}
);
} else if (!EventIndexPeg.platformHasSupport()) {
eventIndexingSettings = (
-
+
{_t(
+ "%(brand)s can't securely cache encrypted messages locally " +
+ "while running in a web browser. Use
%(brand)s Desktop " +
+ "for encrypted messages to appear in search results.",
{
- _t( "%(brand)s can't securely cache encrypted messages locally " +
- "while running in a web browser. Use
%(brand)s Desktop " +
- "for encrypted messages to appear in search results.",
- {
- brand,
- },
- {
- 'desktopLink': (sub) =>
{sub},
- },
- )
- }
-
+ brand,
+ },
+ {
+ desktopLink: sub =>
{sub},
+ },
+ )}
);
} else {
eventIndexingSettings = (
@@ -233,19 +236,18 @@ export default class EventIndexPanel extends React.Component {
}
{EventIndexPeg.error && (
-
- {_t("Advanced")}
-
- {EventIndexPeg.error.message}
-
-
-
- {_t("Reset")}
-
-
-
+
+ {_t("Advanced")}
+
+ {EventIndexPeg.error.message}
+
+
+
+ {_t("Reset")}
+
+
+
)}
-
);
}
diff --git a/src/components/views/settings/SetIdServer.js b/src/components/views/settings/SetIdServer.tsx
similarity index 89%
rename from src/components/views/settings/SetIdServer.js
rename to src/components/views/settings/SetIdServer.tsx
index fa2a36476d..05d1f83387 100644
--- a/src/components/views/settings/SetIdServer.js
+++ b/src/components/views/settings/SetIdServer.tsx
@@ -1,5 +1,5 @@
/*
-Copyright 2019 The Matrix.org Foundation C.I.C.
+Copyright 2019-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.
@@ -16,7 +16,6 @@ limitations under the License.
import url from 'url';
import React from 'react';
-import PropTypes from 'prop-types';
import {_t} from "../../../languageHandler";
import * as sdk from '../../../index';
import {MatrixClientPeg} from "../../../MatrixClientPeg";
@@ -28,6 +27,7 @@ import {abbreviateUrl, unabbreviateUrl} from "../../../utils/UrlUtils";
import { getDefaultIdentityServerUrl, doesIdentityServerHaveTerms } from '../../../utils/IdentityServerUtils';
import {timeout} from "../../../utils/promise";
import {replaceableComponent} from "../../../utils/replaceableComponent";
+import { ActionPayload } from '../../../dispatcher/payloads';
// We'll wait up to this long when checking for 3PID bindings on the IS.
const REACHABILITY_TIMEOUT = 10000; // ms
@@ -59,16 +59,28 @@ async function checkIdentityServerUrl(u) {
}
}
-@replaceableComponent("views.settings.SetIdServer")
-export default class SetIdServer extends React.Component {
- static propTypes = {
- // Whether or not the ID server is missing terms. This affects the text
- // shown to the user.
- missingTerms: PropTypes.bool,
- };
+interface IProps {
+ // Whether or not the ID server is missing terms. This affects the text
+ // shown to the user.
+ missingTerms: boolean;
+}
- constructor() {
- super();
+interface IState {
+ defaultIdServer?: string;
+ currentClientIdServer: string;
+ idServer?: string;
+ error?: string;
+ busy: boolean;
+ disconnectBusy: boolean;
+ checking: boolean;
+}
+
+@replaceableComponent("views.settings.SetIdServer")
+export default class SetIdServer extends React.Component
{
+ private dispatcherRef: string;
+
+ constructor(props) {
+ super(props);
let defaultIdServer = '';
if (!MatrixClientPeg.get().getIdentityServerUrl() && getDefaultIdentityServerUrl()) {
@@ -96,7 +108,7 @@ export default class SetIdServer extends React.Component {
dis.unregister(this.dispatcherRef);
}
- onAction = (payload) => {
+ private onAction = (payload: ActionPayload) => {
// We react to changes in the ID server in the event the user is staring at this form
// when changing their identity server on another device.
if (payload.action !== "id_server_changed") return;
@@ -106,13 +118,13 @@ export default class SetIdServer extends React.Component {
});
};
- _onIdentityServerChanged = (ev) => {
+ private onIdentityServerChanged = (ev) => {
const u = ev.target.value;
this.setState({idServer: u});
};
- _getTooltip = () => {
+ private getTooltip = () => {
if (this.state.checking) {
const InlineSpinner = sdk.getComponent('views.elements.InlineSpinner');
return
@@ -126,11 +138,11 @@ export default class SetIdServer extends React.Component {
}
};
- _idServerChangeEnabled = () => {
+ private idServerChangeEnabled = () => {
return !!this.state.idServer && !this.state.busy;
};
- _saveIdServer = (fullUrl) => {
+ private saveIdServer = (fullUrl) => {
// Account data change will update localstorage, client, etc through dispatcher
MatrixClientPeg.get().setAccountData("m.identity_server", {
base_url: fullUrl,
@@ -143,7 +155,7 @@ export default class SetIdServer extends React.Component {
});
};
- _checkIdServer = async (e) => {
+ private checkIdServer = async (e) => {
e.preventDefault();
const { idServer, currentClientIdServer } = this.state;
@@ -166,14 +178,14 @@ export default class SetIdServer extends React.Component {
// Double check that the identity server even has terms of service.
const hasTerms = await doesIdentityServerHaveTerms(fullUrl);
if (!hasTerms) {
- const [confirmed] = await this._showNoTermsWarning(fullUrl);
+ const [confirmed] = await this.showNoTermsWarning(fullUrl);
save = confirmed;
}
// Show a general warning, possibly with details about any bound
// 3PIDs that would be left behind.
if (save && currentClientIdServer && fullUrl !== currentClientIdServer) {
- const [confirmed] = await this._showServerChangeWarning({
+ const [confirmed] = await this.showServerChangeWarning({
title: _t("Change identity server"),
unboundMessage: _t(
"Disconnect from the identity server
and " +
@@ -189,7 +201,7 @@ export default class SetIdServer extends React.Component {
}
if (save) {
- this._saveIdServer(fullUrl);
+ this.saveIdServer(fullUrl);
}
} catch (e) {
console.error(e);
@@ -204,7 +216,7 @@ export default class SetIdServer extends React.Component {
});
};
- _showNoTermsWarning(fullUrl) {
+ private showNoTermsWarning(fullUrl) {
const QuestionDialog = sdk.getComponent("views.dialogs.QuestionDialog");
const { finished } = Modal.createTrackedDialog('No Terms Warning', '', QuestionDialog, {
title: _t("Identity server has no terms of service"),
@@ -223,10 +235,10 @@ export default class SetIdServer extends React.Component {
return finished;
}
- _onDisconnectClicked = async () => {
+ private onDisconnectClicked = async () => {
this.setState({disconnectBusy: true});
try {
- const [confirmed] = await this._showServerChangeWarning({
+ const [confirmed] = await this.showServerChangeWarning({
title: _t("Disconnect identity server"),
unboundMessage: _t(
"Disconnect from the identity server
?", {},
@@ -235,14 +247,14 @@ export default class SetIdServer extends React.Component {
button: _t("Disconnect"),
});
if (confirmed) {
- this._disconnectIdServer();
+ this.disconnectIdServer();
}
} finally {
this.setState({disconnectBusy: false});
}
};
- async _showServerChangeWarning({ title, unboundMessage, button }) {
+ private async showServerChangeWarning({ title, unboundMessage, button }) {
const { currentClientIdServer } = this.state;
let threepids = [];
@@ -318,7 +330,7 @@ export default class SetIdServer extends React.Component {
return finished;
}
- _disconnectIdServer = () => {
+ private disconnectIdServer = () => {
// Account data change will update localstorage, client, etc through dispatcher
MatrixClientPeg.get().setAccountData("m.identity_server", {
base_url: null, // clear
@@ -371,7 +383,7 @@ export default class SetIdServer extends React.Component {
let discoSection;
if (idServerUrl) {
- let discoButtonContent = _t("Disconnect");
+ let discoButtonContent: React.ReactNode = _t("Disconnect");
let discoBodyText = _t(
"Disconnecting from your identity server will mean you " +
"won't be discoverable by other users and you won't be " +
@@ -391,14 +403,14 @@ export default class SetIdServer extends React.Component {
}
discoSection =
{discoBodyText}
-
+
{discoButtonContent}
;
}
return (
-
diff --git a/src/components/views/settings/tabs/room/RolesRoomSettingsTab.js b/src/components/views/settings/tabs/room/RolesRoomSettingsTab.tsx
similarity index 89%
rename from src/components/views/settings/tabs/room/RolesRoomSettingsTab.js
rename to src/components/views/settings/tabs/room/RolesRoomSettingsTab.tsx
index 59a175906d..4fa521f598 100644
--- a/src/components/views/settings/tabs/room/RolesRoomSettingsTab.js
+++ b/src/components/views/settings/tabs/room/RolesRoomSettingsTab.tsx
@@ -1,5 +1,5 @@
/*
-Copyright 2019, 2021 The Matrix.org Foundation C.I.C.
+Copyright 2019-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.
@@ -15,7 +15,6 @@ limitations under the License.
*/
import React from 'react';
-import PropTypes from 'prop-types';
import {_t, _td} from "../../../../../languageHandler";
import {MatrixClientPeg} from "../../../../../MatrixClientPeg";
import * as sdk from "../../../../..";
@@ -23,6 +22,9 @@ import AccessibleButton from "../../../elements/AccessibleButton";
import Modal from "../../../../../Modal";
import {replaceableComponent} from "../../../../../utils/replaceableComponent";
import {EventType} from "matrix-js-sdk/src/@types/event";
+import { RoomMember } from "matrix-js-sdk/src/models/room-member";
+import { MatrixEvent } from "matrix-js-sdk/src/models/event";
+import { RoomState } from "matrix-js-sdk/src/models/room-state";
const plEventsToLabels = {
// These will be translated for us later.
@@ -63,15 +65,15 @@ function parseIntWithDefault(val, def) {
return isNaN(res) ? def : res;
}
-export class BannedUser extends React.Component {
- static propTypes = {
- canUnban: PropTypes.bool,
- member: PropTypes.object.isRequired, // js-sdk RoomMember
- by: PropTypes.string.isRequired,
- reason: PropTypes.string,
- };
+interface IBannedUserProps {
+ canUnban?: boolean;
+ member: RoomMember;
+ by: string;
+ reason?: string;
+}
- _onUnbanClick = (e) => {
+export class BannedUser extends React.Component
{
+ private onUnbanClick = (e) => {
MatrixClientPeg.get().unban(this.props.member.roomId, this.props.member.userId).catch((err) => {
const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
console.error("Failed to unban: " + err);
@@ -87,8 +89,10 @@ export class BannedUser extends React.Component {
if (this.props.canUnban) {
unbanButton = (
-
+
{ _t('Unban') }
);
@@ -107,29 +111,29 @@ export class BannedUser extends React.Component {
}
}
-@replaceableComponent("views.settings.tabs.room.RolesRoomSettingsTab")
-export default class RolesRoomSettingsTab extends React.Component {
- static propTypes = {
- roomId: PropTypes.string.isRequired,
- };
+interface IProps {
+ roomId: string;
+}
- componentDidMount(): void {
- MatrixClientPeg.get().on("RoomState.members", this._onRoomMembership);
+@replaceableComponent("views.settings.tabs.room.RolesRoomSettingsTab")
+export default class RolesRoomSettingsTab extends React.Component {
+ componentDidMount() {
+ MatrixClientPeg.get().on("RoomState.members", this.onRoomMembership);
}
- componentWillUnmount(): void {
+ componentWillUnmount() {
const client = MatrixClientPeg.get();
if (client) {
- client.removeListener("RoomState.members", this._onRoomMembership);
+ client.removeListener("RoomState.members", this.onRoomMembership);
}
}
- _onRoomMembership = (event, state, member) => {
+ private onRoomMembership = (event: MatrixEvent, state: RoomState, member: RoomMember) => {
if (state.roomId !== this.props.roomId) return;
this.forceUpdate();
};
- _populateDefaultPlEvents(eventsSection, stateLevel, eventsLevel) {
+ private populateDefaultPlEvents(eventsSection: Record, stateLevel: number, eventsLevel: number) {
for (const desiredEvent of Object.keys(plEventsToShow)) {
if (!(desiredEvent in eventsSection)) {
eventsSection[desiredEvent] = (plEventsToShow[desiredEvent].isState ? stateLevel : eventsLevel);
@@ -137,7 +141,7 @@ export default class RolesRoomSettingsTab extends React.Component {
}
}
- _onPowerLevelsChanged = (value, powerLevelKey) => {
+ private onPowerLevelsChanged = (inputValue: string, powerLevelKey: string) => {
const client = MatrixClientPeg.get();
const room = client.getRoom(this.props.roomId);
const plEvent = room.currentState.getStateEvents('m.room.power_levels', '');
@@ -148,7 +152,7 @@ export default class RolesRoomSettingsTab extends React.Component {
const eventsLevelPrefix = "event_levels_";
- value = parseInt(value);
+ const value = parseInt(inputValue);
if (powerLevelKey.startsWith(eventsLevelPrefix)) {
// deep copy "events" object, Object.assign itself won't deep copy
@@ -182,7 +186,7 @@ export default class RolesRoomSettingsTab extends React.Component {
});
};
- _onUserPowerLevelChanged = (value, powerLevelKey) => {
+ private onUserPowerLevelChanged = (value: string, powerLevelKey: string) => {
const client = MatrixClientPeg.get();
const room = client.getRoom(this.props.roomId);
const plEvent = room.currentState.getStateEvents('m.room.power_levels', '');
@@ -266,7 +270,7 @@ export default class RolesRoomSettingsTab extends React.Component {
currentUserLevel = defaultUserLevel;
}
- this._populateDefaultPlEvents(
+ this.populateDefaultPlEvents(
eventsLevels,
parseIntWithDefault(plContent.state_default, powerLevelDescriptors.state_default.defaultValue),
parseIntWithDefault(plContent.events_default, powerLevelDescriptors.events_default.defaultValue),
@@ -288,7 +292,7 @@ export default class RolesRoomSettingsTab extends React.Component {
label={user}
key={user}
powerLevelKey={user} // Will be sent as the second parameter to `onChange`
- onChange={this._onUserPowerLevelChanged}
+ onChange={this.onUserPowerLevelChanged}
/>,
);
} else if (userLevels[user] < defaultUserLevel) { // muted
@@ -299,7 +303,7 @@ export default class RolesRoomSettingsTab extends React.Component {
label={user}
key={user}
powerLevelKey={user} // Will be sent as the second parameter to `onChange`
- onChange={this._onUserPowerLevelChanged}
+ onChange={this.onUserPowerLevelChanged}
/>,
);
}
@@ -345,8 +349,9 @@ export default class RolesRoomSettingsTab extends React.Component {
if (sender) bannedBy = sender.name;
return (
+ member={member} reason={banEvent.reason}
+ by={bannedBy}
+ />
);
})}
@@ -373,7 +378,7 @@ export default class RolesRoomSettingsTab extends React.Component {
usersDefault={defaultUserLevel}
disabled={!canChangeLevels || currentUserLevel < value}
powerLevelKey={key} // Will be sent as the second parameter to `onChange`
- onChange={this._onPowerLevelsChanged}
+ onChange={this.onPowerLevelsChanged}
/>
;
});
@@ -398,7 +403,7 @@ export default class RolesRoomSettingsTab extends React.Component {
usersDefault={defaultUserLevel}
disabled={!canChangeLevels || currentUserLevel < eventsLevels[eventType]}
powerLevelKey={"event_levels_" + eventType}
- onChange={this._onPowerLevelsChanged}
+ onChange={this.onPowerLevelsChanged}
/>
);
diff --git a/src/components/views/settings/tabs/room/SecurityRoomSettingsTab.js b/src/components/views/settings/tabs/room/SecurityRoomSettingsTab.tsx
similarity index 79%
rename from src/components/views/settings/tabs/room/SecurityRoomSettingsTab.js
rename to src/components/views/settings/tabs/room/SecurityRoomSettingsTab.tsx
index ce883c6d23..02bbcfb751 100644
--- a/src/components/views/settings/tabs/room/SecurityRoomSettingsTab.js
+++ b/src/components/views/settings/tabs/room/SecurityRoomSettingsTab.tsx
@@ -1,5 +1,5 @@
/*
-Copyright 2019 New Vector Ltd
+Copyright 2019-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.
@@ -15,7 +15,7 @@ limitations under the License.
*/
import React from 'react';
-import PropTypes from 'prop-types';
+import { MatrixEvent } from "matrix-js-sdk/src/models/event";
import {_t} from "../../../../../languageHandler";
import {MatrixClientPeg} from "../../../../../MatrixClientPeg";
import * as sdk from "../../../../..";
@@ -26,64 +26,92 @@ 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 { replaceableComponent } from "../../../../../utils/replaceableComponent";
+
+// Knock and private are reserved keywords which are not yet implemented.
+enum JoinRule {
+ Public = "public",
+ Knock = "knock",
+ Invite = "invite",
+ Private = "private",
+}
+
+enum GuestAccess {
+ CanJoin = "can_join",
+ Forbidden = "forbidden",
+}
+
+enum HistoryVisibility {
+ Invited = "invited",
+ Joined = "joined",
+ Shared = "shared",
+ WorldReadable = "world_readable",
+}
+
+interface IProps {
+ roomId: string;
+}
+
+interface IState {
+ joinRule: JoinRule;
+ guestAccess: GuestAccess;
+ history: HistoryVisibility;
+ hasAliases: boolean;
+ encrypted: boolean;
+}
@replaceableComponent("views.settings.tabs.room.SecurityRoomSettingsTab")
-export default class SecurityRoomSettingsTab extends React.Component {
- static propTypes = {
- roomId: PropTypes.string.isRequired,
- };
-
- constructor() {
- super();
+export default class SecurityRoomSettingsTab extends React.Component
{
+ constructor(props) {
+ super(props);
this.state = {
- joinRule: "invite",
- guestAccess: "can_join",
- history: "shared",
+ joinRule: JoinRule.Invite,
+ guestAccess: GuestAccess.CanJoin,
+ history: HistoryVisibility.Shared,
hasAliases: false,
encrypted: false,
};
}
// TODO: [REACT-WARNING] Move this to constructor
- async UNSAFE_componentWillMount(): void { // eslint-disable-line camelcase
- MatrixClientPeg.get().on("RoomState.events", this._onStateEvent);
+ async UNSAFE_componentWillMount() { // eslint-disable-line camelcase
+ MatrixClientPeg.get().on("RoomState.events", this.onStateEvent);
const room = MatrixClientPeg.get().getRoom(this.props.roomId);
const state = room.currentState;
- const joinRule = this._pullContentPropertyFromEvent(
+ const joinRule: JoinRule = this.pullContentPropertyFromEvent(
state.getStateEvents("m.room.join_rules", ""),
'join_rule',
- 'invite',
+ JoinRule.Invite,
);
- const guestAccess = this._pullContentPropertyFromEvent(
+ const guestAccess: GuestAccess = this.pullContentPropertyFromEvent(
state.getStateEvents("m.room.guest_access", ""),
'guest_access',
- 'forbidden',
+ GuestAccess.Forbidden,
);
- const history = this._pullContentPropertyFromEvent(
+ const history: HistoryVisibility = this.pullContentPropertyFromEvent(
state.getStateEvents("m.room.history_visibility", ""),
'history_visibility',
- 'shared',
+ HistoryVisibility.Shared,
);
const encrypted = MatrixClientPeg.get().isRoomEncrypted(this.props.roomId);
this.setState({joinRule, guestAccess, history, encrypted});
- const hasAliases = await this._hasAliases();
+ const hasAliases = await this.hasAliases();
this.setState({hasAliases});
}
- _pullContentPropertyFromEvent(event, key, defaultValue) {
+ private pullContentPropertyFromEvent(event: MatrixEvent, key: string, defaultValue: T): T {
if (!event || !event.getContent()) return defaultValue;
return event.getContent()[key] || defaultValue;
}
- componentWillUnmount(): void {
- MatrixClientPeg.get().removeListener("RoomState.events", this._onStateEvent);
+ componentWillUnmount() {
+ MatrixClientPeg.get().removeListener("RoomState.events", this.onStateEvent);
}
- _onStateEvent = (e) => {
+ private onStateEvent = (e: MatrixEvent) => {
const refreshWhenTypes = [
'm.room.join_rules',
'm.room.guest_access',
@@ -93,7 +121,7 @@ export default class SecurityRoomSettingsTab extends React.Component {
if (refreshWhenTypes.includes(e.getType())) this.forceUpdate();
};
- _onEncryptionChange = (e) => {
+ private onEncryptionChange = (e: React.ChangeEvent) => {
Modal.createTrackedDialog('Enable encryption', '', QuestionDialog, {
title: _t('Enable encryption?'),
description: _t(
@@ -102,10 +130,9 @@ export default class SecurityRoomSettingsTab extends React.Component {
"may prevent many bots and bridges from working correctly. Learn more about encryption.",
{},
{
- 'a': (sub) => {
- return {sub};
- },
+ a: sub => {sub},
},
),
onFinished: (confirm) => {
@@ -127,12 +154,12 @@ export default class SecurityRoomSettingsTab extends React.Component {
});
};
- _fixGuestAccess = (e) => {
+ private fixGuestAccess = (e: React.MouseEvent) => {
e.preventDefault();
e.stopPropagation();
- const joinRule = "invite";
- const guestAccess = "can_join";
+ const joinRule = JoinRule.Invite;
+ const guestAccess = GuestAccess.CanJoin;
const beforeJoinRule = this.state.joinRule;
const beforeGuestAccess = this.state.guestAccess;
@@ -149,7 +176,7 @@ export default class SecurityRoomSettingsTab extends React.Component {
});
};
- _onRoomAccessRadioToggle = (roomAccess) => {
+ private onRoomAccessRadioToggle = (roomAccess: string) => {
// join_rule
// INVITE | PUBLIC
// ----------------------+----------------
@@ -163,20 +190,20 @@ export default class SecurityRoomSettingsTab extends React.Component {
// invite them, you clearly want them to join, whether they're a
// guest or not. In practice, guest_access should probably have
// been implemented as part of the join_rules enum.
- let joinRule = "invite";
- let guestAccess = "can_join";
+ let joinRule = JoinRule.Invite;
+ let guestAccess = GuestAccess.CanJoin;
switch (roomAccess) {
case "invite_only":
// no change - use defaults above
break;
case "public_no_guests":
- joinRule = "public";
- guestAccess = "forbidden";
+ joinRule = JoinRule.Public;
+ guestAccess = GuestAccess.Forbidden;
break;
case "public_with_guests":
- joinRule = "public";
- guestAccess = "can_join";
+ joinRule = JoinRule.Public;
+ guestAccess = GuestAccess.CanJoin;
break;
}
@@ -195,7 +222,7 @@ export default class SecurityRoomSettingsTab extends React.Component {
});
};
- _onHistoryRadioToggle = (history) => {
+ private onHistoryRadioToggle = (history: HistoryVisibility) => {
const beforeHistory = this.state.history;
this.setState({history: history});
MatrixClientPeg.get().sendStateEvent(this.props.roomId, "m.room.history_visibility", {
@@ -206,11 +233,11 @@ export default class SecurityRoomSettingsTab extends React.Component {
});
};
- _updateBlacklistDevicesFlag = (checked) => {
+ private updateBlacklistDevicesFlag = (checked: boolean) => {
MatrixClientPeg.get().getRoom(this.props.roomId).setBlacklistUnverifiedDevices(checked);
};
- async _hasAliases() {
+ private async hasAliases(): Promise {
const cli = MatrixClientPeg.get();
if (await cli.doesServerSupportUnstableFeature("org.matrix.msc2432")) {
const response = await cli.unstableGetLocalAliases(this.props.roomId);
@@ -224,7 +251,7 @@ export default class SecurityRoomSettingsTab extends React.Component {
}
}
- _renderRoomAccess() {
+ private renderRoomAccess() {
const client = MatrixClientPeg.get();
const room = client.getRoom(this.props.roomId);
const joinRule = this.state.joinRule;
@@ -240,7 +267,7 @@ export default class SecurityRoomSettingsTab extends React.Component {
{_t("Guests cannot join this room even if explicitly invited.")}
- {_t("Click here to fix")}
+ {_t("Click here to fix")}
);
@@ -265,7 +292,7 @@ export default class SecurityRoomSettingsTab extends React.Component {
;
}
@@ -356,7 +383,7 @@ export default class SecurityRoomSettingsTab extends React.Component {
let historySection = (<>
{_t("Who can read history?")}
- {this._renderHistory()}
+ {this.renderHistory()}
>);
if (!SettingsStore.getValue(UIFeature.RoomHistorySettings)) {
@@ -373,15 +400,16 @@ export default class SecurityRoomSettingsTab extends React.Component {
{_t("Once enabled, encryption cannot be disabled.")}
-
+
{encryptionSettings}