-
- { this.props.title }
+
+
+
+
+ { this.props.title }
+
+ { this.props.headerButton }
+ { cancelButton }
- { this.props.headerButton }
- { cancelButton }
-
- { this.props.children }
-
+ { this.props.children }
+
+
);
},
});
diff --git a/src/components/views/dialogs/BugReportDialog.js b/src/components/views/dialogs/BugReportDialog.js
index a3c4ad96ee..91d2bb5213 100644
--- a/src/components/views/dialogs/BugReportDialog.js
+++ b/src/components/views/dialogs/BugReportDialog.js
@@ -25,8 +25,8 @@ import Modal from '../../../Modal';
import { _t } from '../../../languageHandler';
export default class BugReportDialog extends React.Component {
- constructor(props, context) {
- super(props, context);
+ constructor(props) {
+ super(props);
this.state = {
sendLogs: true,
busy: false,
diff --git a/src/components/views/dialogs/DeactivateAccountDialog.js b/src/components/views/dialogs/DeactivateAccountDialog.js
index 703ecaf092..fc7669e1fe 100644
--- a/src/components/views/dialogs/DeactivateAccountDialog.js
+++ b/src/components/views/dialogs/DeactivateAccountDialog.js
@@ -25,8 +25,8 @@ import * as Lifecycle from '../../../Lifecycle';
import { _t } from '../../../languageHandler';
export default class DeactivateAccountDialog extends React.Component {
- constructor(props, context) {
- super(props, context);
+ constructor(props) {
+ super(props);
this._onOk = this._onOk.bind(this);
this._onCancel = this._onCancel.bind(this);
diff --git a/src/components/views/dialogs/DevtoolsDialog.js b/src/components/views/dialogs/DevtoolsDialog.js
index 9327e1e54e..c9ed71466d 100644
--- a/src/components/views/dialogs/DevtoolsDialog.js
+++ b/src/components/views/dialogs/DevtoolsDialog.js
@@ -16,23 +16,19 @@ limitations under the License.
import React from 'react';
import PropTypes from 'prop-types';
+import { Room } from "matrix-js-sdk";
+
import sdk from '../../../index';
import SyntaxHighlight from '../elements/SyntaxHighlight';
import { _t } from '../../../languageHandler';
-import MatrixClientPeg from '../../../MatrixClientPeg';
import Field from "../elements/Field";
+import MatrixClientContext from "../../../contexts/MatrixClientContext";
-class DevtoolsComponent extends React.Component {
- static contextTypes = {
- roomId: PropTypes.string.isRequired,
- };
-}
-
-class GenericEditor extends DevtoolsComponent {
+class GenericEditor extends React.PureComponent {
// static propTypes = {onBack: PropTypes.func.isRequired};
- constructor(props, context) {
- super(props, context);
+ constructor(props) {
+ super(props);
this._onChange = this._onChange.bind(this);
this.onBack = this.onBack.bind(this);
}
@@ -67,12 +63,15 @@ class SendCustomEvent extends GenericEditor {
static propTypes = {
onBack: PropTypes.func.isRequired,
+ room: PropTypes.instanceOf(Room).isRequired,
forceStateEvent: PropTypes.bool,
inputs: PropTypes.object,
};
- constructor(props, context) {
- super(props, context);
+ static contextType = MatrixClientContext;
+
+ constructor(props) {
+ super(props);
this._send = this._send.bind(this);
const {eventType, stateKey, evContent} = Object.assign({
@@ -91,11 +90,11 @@ class SendCustomEvent extends GenericEditor {
}
send(content) {
- const cli = MatrixClientPeg.get();
+ const cli = this.context;
if (this.state.isStateEvent) {
- return cli.sendStateEvent(this.context.roomId, this.state.eventType, content, this.state.stateKey);
+ return cli.sendStateEvent(this.props.room.roomId, this.state.eventType, content, this.state.stateKey);
} else {
- return cli.sendEvent(this.context.roomId, this.state.eventType, content);
+ return cli.sendEvent(this.props.room.roomId, this.state.eventType, content);
}
}
@@ -154,13 +153,16 @@ class SendAccountData extends GenericEditor {
static getLabel() { return _t('Send Account Data'); }
static propTypes = {
+ room: PropTypes.instanceOf(Room).isRequired,
isRoomAccountData: PropTypes.bool,
forceMode: PropTypes.bool,
inputs: PropTypes.object,
};
- constructor(props, context) {
- super(props, context);
+ static contextType = MatrixClientContext;
+
+ constructor(props) {
+ super(props);
this._send = this._send.bind(this);
const {eventType, evContent} = Object.assign({
@@ -177,9 +179,9 @@ class SendAccountData extends GenericEditor {
}
send(content) {
- const cli = MatrixClientPeg.get();
+ const cli = this.context;
if (this.state.isRoomAccountData) {
- return cli.setRoomAccountData(this.context.roomId, this.state.eventType, content);
+ return cli.setRoomAccountData(this.props.room.roomId, this.state.eventType, content);
}
return cli.setAccountData(this.state.eventType, content);
}
@@ -234,7 +236,7 @@ class SendAccountData extends GenericEditor {
const INITIAL_LOAD_TILES = 20;
const LOAD_TILES_STEP_SIZE = 50;
-class FilteredList extends React.Component {
+class FilteredList extends React.PureComponent {
static propTypes = {
children: PropTypes.any,
query: PropTypes.string,
@@ -247,8 +249,8 @@ class FilteredList extends React.Component {
return children.filter((child) => child.key.toLowerCase().includes(lcQuery));
}
- constructor(props, context) {
- super(props, context);
+ constructor(props) {
+ super(props);
this.state = {
filteredChildren: FilteredList.filterChildren(this.props.children, this.props.query),
@@ -305,19 +307,20 @@ class FilteredList extends React.Component {
}
}
-class RoomStateExplorer extends DevtoolsComponent {
+class RoomStateExplorer extends React.PureComponent {
static getLabel() { return _t('Explore Room State'); }
-
static propTypes = {
onBack: PropTypes.func.isRequired,
+ room: PropTypes.instanceOf(Room).isRequired,
};
- constructor(props, context) {
- super(props, context);
+ static contextType = MatrixClientContext;
- const room = MatrixClientPeg.get().getRoom(this.context.roomId);
- this.roomStateEvents = room.currentState.events;
+ constructor(props) {
+ super(props);
+
+ this.roomStateEvents = this.props.room.currentState.events;
this.onBack = this.onBack.bind(this);
this.editEv = this.editEv.bind(this);
@@ -373,7 +376,7 @@ class RoomStateExplorer extends DevtoolsComponent {
render() {
if (this.state.event) {
if (this.state.editing) {
- return
;
+ return
;
}
return
@@ -553,17 +562,20 @@ class AccountDataExplorer extends DevtoolsComponent {
}
}
-class ServersInRoomList extends DevtoolsComponent {
+class ServersInRoomList extends React.PureComponent {
static getLabel() { return _t('View Servers in Room'); }
static propTypes = {
onBack: PropTypes.func.isRequired,
+ room: PropTypes.instanceOf(Room).isRequired,
};
- constructor(props, context) {
- super(props, context);
+ static contextType = MatrixClientContext;
- const room = MatrixClientPeg.get().getRoom(this.context.roomId);
+ constructor(props) {
+ super(props);
+
+ const room = this.props.room;
const servers = new Set();
room.currentState.getStateEvents("m.room.member").forEach(ev => servers.add(ev.getSender().split(":")[1]));
this.servers = Array.from(servers).map(s =>
@@ -602,19 +614,14 @@ const Entries = [
ServersInRoomList,
];
-export default class DevtoolsDialog extends React.Component {
- static childContextTypes = {
- roomId: PropTypes.string.isRequired,
- // client: PropTypes.instanceOf(MatixClient),
- };
-
+export default class DevtoolsDialog extends React.PureComponent {
static propTypes = {
roomId: PropTypes.string.isRequired,
onFinished: PropTypes.func.isRequired,
};
- constructor(props, context) {
- super(props, context);
+ constructor(props) {
+ super(props);
this.onBack = this.onBack.bind(this);
this.onCancel = this.onCancel.bind(this);
@@ -627,10 +634,6 @@ export default class DevtoolsDialog extends React.Component {
this._unmounted = true;
}
- getChildContext() {
- return { roomId: this.props.roomId };
- }
-
_setMode(mode) {
return () => {
this.setState({ mode });
@@ -654,15 +657,17 @@ export default class DevtoolsDialog extends React.Component {
let body;
if (this.state.mode) {
- body =
-
{ this.state.mode.getLabel() }
-
Room ID: { this.props.roomId }
-
-
-
;
+ body =
+ {(cli) =>
+ { this.state.mode.getLabel() }
+ Room ID: { this.props.roomId }
+
+
+ }
+ ;
} else {
const classes = "mx_DevTools_RoomStateExplorer_button";
- body =
+ body =
{ _t('Toolbox') }
Room ID: { this.props.roomId }
@@ -679,7 +684,7 @@ export default class DevtoolsDialog extends React.Component {
{ _t('Cancel') }
-
;
+ ;
}
const BaseDialog = sdk.getComponent('views.dialogs.BaseDialog');
diff --git a/src/components/views/dialogs/ReportEventDialog.js b/src/components/views/dialogs/ReportEventDialog.js
index 394e5ad47d..af140f6e18 100644
--- a/src/components/views/dialogs/ReportEventDialog.js
+++ b/src/components/views/dialogs/ReportEventDialog.js
@@ -30,8 +30,8 @@ export default class ReportEventDialog extends PureComponent {
onFinished: PropTypes.func.isRequired,
};
- constructor(props, context) {
- super(props, context);
+ constructor(props) {
+ super(props);
this.state = {
reason: "",
diff --git a/src/components/views/elements/EditableTextContainer.js b/src/components/views/elements/EditableTextContainer.js
index 5cba98470c..3d656e6b79 100644
--- a/src/components/views/elements/EditableTextContainer.js
+++ b/src/components/views/elements/EditableTextContainer.js
@@ -25,13 +25,13 @@ import sdk from '../../../index';
* Parent components should supply an 'onSubmit' callback which returns a
* promise; a spinner is shown until the promise resolves.
*
- * The parent can also supply a 'getIntialValue' callback, which works in a
+ * The parent can also supply a 'getInitialValue' callback, which works in a
* similarly asynchronous way. If this is not provided, the initial value is
* taken from the 'initialValue' property.
*/
export default class EditableTextContainer extends React.Component {
- constructor(props, context) {
- super(props, context);
+ constructor(props) {
+ super(props);
this._unmounted = false;
this.state = {
diff --git a/src/components/views/elements/Flair.js b/src/components/views/elements/Flair.js
index 0b9dabeae6..0af772466b 100644
--- a/src/components/views/elements/Flair.js
+++ b/src/components/views/elements/Flair.js
@@ -18,9 +18,9 @@
import React from 'react';
import PropTypes from 'prop-types';
-import {MatrixClient} from 'matrix-js-sdk';
import FlairStore from '../../../stores/FlairStore';
import dis from '../../../dispatcher';
+import MatrixClientContext from "../../../contexts/MatrixClientContext";
class FlairAvatar extends React.Component {
@@ -40,7 +40,7 @@ class FlairAvatar extends React.Component {
}
render() {
- const httpUrl = this.context.matrixClient.mxcUrlToHttp(
+ const httpUrl = this.context.mxcUrlToHttp(
this.props.groupProfile.avatarUrl, 16, 16, 'scale', false);
const tooltip = this.props.groupProfile.name ?
`${this.props.groupProfile.name} (${this.props.groupProfile.groupId})`:
@@ -62,9 +62,7 @@ FlairAvatar.propTypes = {
}),
};
-FlairAvatar.contextTypes = {
- matrixClient: PropTypes.instanceOf(MatrixClient).isRequired,
-};
+FlairAvatar.contextType = MatrixClientContext;
export default class Flair extends React.Component {
constructor() {
@@ -92,7 +90,7 @@ export default class Flair extends React.Component {
for (const groupId of groups) {
let groupProfile = null;
try {
- groupProfile = await FlairStore.getGroupProfileCached(this.context.matrixClient, groupId);
+ groupProfile = await FlairStore.getGroupProfileCached(this.context, groupId);
} catch (err) {
console.error('Could not get profile for group', groupId, err);
}
@@ -134,6 +132,4 @@ Flair.propTypes = {
groups: PropTypes.arrayOf(PropTypes.string),
};
-Flair.contextTypes = {
- matrixClient: PropTypes.instanceOf(MatrixClient).isRequired,
-};
+Flair.contextType = MatrixClientContext;
diff --git a/src/components/views/elements/Pill.js b/src/components/views/elements/Pill.js
index 12830488b1..99005de03b 100644
--- a/src/components/views/elements/Pill.js
+++ b/src/components/views/elements/Pill.js
@@ -20,12 +20,13 @@ import createReactClass from 'create-react-class';
import sdk from '../../../index';
import dis from '../../../dispatcher';
import classNames from 'classnames';
-import { Room, RoomMember, MatrixClient } from 'matrix-js-sdk';
+import { Room, RoomMember } from 'matrix-js-sdk';
import PropTypes from 'prop-types';
import MatrixClientPeg from '../../../MatrixClientPeg';
import { getDisplayAliasForRoom } from '../../../Rooms';
import FlairStore from "../../../stores/FlairStore";
import {getPrimaryPermalinkEntity} from "../../../utils/permalinks/Permalinks";
+import MatrixClientContext from "../../../contexts/MatrixClientContext";
// For URLs of matrix.to links in the timeline which have been reformatted by
// HttpUtils transformTags to relative links. This excludes event URLs (with `[^\/]*`)
@@ -66,17 +67,6 @@ const Pill = createReactClass({
isSelected: PropTypes.bool,
},
-
- childContextTypes: {
- matrixClient: PropTypes.instanceOf(MatrixClient),
- },
-
- getChildContext() {
- return {
- matrixClient: this._matrixClient,
- };
- },
-
getInitialState() {
return {
// ID/alias of the room/user
@@ -127,7 +117,7 @@ const Pill = createReactClass({
}
break;
case Pill.TYPE_USER_MENTION: {
- const localMember = nextProps.room.getMember(resourceId);
+ const localMember = nextProps.room ? nextProps.room.getMember(resourceId) : undefined;
member = localMember;
if (!localMember) {
member = new RoomMember(null, resourceId);
@@ -276,15 +266,17 @@ const Pill = createReactClass({
});
if (this.state.pillType) {
- return this.props.inMessage ?
-
- { avatar }
- { linkText }
- :
-
- { avatar }
- { linkText }
- ;
+ return
+ { this.props.inMessage ?
+
+ { avatar }
+ { linkText }
+ :
+
+ { avatar }
+ { linkText }
+ }
+ ;
} else {
// Deliberately render nothing if the URL isn't recognised
return null;
diff --git a/src/components/views/elements/ReplyThread.js b/src/components/views/elements/ReplyThread.js
index 55fd028980..e7832efca7 100644
--- a/src/components/views/elements/ReplyThread.js
+++ b/src/components/views/elements/ReplyThread.js
@@ -21,10 +21,11 @@ import {_t} from '../../../languageHandler';
import PropTypes from 'prop-types';
import dis from '../../../dispatcher';
import {wantsDateSeparator} from '../../../DateUtils';
-import {MatrixEvent, MatrixClient} from 'matrix-js-sdk';
+import {MatrixEvent} from 'matrix-js-sdk';
import {makeUserPermalink, RoomPermalinkCreator} from "../../../utils/permalinks/Permalinks";
import SettingsStore from "../../../settings/SettingsStore";
import escapeHtml from "escape-html";
+import MatrixClientContext from "../../../contexts/MatrixClientContext";
// This component does no cycle detection, simply because the only way to make such a cycle would be to
// craft event_id's, using a homeserver that generates predictable event IDs; even then the impact would
@@ -38,12 +39,10 @@ export default class ReplyThread extends React.Component {
permalinkCreator: PropTypes.instanceOf(RoomPermalinkCreator).isRequired,
};
- static contextTypes = {
- matrixClient: PropTypes.instanceOf(MatrixClient).isRequired,
- };
+ static contextType = MatrixClientContext;
- constructor(props, context) {
- super(props, context);
+ constructor(props) {
+ super(props);
this.state = {
// The loaded events to be rendered as linear-replies
@@ -187,7 +186,7 @@ export default class ReplyThread extends React.Component {
componentWillMount() {
this.unmounted = false;
- this.room = this.context.matrixClient.getRoom(this.props.parentEv.getRoomId());
+ this.room = this.context.getRoom(this.props.parentEv.getRoomId());
this.room.on("Room.redaction", this.onRoomRedaction);
// same event handler as Room.redaction as for both we just do forceUpdate
this.room.on("Room.redactionCancelled", this.onRoomRedaction);
@@ -259,7 +258,7 @@ export default class ReplyThread extends React.Component {
try {
// ask the client to fetch the event we want using the context API, only interface to do so is to ask
// for a timeline with that event, but once it is loaded we can use findEventById to look up the ev map
- await this.context.matrixClient.getEventTimeline(this.room.getUnfilteredTimelineSet(), eventId);
+ await this.context.getEventTimeline(this.room.getUnfilteredTimelineSet(), eventId);
} catch (e) {
// if it fails catch the error and return early, there's no point trying to find the event in this case.
// Return null as it is falsey and thus should be treated as an error (as the event cannot be resolved).
@@ -300,7 +299,7 @@ export default class ReplyThread extends React.Component {
} else if (this.state.loadedEv) {
const ev = this.state.loadedEv;
const Pill = sdk.getComponent('elements.Pill');
- const room = this.context.matrixClient.getRoom(ev.getRoomId());
+ const room = this.context.getRoom(ev.getRoomId());
header =
{
_t('In reply to ', {}, {
diff --git a/src/components/views/elements/SyntaxHighlight.js b/src/components/views/elements/SyntaxHighlight.js
index 82b5ae572c..bce65cf1a9 100644
--- a/src/components/views/elements/SyntaxHighlight.js
+++ b/src/components/views/elements/SyntaxHighlight.js
@@ -24,8 +24,8 @@ export default class SyntaxHighlight extends React.Component {
children: PropTypes.node,
};
- constructor(props, context) {
- super(props, context);
+ constructor(props) {
+ super(props);
this._ref = this._ref.bind(this);
}
diff --git a/src/components/views/elements/TagTile.js b/src/components/views/elements/TagTile.js
index 767980f0a0..c57d973086 100644
--- a/src/components/views/elements/TagTile.js
+++ b/src/components/views/elements/TagTile.js
@@ -20,7 +20,6 @@ import React, {createRef} from 'react';
import PropTypes from 'prop-types';
import createReactClass from 'create-react-class';
import classNames from 'classnames';
-import { MatrixClient } from 'matrix-js-sdk';
import sdk from '../../../index';
import dis from '../../../dispatcher';
import {_t} from '../../../languageHandler';
@@ -31,6 +30,7 @@ import FlairStore from '../../../stores/FlairStore';
import GroupStore from '../../../stores/GroupStore';
import TagOrderStore from '../../../stores/TagOrderStore';
import {ContextMenu, ContextMenuButton, toRightOf} from "../../structures/ContextMenu";
+import MatrixClientContext from "../../../contexts/MatrixClientContext";
// A class for a child of TagPanel (possibly wrapped in a DNDTagTile) that represents
// a thing to click on for the user to filter the visible rooms in the RoomList to:
@@ -46,8 +46,8 @@ export default createReactClass({
tag: PropTypes.string,
},
- contextTypes: {
- matrixClient: PropTypes.instanceOf(MatrixClient).isRequired,
+ statics: {
+ contextType: MatrixClientContext,
},
getInitialState() {
@@ -81,7 +81,7 @@ export default createReactClass({
_onFlairStoreUpdated() {
if (this.unmounted) return;
FlairStore.getGroupProfileCached(
- this.context.matrixClient,
+ this.context,
this.props.tag,
).then((profile) => {
if (this.unmounted) return;
@@ -145,7 +145,7 @@ export default createReactClass({
const name = profile.name || this.props.tag;
const avatarHeight = 40;
- const httpUrl = profile.avatarUrl ? this.context.matrixClient.mxcUrlToHttp(
+ const httpUrl = profile.avatarUrl ? this.context.mxcUrlToHttp(
profile.avatarUrl, avatarHeight, avatarHeight, "crop",
) : null;
diff --git a/src/components/views/groups/GroupInviteTile.js b/src/components/views/groups/GroupInviteTile.js
index a21b091145..c0d0d9eafe 100644
--- a/src/components/views/groups/GroupInviteTile.js
+++ b/src/components/views/groups/GroupInviteTile.js
@@ -19,13 +19,13 @@ limitations under the License.
import React from 'react';
import PropTypes from 'prop-types';
import createReactClass from 'create-react-class';
-import { MatrixClient } from 'matrix-js-sdk';
import sdk from '../../../index';
import dis from '../../../dispatcher';
import {_t} from '../../../languageHandler';
import classNames from 'classnames';
import MatrixClientPeg from "../../../MatrixClientPeg";
import {ContextMenu, ContextMenuButton, toRightOf} from "../../structures/ContextMenu";
+import MatrixClientContext from "../../../contexts/MatrixClientContext";
// XXX this class copies a lot from RoomTile.js
export default createReactClass({
@@ -35,8 +35,8 @@ export default createReactClass({
group: PropTypes.object.isRequired,
},
- contextTypes: {
- matrixClient: PropTypes.instanceOf(MatrixClient),
+ statics: {
+ contextType: MatrixClientContext,
},
getInitialState: function() {
@@ -58,7 +58,7 @@ export default createReactClass({
onMouseEnter: function() {
const state = {hover: true};
// Only allow non-guests to access the context menu
- if (!this.context.matrixClient.isGuest()) {
+ if (!this.context.isGuest()) {
state.badgeHover = true;
}
this.setState(state);
@@ -118,7 +118,7 @@ export default createReactClass({
const groupName = this.props.group.name || this.props.group.groupId;
const httpAvatarUrl = this.props.group.avatarUrl ?
- this.context.matrixClient.mxcUrlToHttp(this.props.group.avatarUrl, 24, 24) : null;
+ this.context.mxcUrlToHttp(this.props.group.avatarUrl, 24, 24) : null;
const av = ;
diff --git a/src/components/views/groups/GroupMemberInfo.js b/src/components/views/groups/GroupMemberInfo.js
index 3dac90fc35..eb90cdc0f8 100644
--- a/src/components/views/groups/GroupMemberInfo.js
+++ b/src/components/views/groups/GroupMemberInfo.js
@@ -18,7 +18,6 @@ limitations under the License.
import React from 'react';
import PropTypes from 'prop-types';
import createReactClass from 'create-react-class';
-import { MatrixClient } from 'matrix-js-sdk';
import dis from '../../../dispatcher';
import Modal from '../../../Modal';
import sdk from '../../../index';
@@ -26,12 +25,13 @@ import { _t } from '../../../languageHandler';
import { GroupMemberType } from '../../../groups';
import GroupStore from '../../../stores/GroupStore';
import AccessibleButton from '../elements/AccessibleButton';
+import MatrixClientContext from "../../../contexts/MatrixClientContext";
module.exports = createReactClass({
displayName: 'GroupMemberInfo',
- contextTypes: {
- matrixClient: PropTypes.instanceOf(MatrixClient),
+ statics: {
+ contextType: MatrixClientContext,
},
propTypes: {
@@ -85,7 +85,7 @@ module.exports = createReactClass({
_onKick: function() {
const ConfirmUserActionDialog = sdk.getComponent("dialogs.ConfirmUserActionDialog");
Modal.createDialog(ConfirmUserActionDialog, {
- matrixClient: this.context.matrixClient,
+ matrixClient: this.context,
groupMember: this.props.groupMember,
action: this.state.isUserInvited ? _t('Disinvite') : _t('Remove from community'),
title: this.state.isUserInvited ? _t('Disinvite this user from community?')
@@ -95,7 +95,7 @@ module.exports = createReactClass({
if (!proceed) return;
this.setState({removingUser: true});
- this.context.matrixClient.removeUserFromGroup(
+ this.context.removeUserFromGroup(
this.props.groupId, this.props.groupMember.userId,
).then(() => {
// return to the user list
@@ -171,7 +171,7 @@ module.exports = createReactClass({
const avatarUrl = this.props.groupMember.avatarUrl;
let avatarElement;
if (avatarUrl) {
- const httpUrl = this.context.matrixClient.mxcUrlToHttp(avatarUrl, 800, 800);
+ const httpUrl = this.context.mxcUrlToHttp(avatarUrl, 800, 800);
avatarElement = ();
diff --git a/src/components/views/groups/GroupMemberTile.js b/src/components/views/groups/GroupMemberTile.js
index c4b41d23ce..7a9ba9289b 100644
--- a/src/components/views/groups/GroupMemberTile.js
+++ b/src/components/views/groups/GroupMemberTile.js
@@ -19,10 +19,10 @@ limitations under the License.
import React from 'react';
import PropTypes from 'prop-types';
import createReactClass from 'create-react-class';
-import { MatrixClient } from 'matrix-js-sdk';
import sdk from '../../../index';
import dis from '../../../dispatcher';
import { GroupMemberType } from '../../../groups';
+import MatrixClientContext from "../../../contexts/MatrixClientContext";
export default createReactClass({
displayName: 'GroupMemberTile',
@@ -36,8 +36,8 @@ export default createReactClass({
return {};
},
- contextTypes: {
- matrixClient: PropTypes.instanceOf(MatrixClient).isRequired,
+ statics: {
+ contextType: MatrixClientContext,
},
onClick: function(e) {
@@ -53,7 +53,7 @@ export default createReactClass({
const EntityTile = sdk.getComponent('rooms.EntityTile');
const name = this.props.member.displayname || this.props.member.userId;
- const avatarUrl = this.context.matrixClient.mxcUrlToHttp(
+ const avatarUrl = this.context.mxcUrlToHttp(
this.props.member.avatarUrl,
36, 36, 'crop',
);
diff --git a/src/components/views/groups/GroupRoomInfo.js b/src/components/views/groups/GroupRoomInfo.js
index f9f7324e23..d5b8759a67 100644
--- a/src/components/views/groups/GroupRoomInfo.js
+++ b/src/components/views/groups/GroupRoomInfo.js
@@ -17,18 +17,18 @@ limitations under the License.
import React from 'react';
import PropTypes from 'prop-types';
import createReactClass from 'create-react-class';
-import { MatrixClient } from 'matrix-js-sdk';
import dis from '../../../dispatcher';
import Modal from '../../../Modal';
import sdk from '../../../index';
import { _t } from '../../../languageHandler';
import GroupStore from '../../../stores/GroupStore';
+import MatrixClientContext from "../../../contexts/MatrixClientContext";
module.exports = createReactClass({
displayName: 'GroupRoomInfo',
- contextTypes: {
- matrixClient: PropTypes.instanceOf(MatrixClient),
+ statics: {
+ contextType: MatrixClientContext,
},
propTypes: {
@@ -206,7 +206,7 @@ module.exports = createReactClass({
const avatarUrl = this.state.groupRoom.avatarUrl;
let avatarElement;
if (avatarUrl) {
- const httpUrl = this.context.matrixClient.mxcUrlToHttp(avatarUrl, 800, 800);
+ const httpUrl = this.context.mxcUrlToHttp(avatarUrl, 800, 800);
avatarElement = ();
diff --git a/src/components/views/groups/GroupRoomTile.js b/src/components/views/groups/GroupRoomTile.js
index ae325d4796..527e65d30c 100644
--- a/src/components/views/groups/GroupRoomTile.js
+++ b/src/components/views/groups/GroupRoomTile.js
@@ -17,10 +17,10 @@ limitations under the License.
import React from 'react';
import PropTypes from 'prop-types';
import createReactClass from 'create-react-class';
-import {MatrixClient} from 'matrix-js-sdk';
import sdk from '../../../index';
import dis from '../../../dispatcher';
import { GroupRoomType } from '../../../groups';
+import MatrixClientContext from "../../../contexts/MatrixClientContext";
const GroupRoomTile = createReactClass({
displayName: 'GroupRoomTile',
@@ -41,7 +41,7 @@ const GroupRoomTile = createReactClass({
render: function() {
const BaseAvatar = sdk.getComponent('avatars.BaseAvatar');
const AccessibleButton = sdk.getComponent('elements.AccessibleButton');
- const avatarUrl = this.context.matrixClient.mxcUrlToHttp(
+ const avatarUrl = this.context.mxcUrlToHttp(
this.props.groupRoom.avatarUrl,
36, 36, 'crop',
);
@@ -66,9 +66,7 @@ const GroupRoomTile = createReactClass({
},
});
-GroupRoomTile.contextTypes = {
- matrixClient: PropTypes.instanceOf(MatrixClient).isRequired,
-};
+GroupRoomTile.contextType = MatrixClientContext;
export default GroupRoomTile;
diff --git a/src/components/views/groups/GroupTile.js b/src/components/views/groups/GroupTile.js
index 3b64c10a1e..f3d7418a44 100644
--- a/src/components/views/groups/GroupTile.js
+++ b/src/components/views/groups/GroupTile.js
@@ -17,11 +17,11 @@ limitations under the License.
import React from 'react';
import PropTypes from 'prop-types';
import createReactClass from 'create-react-class';
-import {MatrixClient} from 'matrix-js-sdk';
import { Draggable, Droppable } from 'react-beautiful-dnd';
import sdk from '../../../index';
import dis from '../../../dispatcher';
import FlairStore from '../../../stores/FlairStore';
+import MatrixClientContext from "../../../contexts/MatrixClientContext";
function nop() {}
@@ -37,8 +37,8 @@ const GroupTile = createReactClass({
draggable: PropTypes.bool,
},
- contextTypes: {
- matrixClient: PropTypes.instanceOf(MatrixClient).isRequired,
+ statics: {
+ contextType: MatrixClientContext,
},
getInitialState() {
@@ -56,7 +56,7 @@ const GroupTile = createReactClass({
},
componentWillMount: function() {
- FlairStore.getGroupProfileCached(this.context.matrixClient, this.props.groupId).then((profile) => {
+ FlairStore.getGroupProfileCached(this.context, this.props.groupId).then((profile) => {
this.setState({profile});
}).catch((err) => {
console.error('Error whilst getting cached profile for GroupTile', err);
@@ -80,7 +80,7 @@ const GroupTile = createReactClass({
const descElement = this.props.showDescription ?
{ profile.shortDescription }
:
;
- const httpUrl = profile.avatarUrl ? this.context.matrixClient.mxcUrlToHttp(
+ const httpUrl = profile.avatarUrl ? this.context.mxcUrlToHttp(
profile.avatarUrl, avatarHeight, avatarHeight, "crop") : null;
let avatarElement = (
diff --git a/src/components/views/groups/GroupUserSettings.js b/src/components/views/groups/GroupUserSettings.js
index 3cd5731b99..297c0fbd30 100644
--- a/src/components/views/groups/GroupUserSettings.js
+++ b/src/components/views/groups/GroupUserSettings.js
@@ -15,17 +15,16 @@ limitations under the License.
*/
import React from 'react';
-import PropTypes from 'prop-types';
import createReactClass from 'create-react-class';
import sdk from '../../../index';
-import { MatrixClient } from 'matrix-js-sdk';
import { _t } from '../../../languageHandler';
+import MatrixClientContext from "../../../contexts/MatrixClientContext";
export default createReactClass({
displayName: 'GroupUserSettings',
- contextTypes: {
- matrixClient: PropTypes.instanceOf(MatrixClient),
+ statics: {
+ contextType: MatrixClientContext,
},
getInitialState() {
@@ -36,7 +35,7 @@ export default createReactClass({
},
componentWillMount: function() {
- this.context.matrixClient.getJoinedGroups().then((result) => {
+ this.context.getJoinedGroups().then((result) => {
this.setState({groups: result.groups || [], error: null});
}, (err) => {
console.error(err);
diff --git a/src/components/views/messages/MFileBody.js b/src/components/views/messages/MFileBody.js
index 552b1108d2..6045ec0571 100644
--- a/src/components/views/messages/MFileBody.js
+++ b/src/components/views/messages/MFileBody.js
@@ -26,6 +26,7 @@ import {decryptFile} from '../../../utils/DecryptFile';
import Tinter from '../../../Tinter';
import request from 'browser-request';
import Modal from '../../../Modal';
+import SdkConfig from "../../../SdkConfig";
// A cached tinted copy of require("../../../../res/img/download.svg")
@@ -214,10 +215,6 @@ module.exports = createReactClass({
tileShape: PropTypes.string,
},
- contextTypes: {
- appConfig: PropTypes.object,
- },
-
/**
* Extracts a human readable label for the file attachment to use as
* link text.
@@ -360,8 +357,9 @@ module.exports = createReactClass({
// If the attachment is encryped then put the link inside an iframe.
let renderer_url = DEFAULT_CROSS_ORIGIN_RENDERER;
- if (this.context.appConfig && this.context.appConfig.cross_origin_renderer_url) {
- renderer_url = this.context.appConfig.cross_origin_renderer_url;
+ const appConfig = SdkConfig.get();
+ if (appConfig && appConfig.cross_origin_renderer_url) {
+ renderer_url = appConfig.cross_origin_renderer_url;
}
renderer_url += "?origin=" + encodeURIComponent(window.location.origin);
return (
diff --git a/src/components/views/messages/MImageBody.js b/src/components/views/messages/MImageBody.js
index 427056203d..dbe6636c6b 100644
--- a/src/components/views/messages/MImageBody.js
+++ b/src/components/views/messages/MImageBody.js
@@ -18,7 +18,6 @@ limitations under the License.
import React, {createRef} from 'react';
import PropTypes from 'prop-types';
-import { MatrixClient } from 'matrix-js-sdk';
import MFileBody from './MFileBody';
import Modal from '../../../Modal';
@@ -26,6 +25,7 @@ import sdk from '../../../index';
import { decryptFile } from '../../../utils/DecryptFile';
import { _t } from '../../../languageHandler';
import SettingsStore from "../../../settings/SettingsStore";
+import MatrixClientContext from "../../../contexts/MatrixClientContext";
export default class MImageBody extends React.Component {
static propTypes = {
@@ -39,9 +39,7 @@ export default class MImageBody extends React.Component {
maxImageHeight: PropTypes.number,
};
- static contextTypes = {
- matrixClient: PropTypes.instanceOf(MatrixClient),
- };
+ static contextType = MatrixClientContext;
constructor(props) {
super(props);
@@ -71,7 +69,7 @@ export default class MImageBody extends React.Component {
componentWillMount() {
this.unmounted = false;
- this.context.matrixClient.on('sync', this.onClientSync);
+ this.context.on('sync', this.onClientSync);
}
// FIXME: factor this out and aplpy it to MVideoBody and MAudioBody too!
@@ -174,7 +172,7 @@ export default class MImageBody extends React.Component {
if (content.file !== undefined) {
return this.state.decryptedUrl;
} else {
- return this.context.matrixClient.mxcUrlToHttp(content.url);
+ return this.context.mxcUrlToHttp(content.url);
}
}
@@ -198,7 +196,7 @@ export default class MImageBody extends React.Component {
// special case to return clientside sender-generated thumbnails for SVGs, if any,
// given we deliberately don't thumbnail them serverside to prevent
// billion lol attacks and similar
- return this.context.matrixClient.mxcUrlToHttp(
+ return this.context.mxcUrlToHttp(
content.info.thumbnail_url,
thumbWidth,
thumbHeight,
@@ -221,7 +219,7 @@ export default class MImageBody extends React.Component {
pixelRatio === 1.0 ||
(!info || !info.w || !info.h || !info.size)
) {
- return this.context.matrixClient.mxcUrlToHttp(content.url, thumbWidth, thumbHeight);
+ return this.context.mxcUrlToHttp(content.url, thumbWidth, thumbHeight);
} else {
// we should only request thumbnails if the image is bigger than 800x600
// (or 1600x1200 on retina) otherwise the image in the timeline will just
@@ -242,7 +240,7 @@ export default class MImageBody extends React.Component {
// image is too large physically and bytewise to clutter our timeline so
// we ask for a thumbnail, despite knowing that it will be max 800x600
// despite us being retina (as synapse doesn't do 1600x1200 thumbs yet).
- return this.context.matrixClient.mxcUrlToHttp(
+ return this.context.mxcUrlToHttp(
content.url,
thumbWidth,
thumbHeight,
@@ -251,7 +249,7 @@ export default class MImageBody extends React.Component {
// download the original image otherwise, so we can scale it client side
// to take pixelRatio into account.
// ( no width/height means we want the original image)
- return this.context.matrixClient.mxcUrlToHttp(
+ return this.context.mxcUrlToHttp(
content.url,
);
}
@@ -308,7 +306,7 @@ export default class MImageBody extends React.Component {
componentWillUnmount() {
this.unmounted = true;
- this.context.matrixClient.removeListener('sync', this.onClientSync);
+ this.context.removeListener('sync', this.onClientSync);
this._afterComponentWillUnmount();
if (this.state.decryptedUrl) {
diff --git a/src/components/views/messages/MessageActionBar.js b/src/components/views/messages/MessageActionBar.js
index 52d7a74632..5c40c19b49 100644
--- a/src/components/views/messages/MessageActionBar.js
+++ b/src/components/views/messages/MessageActionBar.js
@@ -25,7 +25,7 @@ import dis from '../../../dispatcher';
import Modal from '../../../Modal';
import {aboveLeftOf, ContextMenu, ContextMenuButton, useContextMenu} from '../../structures/ContextMenu';
import { isContentActionable, canEditContent } from '../../../utils/EventUtils';
-import {RoomContext} from "../../structures/RoomView";
+import RoomContext from "../../../contexts/RoomContext";
const OptionsButton = ({mxEvent, getTile, getReplyThread, permalinkCreator, onFocusChange}) => {
const [menuDisplayed, button, openMenu, closeMenu] = useContextMenu();
@@ -117,9 +117,7 @@ export default class MessageActionBar extends React.PureComponent {
onFocusChange: PropTypes.func,
};
- static contextTypes = {
- room: RoomContext,
- };
+ static contextType = RoomContext;
componentDidMount() {
this.props.mxEvent.on("Event.decrypted", this.onDecrypted);
@@ -164,12 +162,12 @@ export default class MessageActionBar extends React.PureComponent {
let editButton;
if (isContentActionable(this.props.mxEvent)) {
- if (this.context.room.canReact) {
+ if (this.context.canReact) {
reactButton = (
);
}
- if (this.context.room.canReply) {
+ if (this.context.canReply) {
replyButton = {
if (this.unmounted) return;
this.setState({userGroups});
});
- this.context.matrixClient.on('RoomState.events', this.onRoomStateEvents);
+ this.context.on('RoomState.events', this.onRoomStateEvents);
},
componentWillUnmount() {
this.unmounted = true;
- this.context.matrixClient.removeListener('RoomState.events', this.onRoomStateEvents);
+ this.context.removeListener('RoomState.events', this.onRoomStateEvents);
},
onRoomStateEvents(event) {
@@ -71,7 +71,7 @@ export default createReactClass({
_updateRelatedGroups() {
if (this.unmounted) return;
- const room = this.context.matrixClient.getRoom(this.props.mxEvent.getRoomId());
+ const room = this.context.getRoom(this.props.mxEvent.getRoomId());
if (!room) return;
const relatedGroupsEvent = room.currentState.getStateEvents('m.room.related_groups', '');
diff --git a/src/components/views/right_panel/UserInfo.js b/src/components/views/right_panel/UserInfo.js
index 65fc0bf66b..da82fb2bdf 100644
--- a/src/components/views/right_panel/UserInfo.js
+++ b/src/components/views/right_panel/UserInfo.js
@@ -17,7 +17,7 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
-import React, {useCallback, useMemo, useState, useEffect} from 'react';
+import React, {useCallback, useMemo, useState, useEffect, useContext} from 'react';
import PropTypes from 'prop-types';
import classNames from 'classnames';
import {Group, RoomMember, User} from 'matrix-js-sdk';
@@ -37,9 +37,9 @@ import MultiInviter from "../../../utils/MultiInviter";
import GroupStore from "../../../stores/GroupStore";
import MatrixClientPeg from "../../../MatrixClientPeg";
import E2EIcon from "../rooms/E2EIcon";
-import withLegacyMatrixClient from "../../../utils/withLegacyMatrixClient";
import {useEventEmitter} from "../../../hooks/useEventEmitter";
import {textualPowerLevel} from '../../../Roles';
+import MatrixClientContext from "../../../contexts/MatrixClientContext";
const _disambiguateDevices = (devices) => {
const names = Object.create(null);
@@ -203,7 +203,9 @@ function DevicesSection({devices, userId, loading}) {
);
}
-const UserOptionsSection = withLegacyMatrixClient(({matrixClient: cli, member, isIgnored, canInvite, devices}) => {
+const UserOptionsSection = ({member, isIgnored, canInvite, devices}) => {
+ const cli = useContext(MatrixClientContext);
+
let ignoreButton = null;
let insertPillButton = null;
let inviteUserButton = null;
@@ -336,7 +338,7 @@ const UserOptionsSection = withLegacyMatrixClient(({matrixClient: cli, member, i
);
-});
+};
const _warnSelfDemote = async () => {
const QuestionDialog = sdk.getComponent("dialogs.QuestionDialog");
@@ -404,7 +406,9 @@ const useRoomPowerLevels = (cli, room) => {
return powerLevels;
};
-const RoomKickButton = withLegacyMatrixClient(({matrixClient: cli, member, startUpdating, stopUpdating}) => {
+const RoomKickButton = ({member, startUpdating, stopUpdating}) => {
+ const cli = useContext(MatrixClientContext);
+
const onKick = async () => {
const ConfirmUserActionDialog = sdk.getComponent("dialogs.ConfirmUserActionDialog");
const {finished} = Modal.createTrackedDialog(
@@ -444,9 +448,11 @@ const RoomKickButton = withLegacyMatrixClient(({matrixClient: cli, member, start
return
{ kickLabel }
;
-});
+};
+
+const RedactMessagesButton = ({member}) => {
+ const cli = useContext(MatrixClientContext);
-const RedactMessagesButton = withLegacyMatrixClient(({matrixClient: cli, member}) => {
const onRedactAllMessages = async () => {
const {roomId, userId} = member;
const room = cli.getRoom(roomId);
@@ -517,9 +523,11 @@ const RedactMessagesButton = withLegacyMatrixClient(({matrixClient: cli, member}
return
{ _t("Remove recent messages") }
;
-});
+};
+
+const BanToggleButton = ({member, startUpdating, stopUpdating}) => {
+ const cli = useContext(MatrixClientContext);
-const BanToggleButton = withLegacyMatrixClient(({matrixClient: cli, member, startUpdating, stopUpdating}) => {
const onBanOrUnban = async () => {
const ConfirmUserActionDialog = sdk.getComponent("dialogs.ConfirmUserActionDialog");
const {finished} = Modal.createTrackedDialog(
@@ -573,207 +581,206 @@ const BanToggleButton = withLegacyMatrixClient(({matrixClient: cli, member, star
return
{ label }
;
-});
+};
-const MuteToggleButton = withLegacyMatrixClient(
- ({matrixClient: cli, member, room, powerLevels, startUpdating, stopUpdating}) => {
- const isMuted = _isMuted(member, powerLevels);
- const onMuteToggle = async () => {
- const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
- const roomId = member.roomId;
- const target = member.userId;
+const MuteToggleButton = ({member, room, powerLevels, startUpdating, stopUpdating}) => {
+ const cli = useContext(MatrixClientContext);
- // if muting self, warn as it may be irreversible
- if (target === cli.getUserId()) {
- try {
- if (!(await _warnSelfDemote())) return;
- } catch (e) {
- console.error("Failed to warn about self demotion: ", e);
- return;
- }
+ const isMuted = _isMuted(member, powerLevels);
+ const onMuteToggle = async () => {
+ const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
+ const roomId = member.roomId;
+ const target = member.userId;
+
+ // if muting self, warn as it may be irreversible
+ if (target === cli.getUserId()) {
+ try {
+ if (!(await _warnSelfDemote())) return;
+ } catch (e) {
+ console.error("Failed to warn about self demotion: ", e);
+ return;
}
+ }
- const powerLevelEvent = room.currentState.getStateEvents("m.room.power_levels", "");
- if (!powerLevelEvent) return;
+ const powerLevelEvent = room.currentState.getStateEvents("m.room.power_levels", "");
+ if (!powerLevelEvent) return;
- const powerLevels = powerLevelEvent.getContent();
- const levelToSend = (
- (powerLevels.events ? powerLevels.events["m.room.message"] : null) ||
- powerLevels.events_default
- );
- let level;
- if (isMuted) { // unmute
- level = levelToSend;
- } else { // mute
- level = levelToSend - 1;
- }
- level = parseInt(level);
+ const powerLevels = powerLevelEvent.getContent();
+ const levelToSend = (
+ (powerLevels.events ? powerLevels.events["m.room.message"] : null) ||
+ powerLevels.events_default
+ );
+ let level;
+ if (isMuted) { // unmute
+ level = levelToSend;
+ } else { // mute
+ level = levelToSend - 1;
+ }
+ level = parseInt(level);
- if (!isNaN(level)) {
- startUpdating();
- cli.setPowerLevel(roomId, target, level, powerLevelEvent).then(() => {
- // NO-OP; rely on the m.room.member event coming down else we could
- // get out of sync if we force setState here!
- console.log("Mute toggle success");
- }, function(err) {
- console.error("Mute error: " + err);
- Modal.createTrackedDialog('Failed to mute user', '', ErrorDialog, {
- title: _t("Error"),
- description: _t("Failed to mute user"),
- });
- }).finally(() => {
- stopUpdating();
+ if (!isNaN(level)) {
+ startUpdating();
+ cli.setPowerLevel(roomId, target, level, powerLevelEvent).then(() => {
+ // NO-OP; rely on the m.room.member event coming down else we could
+ // get out of sync if we force setState here!
+ console.log("Mute toggle success");
+ }, function(err) {
+ console.error("Mute error: " + err);
+ Modal.createTrackedDialog('Failed to mute user', '', ErrorDialog, {
+ title: _t("Error"),
+ description: _t("Failed to mute user"),
});
- }
+ }).finally(() => {
+ stopUpdating();
+ });
+ }
+ };
+
+ const classes = classNames("mx_UserInfo_field", {
+ mx_UserInfo_destructive: !isMuted,
+ });
+
+ const muteLabel = isMuted ? _t("Unmute") : _t("Mute");
+ return
+ { muteLabel }
+ ;
+};
+
+const RoomAdminToolsContainer = ({room, children, member, startUpdating, stopUpdating, powerLevels}) => {
+ const cli = useContext(MatrixClientContext);
+ let kickButton;
+ let banButton;
+ let muteButton;
+ let redactButton;
+
+ const editPowerLevel = (
+ (powerLevels.events ? powerLevels.events["m.room.power_levels"] : null) ||
+ powerLevels.state_default
+ );
+
+ const me = room.getMember(cli.getUserId());
+ const isMe = me.userId === member.userId;
+ const canAffectUser = member.powerLevel < me.powerLevel || isMe;
+
+ if (canAffectUser && me.powerLevel >= powerLevels.kick) {
+ kickButton =
;
+ }
+ if (me.powerLevel >= powerLevels.redact) {
+ redactButton = (
+
+ );
+ }
+ if (canAffectUser && me.powerLevel >= powerLevels.ban) {
+ banButton =
;
+ }
+ if (canAffectUser && me.powerLevel >= editPowerLevel) {
+ muteButton = (
+
+ );
+ }
+
+ if (kickButton || banButton || muteButton || redactButton || children) {
+ return
+ { muteButton }
+ { kickButton }
+ { banButton }
+ { redactButton }
+ { children }
+ ;
+ }
+
+ return
;
+};
+
+const GroupAdminToolsSection = ({children, groupId, groupMember, startUpdating, stopUpdating}) => {
+ const cli = useContext(MatrixClientContext);
+
+ const [isPrivileged, setIsPrivileged] = useState(false);
+ const [isInvited, setIsInvited] = useState(false);
+
+ // Listen to group store changes
+ useEffect(() => {
+ let unmounted = false;
+
+ const onGroupStoreUpdated = () => {
+ if (unmounted) return;
+ setIsPrivileged(GroupStore.isUserPrivileged(groupId));
+ setIsInvited(GroupStore.getGroupInvitedMembers(groupId).some(
+ (m) => m.userId === groupMember.userId,
+ ));
};
- const classes = classNames("mx_UserInfo_field", {
- mx_UserInfo_destructive: !isMuted,
- });
+ GroupStore.registerListener(groupId, onGroupStoreUpdated);
+ onGroupStoreUpdated();
+ // Handle unmount
+ return () => {
+ unmounted = true;
+ GroupStore.unregisterListener(onGroupStoreUpdated);
+ };
+ }, [groupId, groupMember.userId]);
- const muteLabel = isMuted ? _t("Unmute") : _t("Mute");
- return
- { muteLabel }
- ;
- },
-);
+ if (isPrivileged) {
+ const _onKick = async () => {
+ const ConfirmUserActionDialog = sdk.getComponent("dialogs.ConfirmUserActionDialog");
+ const {finished} = Modal.createDialog(ConfirmUserActionDialog, {
+ matrixClient: cli,
+ groupMember,
+ action: isInvited ? _t('Disinvite') : _t('Remove from community'),
+ title: isInvited ? _t('Disinvite this user from community?')
+ : _t('Remove this user from community?'),
+ danger: true,
+ });
-const RoomAdminToolsContainer = withLegacyMatrixClient(
- ({matrixClient: cli, room, children, member, startUpdating, stopUpdating, powerLevels}) => {
- let kickButton;
- let banButton;
- let muteButton;
- let redactButton;
+ const [proceed] = await finished;
+ if (!proceed) return;
- const editPowerLevel = (
- (powerLevels.events ? powerLevels.events["m.room.power_levels"] : null) ||
- powerLevels.state_default
+ startUpdating();
+ cli.removeUserFromGroup(groupId, groupMember.userId).then(() => {
+ // return to the user list
+ dis.dispatch({
+ action: "view_user",
+ member: null,
+ });
+ }).catch((e) => {
+ const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
+ Modal.createTrackedDialog('Failed to remove user from group', '', ErrorDialog, {
+ title: _t('Error'),
+ description: isInvited ?
+ _t('Failed to withdraw invitation') :
+ _t('Failed to remove user from community'),
+ });
+ console.log(e);
+ }).finally(() => {
+ stopUpdating();
+ });
+ };
+
+ const kickButton = (
+
+ { isInvited ? _t('Disinvite') : _t('Remove from community') }
+
);
- const me = room.getMember(cli.getUserId());
- const isMe = me.userId === member.userId;
- const canAffectUser = member.powerLevel < me.powerLevel || isMe;
+ // No make/revoke admin API yet
+ /*const opLabel = this.state.isTargetMod ? _t("Revoke Moderator") : _t("Make Moderator");
+ giveModButton =
+ {giveOpLabel}
+ ;*/
- if (canAffectUser && me.powerLevel >= powerLevels.kick) {
- kickButton =
;
- }
- if (me.powerLevel >= powerLevels.redact) {
- redactButton = (
-
- );
- }
- if (canAffectUser && me.powerLevel >= powerLevels.ban) {
- banButton =
;
- }
- if (canAffectUser && me.powerLevel >= editPowerLevel) {
- muteButton = (
-
- );
- }
+ return
+ { kickButton }
+ { children }
+ ;
+ }
- if (kickButton || banButton || muteButton || redactButton || children) {
- return
- { muteButton }
- { kickButton }
- { banButton }
- { redactButton }
- { children }
- ;
- }
-
- return
;
- },
-);
-
-const GroupAdminToolsSection = withLegacyMatrixClient(
- ({matrixClient: cli, children, groupId, groupMember, startUpdating, stopUpdating}) => {
- const [isPrivileged, setIsPrivileged] = useState(false);
- const [isInvited, setIsInvited] = useState(false);
-
- // Listen to group store changes
- useEffect(() => {
- let unmounted = false;
-
- const onGroupStoreUpdated = () => {
- if (unmounted) return;
- setIsPrivileged(GroupStore.isUserPrivileged(groupId));
- setIsInvited(GroupStore.getGroupInvitedMembers(groupId).some(
- (m) => m.userId === groupMember.userId,
- ));
- };
-
- GroupStore.registerListener(groupId, onGroupStoreUpdated);
- onGroupStoreUpdated();
- // Handle unmount
- return () => {
- unmounted = true;
- GroupStore.unregisterListener(onGroupStoreUpdated);
- };
- }, [groupId, groupMember.userId]);
-
- if (isPrivileged) {
- const _onKick = async () => {
- const ConfirmUserActionDialog = sdk.getComponent("dialogs.ConfirmUserActionDialog");
- const {finished} = Modal.createDialog(ConfirmUserActionDialog, {
- matrixClient: cli,
- groupMember,
- action: isInvited ? _t('Disinvite') : _t('Remove from community'),
- title: isInvited ? _t('Disinvite this user from community?')
- : _t('Remove this user from community?'),
- danger: true,
- });
-
- const [proceed] = await finished;
- if (!proceed) return;
-
- startUpdating();
- cli.removeUserFromGroup(groupId, groupMember.userId).then(() => {
- // return to the user list
- dis.dispatch({
- action: "view_user",
- member: null,
- });
- }).catch((e) => {
- const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
- Modal.createTrackedDialog('Failed to remove user from group', '', ErrorDialog, {
- title: _t('Error'),
- description: isInvited ?
- _t('Failed to withdraw invitation') :
- _t('Failed to remove user from community'),
- });
- console.log(e);
- }).finally(() => {
- stopUpdating();
- });
- };
-
- const kickButton = (
-
- { isInvited ? _t('Disinvite') : _t('Remove from community') }
-
- );
-
- // No make/revoke admin API yet
- /*const opLabel = this.state.isTargetMod ? _t("Revoke Moderator") : _t("Make Moderator");
- giveModButton =
- {giveOpLabel}
- ;*/
-
- return
- { kickButton }
- { children }
- ;
- }
-
- return
;
- },
-);
+ return
;
+};
const GroupMember = PropTypes.shape({
userId: PropTypes.string.isRequired,
@@ -849,7 +856,7 @@ function useRoomPermissions(cli, room, user) {
return roomPermissions;
}
-const PowerLevelSection = withLegacyMatrixClient(({matrixClient: cli, user, room, roomPermissions, powerLevels}) => {
+const PowerLevelSection = ({user, room, roomPermissions, powerLevels}) => {
const [isEditing, setEditing] = useState(false);
if (room && user.roomId) { // is in room
if (isEditing) {
@@ -876,9 +883,11 @@ const PowerLevelSection = withLegacyMatrixClient(({matrixClient: cli, user, room
} else {
return null;
}
-});
+};
+
+const PowerLevelEditor = ({user, room, roomPermissions, onFinished}) => {
+ const cli = useContext(MatrixClientContext);
-const PowerLevelEditor = withLegacyMatrixClient(({matrixClient: cli, user, room, roomPermissions, onFinished}) => {
const [isUpdating, setIsUpdating] = useState(false);
const [selectedPowerLevel, setSelectedPowerLevel] = useState(parseInt(user.powerLevel, 10));
const [isDirty, setIsDirty] = useState(false);
@@ -982,10 +991,11 @@ const PowerLevelEditor = withLegacyMatrixClient(({matrixClient: cli, user, room,
{buttonOrSpinner}
);
-});
+};
+
+const UserInfo = ({user, groupId, roomId, onClose}) => {
+ const cli = useContext(MatrixClientContext);
-// cli is injected by withLegacyMatrixClient
-const UserInfo = withLegacyMatrixClient(({matrixClient: cli, user, groupId, roomId, onClose}) => {
// Load room if we are given a room id and memoize it
const room = useMemo(() => roomId ? cli.getRoom(roomId) : null, [cli, roomId]);
@@ -1316,7 +1326,7 @@ const UserInfo = withLegacyMatrixClient(({matrixClient: cli, user, groupId, room