Remove create-react-class

This commit is contained in:
Michael Telatynski 2020-08-29 12:14:16 +01:00
parent 672d0fe97b
commit 72498df28f
108 changed files with 3059 additions and 3545 deletions

View file

@ -17,7 +17,6 @@ limitations under the License.
import React from 'react';
import PropTypes from 'prop-types';
import createReactClass from 'create-react-class';
import {MatrixClientPeg} from '../../../MatrixClientPeg';
import AppTile from '../elements/AppTile';
import Modal from '../../../Modal';
@ -34,50 +33,50 @@ import SettingsStore from "../../../settings/SettingsStore";
// The maximum number of widgets that can be added in a room
const MAX_WIDGETS = 2;
export default createReactClass({
displayName: 'AppsDrawer',
propTypes: {
export default class AppsDrawer extends React.Component {
static propTypes = {
userId: PropTypes.string.isRequired,
room: PropTypes.object.isRequired,
showApps: PropTypes.bool, // Should apps be rendered
hide: PropTypes.bool, // If rendered, should apps drawer be visible
},
};
getDefaultProps: () => ({
static defaultProps = {
showApps: true,
hide: false,
}),
};
getInitialState: function() {
return {
constructor(props) {
super(props);
this.state = {
apps: this._getApps(),
};
},
}
componentDidMount: function() {
componentDidMount() {
ScalarMessaging.startListening();
MatrixClientPeg.get().on('RoomState.events', this.onRoomStateEvents);
WidgetEchoStore.on('update', this._updateApps);
this.dispatcherRef = dis.register(this.onAction);
},
}
componentWillUnmount: function() {
componentWillUnmount() {
ScalarMessaging.stopListening();
if (MatrixClientPeg.get()) {
MatrixClientPeg.get().removeListener('RoomState.events', this.onRoomStateEvents);
}
WidgetEchoStore.removeListener('update', this._updateApps);
if (this.dispatcherRef) dis.unregister(this.dispatcherRef);
},
}
// TODO: [REACT-WARNING] Replace with appropriate lifecycle event
UNSAFE_componentWillReceiveProps(newProps) {
// Room has changed probably, update apps
this._updateApps();
},
}
onAction: function(action) {
onAction = (action) => {
const hideWidgetKey = this.props.room.roomId + '_hide_widget_drawer';
switch (action.action) {
case 'appsDrawer':
@ -93,16 +92,16 @@ export default createReactClass({
break;
}
},
};
onRoomStateEvents: function(ev, state) {
onRoomStateEvents = (ev, state) => {
if (ev.getRoomId() !== this.props.room.roomId || ev.getType() !== 'im.vector.modular.widgets') {
return;
}
this._updateApps();
},
};
_getApps: function() {
_getApps() {
const widgets = WidgetEchoStore.getEchoedRoomWidgets(
this.props.room.roomId, WidgetUtils.getRoomWidgets(this.props.room),
);
@ -111,33 +110,33 @@ export default createReactClass({
ev.getStateKey(), ev.getContent(), ev.getSender(), ev.getRoomId(), ev.getId(),
);
});
},
}
_updateApps: function() {
_updateApps = () => {
const apps = this._getApps();
this.setState({
apps: apps,
});
},
};
_canUserModify: function() {
_canUserModify() {
try {
return WidgetUtils.canUserModifyWidgets(this.props.room.roomId);
} catch (err) {
console.error(err);
return false;
}
},
}
_launchManageIntegrations: function() {
_launchManageIntegrations() {
if (SettingsStore.getValue("feature_many_integration_managers")) {
IntegrationManagers.sharedInstance().openAll();
} else {
IntegrationManagers.sharedInstance().getPrimaryManager().open(this.props.room, 'add_integ');
}
},
}
onClickAddWidget: function(e) {
onClickAddWidget = (e) => {
e.preventDefault();
// Display a warning dialog if the max number of widgets have already been added to the room
const apps = this._getApps();
@ -152,9 +151,9 @@ export default createReactClass({
return;
}
this._launchManageIntegrations();
},
};
render: function() {
render() {
const apps = this.state.apps.map((app, index, arr) => {
const capWhitelist = WidgetUtils.getCapWhitelistForAppTypeInRoomId(app.type, this.props.room.roomId);
@ -211,5 +210,5 @@ export default createReactClass({
{ this._canUserModify() && addWidget }
</div>
);
},
});
}
}

View file

@ -17,7 +17,6 @@ limitations under the License.
import React from 'react';
import PropTypes from 'prop-types';
import createReactClass from 'create-react-class';
import {MatrixClientPeg} from "../../../MatrixClientPeg";
import * as sdk from '../../../index';
import dis from "../../../dispatcher/dispatcher";
@ -31,10 +30,8 @@ import AutoHideScrollbar from "../../structures/AutoHideScrollbar";
import CallView from "../voip/CallView";
export default createReactClass({
displayName: 'AuxPanel',
propTypes: {
export default class AuxPanel extends React.Component {
static propTypes = {
// js-sdk room object
room: PropTypes.object.isRequired,
userId: PropTypes.string.isRequired,
@ -58,42 +55,46 @@ export default createReactClass({
// content in a way that is likely to make it change size.
onResize: PropTypes.func,
fullHeight: PropTypes.bool,
},
};
getDefaultProps: () => ({
static defaultProps = {
showApps: true,
hideAppsDrawer: false,
}),
};
getInitialState: function() {
return { counters: this._computeCounters() };
},
constructor(props) {
super(props);
componentDidMount: function() {
this.state = {
counters: this._computeCounters(),
};
}
componentDidMount() {
const cli = MatrixClientPeg.get();
cli.on("RoomState.events", this._rateLimitedUpdate);
},
}
componentWillUnmount: function() {
componentWillUnmount() {
const cli = MatrixClientPeg.get();
if (cli) {
cli.removeListener("RoomState.events", this._rateLimitedUpdate);
}
},
}
shouldComponentUpdate: function(nextProps, nextState) {
shouldComponentUpdate(nextProps, nextState) {
return (!ObjectUtils.shallowEqual(this.props, nextProps) ||
!ObjectUtils.shallowEqual(this.state, nextState));
},
}
componentDidUpdate: function(prevProps, prevState) {
componentDidUpdate(prevProps, prevState) {
// most changes are likely to cause a resize
if (this.props.onResize) {
this.props.onResize();
}
},
}
onConferenceNotificationClick: function(ev, type) {
onConferenceNotificationClick = (ev, type) => {
dis.dispatch({
action: 'place_call',
type: type,
@ -101,15 +102,15 @@ export default createReactClass({
});
ev.stopPropagation();
ev.preventDefault();
},
};
_rateLimitedUpdate: new RateLimitedFunc(function() {
_rateLimitedUpdate = new RateLimitedFunc(() => {
if (SettingsStore.getValue("feature_state_counters")) {
this.setState({counters: this._computeCounters()});
}
}, 500),
}, 500);
_computeCounters: function() {
_computeCounters() {
let counters = [];
if (this.props.room && SettingsStore.getValue("feature_state_counters")) {
@ -140,9 +141,9 @@ export default createReactClass({
}
return counters;
},
}
render: function() {
render() {
const TintableSvg = sdk.getComponent("elements.TintableSvg");
let fileDropTarget = null;
@ -274,5 +275,5 @@ export default createReactClass({
{ this.props.children }
</AutoHideScrollbar>
);
},
});
}
}

View file

@ -18,7 +18,6 @@ limitations under the License.
import React from 'react';
import PropTypes from 'prop-types';
import createReactClass from 'create-react-class';
import * as sdk from '../../../index';
import AccessibleButton from '../elements/AccessibleButton';
import { _t } from '../../../languageHandler';
@ -51,10 +50,8 @@ function presenceClassForMember(presenceState, lastActiveAgo, showPresence) {
}
}
const EntityTile = createReactClass({
displayName: 'EntityTile',
propTypes: {
class EntityTile extends React.Component {
static propTypes = {
name: PropTypes.string,
title: PropTypes.string,
avatarJsx: PropTypes.any, // <BaseAvatar />
@ -70,33 +67,29 @@ const EntityTile = createReactClass({
showPresence: PropTypes.bool,
subtextLabel: PropTypes.string,
e2eStatus: PropTypes.string,
},
};
getDefaultProps: function() {
return {
shouldComponentUpdate: function(nextProps, nextState) { return true; },
onClick: function() {},
presenceState: "offline",
presenceLastActiveAgo: 0,
presenceLastTs: 0,
showInviteButton: false,
suppressOnHover: false,
showPresence: true,
};
},
static defaultProps = {
shouldComponentUpdate: function(nextProps, nextState) { return true; },
onClick: function() {},
presenceState: "offline",
presenceLastActiveAgo: 0,
presenceLastTs: 0,
showInviteButton: false,
suppressOnHover: false,
showPresence: true,
};
getInitialState: function() {
return {
hover: false,
};
},
state = {
hover: false,
};
shouldComponentUpdate: function(nextProps, nextState) {
shouldComponentUpdate(nextProps, nextState) {
if (this.state.hover !== nextState.hover) return true;
return this.props.shouldComponentUpdate(nextProps, nextState);
},
}
render: function() {
render() {
const mainClassNames = {
"mx_EntityTile": true,
"mx_EntityTile_noHover": this.props.suppressOnHover,
@ -193,8 +186,8 @@ const EntityTile = createReactClass({
</AccessibleButton>
</div>
);
},
});
}
}
EntityTile.POWER_STATUS_MODERATOR = "moderator";
EntityTile.POWER_STATUS_ADMIN = "admin";

View file

@ -20,7 +20,6 @@ limitations under the License.
import ReplyThread from "../elements/ReplyThread";
import React, {createRef} from 'react';
import PropTypes from 'prop-types';
import createReactClass from 'create-react-class';
import classNames from "classnames";
import { _t, _td } from '../../../languageHandler';
import * as TextForEvent from "../../../TextForEvent";
@ -127,10 +126,8 @@ const MAX_READ_AVATARS = 5;
// | '--------------------------------------' |
// '----------------------------------------------------------'
export default createReactClass({
displayName: 'EventTile',
propTypes: {
export default class EventTile extends React.Component {
static propTypes = {
/* the MatrixEvent to show */
mxEvent: PropTypes.object.isRequired,
@ -209,17 +206,19 @@ export default createReactClass({
// whether to use the irc layout
useIRCLayout: PropTypes.bool,
},
};
getDefaultProps: function() {
return {
// no-op function because onHeightChanged is optional yet some sub-components assume its existence
onHeightChanged: function() {},
};
},
static defaultProps = {
// no-op function because onHeightChanged is optional yet some sub-components assume its existence
onHeightChanged: function() {},
};
getInitialState: function() {
return {
static contextType = MatrixClientContext;
constructor(props) {
super(props);
this.state = {
// Whether the action bar is focused.
actionBarFocused: false,
// Whether all read receipts are being displayed. If not, only display
@ -232,23 +231,16 @@ export default createReactClass({
// The Relations model from the JS SDK for reactions to `mxEvent`
reactions: this.getReactions(),
};
},
statics: {
contextType: MatrixClientContext,
},
// TODO: [REACT-WARNING] Replace component with real class, use constructor for refs
UNSAFE_componentWillMount: function() {
// don't do RR animations until we are mounted
this._suppressReadReceiptAnimation = true;
this._verifyEvent(this.props.mxEvent);
this._tile = createRef();
this._replyThread = createRef();
},
}
componentDidMount: function() {
componentDidMount() {
this._suppressReadReceiptAnimation = false;
const client = this.context;
client.on("deviceVerificationChanged", this.onDeviceVerificationChanged);
@ -257,26 +249,26 @@ export default createReactClass({
if (this.props.showReactions) {
this.props.mxEvent.on("Event.relationsCreated", this._onReactionsCreated);
}
},
}
// TODO: [REACT-WARNING] Replace with appropriate lifecycle event
UNSAFE_componentWillReceiveProps: function(nextProps) {
UNSAFE_componentWillReceiveProps(nextProps) {
// re-check the sender verification as outgoing events progress through
// the send process.
if (nextProps.eventSendStatus !== this.props.eventSendStatus) {
this._verifyEvent(nextProps.mxEvent);
}
},
}
shouldComponentUpdate: function(nextProps, nextState) {
shouldComponentUpdate(nextProps, nextState) {
if (!ObjectUtils.shallowEqual(this.state, nextState)) {
return true;
}
return !this._propsEqual(this.props, nextProps);
},
}
componentWillUnmount: function() {
componentWillUnmount() {
const client = this.context;
client.removeListener("deviceVerificationChanged", this.onDeviceVerificationChanged);
client.removeListener("userTrustStatusChanged", this.onUserVerificationChanged);
@ -284,31 +276,31 @@ export default createReactClass({
if (this.props.showReactions) {
this.props.mxEvent.removeListener("Event.relationsCreated", this._onReactionsCreated);
}
},
}
/** called when the event is decrypted after we show it.
*/
_onDecrypted: function() {
_onDecrypted = () => {
// we need to re-verify the sending device.
// (we call onHeightChanged in _verifyEvent to handle the case where decryption
// has caused a change in size of the event tile)
this._verifyEvent(this.props.mxEvent);
this.forceUpdate();
},
};
onDeviceVerificationChanged: function(userId, device) {
onDeviceVerificationChanged = (userId, device) => {
if (userId === this.props.mxEvent.getSender()) {
this._verifyEvent(this.props.mxEvent);
}
},
};
onUserVerificationChanged: function(userId, _trustStatus) {
onUserVerificationChanged = (userId, _trustStatus) => {
if (userId === this.props.mxEvent.getSender()) {
this._verifyEvent(this.props.mxEvent);
}
},
};
_verifyEvent: async function(mxEvent) {
async _verifyEvent(mxEvent) {
if (!mxEvent.isEncrypted()) {
return;
}
@ -360,9 +352,9 @@ export default createReactClass({
this.setState({
verified: E2E_STATE.VERIFIED,
}, this.props.onHeightChanged); // Decryption may have caused a change in size
},
}
_propsEqual: function(objA, objB) {
_propsEqual(objA, objB) {
const keysA = Object.keys(objA);
const keysB = Object.keys(objB);
@ -408,9 +400,9 @@ export default createReactClass({
}
}
return true;
},
}
shouldHighlight: function() {
shouldHighlight() {
const actions = this.context.getPushActionsForEvent(this.props.mxEvent.replacingEvent() || this.props.mxEvent);
if (!actions || !actions.tweaks) { return false; }
@ -420,15 +412,15 @@ export default createReactClass({
}
return actions.tweaks.highlight;
},
}
toggleAllReadAvatars: function() {
toggleAllReadAvatars = () => {
this.setState({
allReadAvatars: !this.state.allReadAvatars,
});
},
};
getReadAvatars: function() {
getReadAvatars() {
// return early if there are no read receipts
if (!this.props.readReceipts || this.props.readReceipts.length === 0) {
return (<span className="mx_EventTile_readAvatars" />);
@ -494,17 +486,17 @@ export default createReactClass({
{ remText }
{ avatars }
</span>;
},
}
onSenderProfileClick: function(event) {
onSenderProfileClick = event => {
const mxEvent = this.props.mxEvent;
dis.dispatch({
action: 'insert_mention',
user_id: mxEvent.getSender(),
});
},
};
onRequestKeysClick: function() {
onRequestKeysClick = () => {
this.setState({
// Indicate in the UI that the keys have been requested (this is expected to
// be reset if the component is mounted in the future).
@ -515,9 +507,9 @@ export default createReactClass({
// is received for the request with the required keys, the event could be
// decrypted successfully.
this.context.cancelAndResendEventRoomKeyRequest(this.props.mxEvent);
},
};
onPermalinkClicked: function(e) {
onPermalinkClicked = e => {
// This allows the permalink to be opened in a new tab/window or copied as
// matrix.to, but also for it to enable routing within Element when clicked.
e.preventDefault();
@ -527,9 +519,9 @@ export default createReactClass({
highlighted: true,
room_id: this.props.mxEvent.getRoomId(),
});
},
};
_renderE2EPadlock: function() {
_renderE2EPadlock() {
const ev = this.props.mxEvent;
// event could not be decrypted
@ -570,23 +562,19 @@ export default createReactClass({
// no padlock needed
return null;
},
}
onActionBarFocusChange(focused) {
onActionBarFocusChange = focused => {
this.setState({
actionBarFocused: focused,
});
},
};
getTile() {
return this._tile.current;
},
getTile = () => this._tile.current;
getReplyThread() {
return this._replyThread.current;
},
getReplyThread = () => this._replyThread.current;
getReactions() {
getReactions = () => {
if (
!this.props.showReactions ||
!this.props.getRelationsForEvent
@ -602,9 +590,9 @@ export default createReactClass({
console.trace("Stacktrace for https://github.com/vector-im/element-web/issues/11120");
}
return this.props.getRelationsForEvent(eventId, "m.annotation", "m.reaction");
},
};
_onReactionsCreated(relationType, eventType) {
_onReactionsCreated = (relationType, eventType) => {
if (relationType !== "m.annotation" || eventType !== "m.reaction") {
return;
}
@ -612,9 +600,9 @@ export default createReactClass({
this.setState({
reactions: this.getReactions(),
});
},
};
render: function() {
render() {
const MessageTimestamp = sdk.getComponent('messages.MessageTimestamp');
const SenderProfile = sdk.getComponent('messages.SenderProfile');
const MemberAvatar = sdk.getComponent('avatars.MemberAvatar');
@ -947,8 +935,8 @@ export default createReactClass({
);
}
}
},
});
}
}
// XXX this'll eventually be dynamic based on the fields once we have extensible event types
const messageTypes = ['m.room.message', 'm.sticker'];

View file

@ -17,49 +17,46 @@
import React from 'react';
import PropTypes from 'prop-types';
import createReactClass from 'create-react-class';
import { _t } from '../../../languageHandler';
import dis from '../../../dispatcher/dispatcher';
import {Key} from '../../../Keyboard';
export default createReactClass({
displayName: 'ForwardMessage',
propTypes: {
export default class ForwardMessage extends React.Component {
static propTypes = {
onCancelClick: PropTypes.func.isRequired,
},
};
componentDidMount: function() {
componentDidMount() {
dis.dispatch({
action: 'panel_disable',
middleDisabled: true,
});
document.addEventListener('keydown', this._onKeyDown);
},
}
componentWillUnmount: function() {
componentWillUnmount() {
dis.dispatch({
action: 'panel_disable',
middleDisabled: false,
});
document.removeEventListener('keydown', this._onKeyDown);
},
}
_onKeyDown: function(ev) {
_onKeyDown = ev => {
switch (ev.key) {
case Key.ESCAPE:
this.props.onCancelClick();
break;
}
},
};
render: function() {
render() {
return (
<div className="mx_ForwardMessage">
<h1>{ _t('Please select the destination room for this message') }</h1>
</div>
);
},
});
}
}

View file

@ -17,7 +17,6 @@ limitations under the License.
import React, {createRef} from 'react';
import PropTypes from 'prop-types';
import createReactClass from 'create-react-class';
import { AllHtmlEntities } from 'html-entities';
import {linkifyElement} from '../../../HtmlUtils';
import SettingsStore from "../../../settings/SettingsStore";
@ -27,24 +26,21 @@ import Modal from "../../../Modal";
import * as ImageUtils from "../../../ImageUtils";
import { _t } from "../../../languageHandler";
export default createReactClass({
displayName: 'LinkPreviewWidget',
propTypes: {
export default class LinkPreviewWidget extends React.Component {
static propTypes = {
link: PropTypes.string.isRequired, // the URL being previewed
mxEvent: PropTypes.object.isRequired, // the Event associated with the preview
onCancelClick: PropTypes.func, // called when the preview's cancel ('hide') button is clicked
onHeightChanged: PropTypes.func, // called when the preview's contents has loaded
},
};
getInitialState: function() {
return {
constructor(props) {
super(props);
this.state = {
preview: null,
};
},
// TODO: [REACT-WARNING] Replace component with real class, use constructor for refs
UNSAFE_componentWillMount: function() {
this.unmounted = false;
MatrixClientPeg.get().getUrlPreview(this.props.link, this.props.mxEvent.getTs()).then((res)=>{
if (this.unmounted) {
@ -59,25 +55,25 @@ export default createReactClass({
});
this._description = createRef();
},
}
componentDidMount: function() {
componentDidMount() {
if (this._description.current) {
linkifyElement(this._description.current);
}
},
}
componentDidUpdate: function() {
componentDidUpdate() {
if (this._description.current) {
linkifyElement(this._description.current);
}
},
}
componentWillUnmount: function() {
componentWillUnmount() {
this.unmounted = true;
},
}
onImageClick: function(ev) {
onImageClick = ev => {
const p = this.state.preview;
if (ev.button != 0 || ev.metaKey) return;
ev.preventDefault();
@ -98,9 +94,9 @@ export default createReactClass({
};
Modal.createDialog(ImageView, params, "mx_Dialog_lightbox");
},
};
render: function() {
render() {
const p = this.state.preview;
if (!p || Object.keys(p).length === 0) {
return <div />;
@ -149,5 +145,5 @@ export default createReactClass({
</AccessibleButton>
</div>
);
},
});
}
}

View file

@ -17,7 +17,6 @@ limitations under the License.
*/
import React from 'react';
import createReactClass from 'create-react-class';
import { _t } from '../../../languageHandler';
import SdkConfig from '../../../SdkConfig';
import dis from '../../../dispatcher/dispatcher';
@ -36,23 +35,19 @@ const SHOW_MORE_INCREMENT = 100;
// matches all ASCII punctuation: !"#$%&'()*+,-./:;<=>?@[\]^_`{|}~
const SORT_REGEX = /[\x21-\x2F\x3A-\x40\x5B-\x60\x7B-\x7E]+/g;
export default createReactClass({
displayName: 'MemberList',
export default class MemberList extends React.Component {
constructor(props) {
super(props);
getInitialState: function() {
const cli = MatrixClientPeg.get();
if (cli.hasLazyLoadMembersEnabled()) {
// show an empty list
return this._getMembersState([]);
this.state = this._getMembersState([]);
} else {
return this._getMembersState(this.roomMembers());
this.state = this._getMembersState(this.roomMembers());
}
},
// TODO: [REACT-WARNING] Move this to constructor
UNSAFE_componentWillMount: function() {
this._mounted = true;
const cli = MatrixClientPeg.get();
if (cli.hasLazyLoadMembersEnabled()) {
this._showMembersAccordingToMembershipWithLL();
cli.on("Room.myMembership", this.onMyMembership);
@ -66,9 +61,9 @@ export default createReactClass({
if (enablePresenceByHsUrl && enablePresenceByHsUrl[hsUrl] !== undefined) {
this._showPresence = enablePresenceByHsUrl[hsUrl];
}
},
}
_listenForMembersChanges: function() {
_listenForMembersChanges() {
const cli = MatrixClientPeg.get();
cli.on("RoomState.members", this.onRoomStateMember);
cli.on("RoomMember.name", this.onRoomMemberName);
@ -80,9 +75,9 @@ export default createReactClass({
cli.on("User.presence", this.onUserPresenceChange);
cli.on("User.currentlyActive", this.onUserPresenceChange);
// cli.on("Room.timeline", this.onRoomTimeline);
},
}
componentWillUnmount: function() {
componentWillUnmount() {
this._mounted = false;
const cli = MatrixClientPeg.get();
if (cli) {
@ -98,14 +93,14 @@ export default createReactClass({
// cancel any pending calls to the rate_limited_funcs
this._updateList.cancelPendingCall();
},
}
/**
* If lazy loading is enabled, either:
* show a spinner and load the members if the user is joined,
* or show the members available so far if the user is invited
*/
_showMembersAccordingToMembershipWithLL: async function() {
async _showMembersAccordingToMembershipWithLL() {
const cli = MatrixClientPeg.get();
if (cli.hasLazyLoadMembersEnabled()) {
const cli = MatrixClientPeg.get();
@ -125,9 +120,9 @@ export default createReactClass({
this.setState(this._getMembersState(this.roomMembers()));
}
}
},
}
_getMembersState: function(members) {
_getMembersState(members) {
// set the state after determining _showPresence to make sure it's
// taken into account while rerendering
return {
@ -142,9 +137,9 @@ export default createReactClass({
truncateAtInvited: INITIAL_LOAD_NUM_INVITED,
searchQuery: "",
};
},
}
onUserPresenceChange(event, user) {
onUserPresenceChange = (event, user) => {
// Attach a SINGLE listener for global presence changes then locate the
// member tile and re-render it. This is more efficient than every tile
// ever attaching their own listener.
@ -153,9 +148,9 @@ export default createReactClass({
if (tile) {
this._updateList(); // reorder the membership list
}
},
};
onRoom: function(room) {
onRoom = room => {
if (room.roomId !== this.props.roomId) {
return;
}
@ -163,40 +158,40 @@ export default createReactClass({
// we need to wait till the room is fully populated with state
// before refreshing the member list else we get a stale list.
this._showMembersAccordingToMembershipWithLL();
},
};
onMyMembership: function(room, membership, oldMembership) {
onMyMembership = (room, membership, oldMembership) => {
if (room.roomId === this.props.roomId && membership === "join") {
this._showMembersAccordingToMembershipWithLL();
}
},
};
onRoomStateMember: function(ev, state, member) {
onRoomStateMember = (ev, state, member) => {
if (member.roomId !== this.props.roomId) {
return;
}
this._updateList();
},
};
onRoomMemberName: function(ev, member) {
onRoomMemberName = (ev, member) => {
if (member.roomId !== this.props.roomId) {
return;
}
this._updateList();
},
};
onRoomStateEvent: function(event, state) {
onRoomStateEvent = (event, state) => {
if (event.getRoomId() === this.props.roomId &&
event.getType() === "m.room.third_party_invite") {
this._updateList();
}
},
};
_updateList: rate_limited_func(function() {
_updateList = rate_limited_func(() => {
this._updateListNow();
}, 500),
}, 500);
_updateListNow: function() {
_updateListNow() {
// console.log("Updating memberlist");
const newState = {
loading: false,
@ -205,9 +200,9 @@ export default createReactClass({
newState.filteredJoinedMembers = this._filterMembers(newState.members, 'join', this.state.searchQuery);
newState.filteredInvitedMembers = this._filterMembers(newState.members, 'invite', this.state.searchQuery);
this.setState(newState);
},
}
getMembersWithUser: function() {
getMembersWithUser() {
if (!this.props.roomId) return [];
const cli = MatrixClientPeg.get();
const room = cli.getRoom(this.props.roomId);
@ -228,9 +223,9 @@ export default createReactClass({
});
return allMembers;
},
}
roomMembers: function() {
roomMembers() {
const ConferenceHandler = CallHandler.getConferenceHandler();
const allMembers = this.getMembersWithUser();
@ -244,17 +239,17 @@ export default createReactClass({
});
filteredAndSortedMembers.sort(this.memberSort);
return filteredAndSortedMembers;
},
}
_createOverflowTileJoined: function(overflowCount, totalCount) {
_createOverflowTileJoined(overflowCount, totalCount) {
return this._createOverflowTile(overflowCount, totalCount, this._showMoreJoinedMemberList);
},
}
_createOverflowTileInvited: function(overflowCount, totalCount) {
_createOverflowTileInvited(overflowCount, totalCount) {
return this._createOverflowTile(overflowCount, totalCount, this._showMoreInvitedMemberList);
},
}
_createOverflowTile: function(overflowCount, totalCount, onClick) {
_createOverflowTile(overflowCount, totalCount, onClick) {
// For now we'll pretend this is any entity. It should probably be a separate tile.
const EntityTile = sdk.getComponent("rooms.EntityTile");
const BaseAvatar = sdk.getComponent("avatars.BaseAvatar");
@ -265,33 +260,33 @@ export default createReactClass({
} name={text} presenceState="online" suppressOnHover={true}
onClick={onClick} />
);
},
}
_showMoreJoinedMemberList: function() {
_showMoreJoinedMemberList = () => {
this.setState({
truncateAtJoined: this.state.truncateAtJoined + SHOW_MORE_INCREMENT,
});
},
};
_showMoreInvitedMemberList: function() {
_showMoreInvitedMemberList = () => {
this.setState({
truncateAtInvited: this.state.truncateAtInvited + SHOW_MORE_INCREMENT,
});
},
};
memberString: function(member) {
memberString(member) {
if (!member) {
return "(null)";
} else {
const u = member.user;
return "(" + member.name + ", " + member.powerLevel + ", " + (u ? u.lastActiveAgo : "<null>") + ", " + (u ? u.getLastActiveTs() : "<null>") + ", " + (u ? u.currentlyActive : "<null>") + ", " + (u ? u.presence : "<null>") + ")";
}
},
}
// returns negative if a comes before b,
// returns 0 if a and b are equivalent in ordering
// returns positive if a comes after b.
memberSort: function(memberA, memberB) {
memberSort = (memberA, memberB) => {
// order by presence, with "active now" first.
// ...and then by power level
// ...and then by last active
@ -348,24 +343,24 @@ export default createReactClass({
ignorePunctuation: true,
sensitivity: "base",
});
},
};
onSearchQueryChanged: function(searchQuery) {
onSearchQueryChanged = searchQuery => {
this.setState({
searchQuery,
filteredJoinedMembers: this._filterMembers(this.state.members, 'join', searchQuery),
filteredInvitedMembers: this._filterMembers(this.state.members, 'invite', searchQuery),
});
},
};
_onPending3pidInviteClick: function(inviteEvent) {
_onPending3pidInviteClick = inviteEvent => {
dis.dispatch({
action: 'view_3pid_invite',
event: inviteEvent,
});
},
};
_filterMembers: function(members, membership, query) {
_filterMembers(members, membership, query) {
return members.filter((m) => {
if (query) {
query = query.toLowerCase();
@ -379,9 +374,9 @@ export default createReactClass({
return m.membership === membership;
});
},
}
_getPending3PidInvites: function() {
_getPending3PidInvites() {
// include 3pid invites (m.room.third_party_invite) state events.
// The HS may have already converted these into m.room.member invites so
// we shouldn't add them if the 3pid invite state key (token) is in the
@ -399,9 +394,9 @@ export default createReactClass({
return true;
});
}
},
}
_makeMemberTiles: function(members) {
_makeMemberTiles(members) {
const MemberTile = sdk.getComponent("rooms.MemberTile");
const EntityTile = sdk.getComponent("rooms.EntityTile");
@ -415,30 +410,30 @@ export default createReactClass({
onClick={() => this._onPending3pidInviteClick(m)} />;
}
});
},
}
_getChildrenJoined: function(start, end) {
_getChildrenJoined(start, end) {
return this._makeMemberTiles(this.state.filteredJoinedMembers.slice(start, end));
},
}
_getChildCountJoined: function() {
_getChildCountJoined() {
return this.state.filteredJoinedMembers.length;
},
}
_getChildrenInvited: function(start, end) {
_getChildrenInvited(start, end) {
let targets = this.state.filteredInvitedMembers;
if (end > this.state.filteredInvitedMembers.length) {
targets = targets.concat(this._getPending3PidInvites());
}
return this._makeMemberTiles(targets.slice(start, end));
},
}
_getChildCountInvited: function() {
_getChildCountInvited() {
return this.state.filteredInvitedMembers.length + (this._getPending3PidInvites() || []).length;
},
}
render: function() {
render() {
if (this.state.loading) {
const Spinner = sdk.getComponent("elements.Spinner");
return <div className="mx_MemberList"><Spinner /></div>;
@ -501,9 +496,9 @@ export default createReactClass({
onSearch={ this.onSearchQueryChanged } />
</div>
);
},
}
onInviteButtonClick: function() {
onInviteButtonClick = () => {
if (MatrixClientPeg.get().isGuest()) {
dis.dispatch({action: 'require_registration'});
return;
@ -514,5 +509,5 @@ export default createReactClass({
action: 'view_invite',
roomId: this.props.roomId,
});
},
});
};
}

View file

@ -18,34 +18,31 @@ limitations under the License.
import SettingsStore from "../../../settings/SettingsStore";
import React from 'react';
import PropTypes from 'prop-types';
import createReactClass from 'create-react-class';
import * as sdk from "../../../index";
import dis from "../../../dispatcher/dispatcher";
import { _t } from '../../../languageHandler';
import { MatrixClientPeg } from "../../../MatrixClientPeg";
import {Action} from "../../../dispatcher/actions";
export default createReactClass({
displayName: 'MemberTile',
propTypes: {
export default class MemberTile extends React.Component {
static propTypes = {
member: PropTypes.any.isRequired, // RoomMember
showPresence: PropTypes.bool,
},
};
getDefaultProps: function() {
return {
showPresence: true,
};
},
static defaultProps = {
showPresence: true,
};
getInitialState: function() {
return {
constructor(props) {
super(props);
this.state = {
statusMessage: this.getStatusMessage(),
isRoomEncrypted: false,
e2eStatus: null,
};
},
}
componentDidMount() {
const cli = MatrixClientPeg.get();
@ -72,7 +69,7 @@ export default createReactClass({
cli.on("RoomState.events", this.onRoomStateEvents);
}
}
},
}
componentWillUnmount() {
const cli = MatrixClientPeg.get();
@ -90,9 +87,9 @@ export default createReactClass({
cli.removeListener("userTrustStatusChanged", this.onUserTrustStatusChanged);
cli.removeListener("deviceVerificationChanged", this.onDeviceVerificationChanged);
}
},
}
onRoomStateEvents: function(ev) {
onRoomStateEvents = ev => {
if (ev.getType() !== "m.room.encryption") return;
const { roomId } = this.props.member;
if (ev.getRoomId() !== roomId) return;
@ -104,19 +101,19 @@ export default createReactClass({
isRoomEncrypted: true,
});
this.updateE2EStatus();
},
};
onUserTrustStatusChanged: function(userId, trustStatus) {
onUserTrustStatusChanged = (userId, trustStatus) => {
if (userId !== this.props.member.userId) return;
this.updateE2EStatus();
},
};
onDeviceVerificationChanged: function(userId, deviceId, deviceInfo) {
onDeviceVerificationChanged = (userId, deviceId, deviceInfo) => {
if (userId !== this.props.member.userId) return;
this.updateE2EStatus();
},
};
updateE2EStatus: async function() {
async updateE2EStatus() {
const cli = MatrixClientPeg.get();
const { userId } = this.props.member;
const isMe = userId === cli.getUserId();
@ -142,7 +139,7 @@ export default createReactClass({
this.setState({
e2eStatus: anyDeviceUnverified ? "warning" : "verified",
});
},
}
getStatusMessage() {
const { user } = this.props.member;
@ -150,16 +147,16 @@ export default createReactClass({
return "";
}
return user._unstable_statusMessage;
},
}
_onStatusMessageCommitted() {
_onStatusMessageCommitted = () => {
// The `User` object has observed a status message change.
this.setState({
statusMessage: this.getStatusMessage(),
});
},
};
shouldComponentUpdate: function(nextProps, nextState) {
shouldComponentUpdate(nextProps, nextState) {
if (
this.member_last_modified_time === undefined ||
this.member_last_modified_time < nextProps.member.getLastModifiedTime()
@ -180,27 +177,27 @@ export default createReactClass({
return true;
}
return false;
},
}
onClick: function(e) {
onClick = e => {
dis.dispatch({
action: Action.ViewUser,
member: this.props.member,
});
},
};
_getDisplayName: function() {
_getDisplayName() {
return this.props.member.name;
},
}
getPowerLabel: function() {
getPowerLabel() {
return _t("%(userName)s (power %(powerLevelNumber)s)", {
userName: this.props.member.userId,
powerLevelNumber: this.props.member.powerLevel,
});
},
}
render: function() {
render() {
const MemberAvatar = sdk.getComponent('avatars.MemberAvatar');
const EntityTile = sdk.getComponent('rooms.EntityTile');
@ -260,5 +257,5 @@ export default createReactClass({
onClick={this.onClick}
/>
);
},
});
}
}

View file

@ -16,7 +16,6 @@ limitations under the License.
import React from "react";
import PropTypes from 'prop-types';
import createReactClass from 'create-react-class';
import {MatrixClientPeg} from "../../../MatrixClientPeg";
import dis from "../../../dispatcher/dispatcher";
import AccessibleButton from "../elements/AccessibleButton";
@ -25,22 +24,23 @@ import MemberAvatar from "../avatars/MemberAvatar";
import { _t } from '../../../languageHandler';
import {formatFullDate} from '../../../DateUtils';
export default createReactClass({
displayName: 'PinnedEventTile',
propTypes: {
export default class PinnedEventTile extends React.Component {
static propTypes = {
mxRoom: PropTypes.object.isRequired,
mxEvent: PropTypes.object.isRequired,
onUnpinned: PropTypes.func,
},
onTileClicked: function() {
};
onTileClicked = () => {
dis.dispatch({
action: 'view_room',
event_id: this.props.mxEvent.getId(),
highlighted: true,
room_id: this.props.mxEvent.getRoomId(),
});
},
onUnpinClicked: function() {
};
onUnpinClicked = () => {
const pinnedEvents = this.props.mxRoom.currentState.getStateEvents("m.room.pinned_events", "");
if (!pinnedEvents || !pinnedEvents.getContent().pinned) {
// Nothing to do: already unpinned
@ -56,11 +56,13 @@ export default createReactClass({
});
} else if (this.props.onUnpinned) this.props.onUnpinned();
}
},
_canUnpin: function() {
};
_canUnpin() {
return this.props.mxRoom.currentState.mayClientSendStateEvent('m.room.pinned_events', MatrixClientPeg.get());
},
render: function() {
}
render() {
const sender = this.props.mxEvent.getSender();
// Get the latest sender profile rather than historical
const senderProfile = this.props.mxRoom.getMember(sender);
@ -100,5 +102,5 @@ export default createReactClass({
</div>
</div>
);
},
});
}
}

View file

@ -17,46 +17,42 @@ limitations under the License.
import React from "react";
import PropTypes from 'prop-types';
import createReactClass from 'create-react-class';
import {MatrixClientPeg} from "../../../MatrixClientPeg";
import AccessibleButton from "../elements/AccessibleButton";
import PinnedEventTile from "./PinnedEventTile";
import { _t } from '../../../languageHandler';
import PinningUtils from "../../../utils/PinningUtils";
export default createReactClass({
displayName: 'PinnedEventsPanel',
propTypes: {
export default class PinnedEventsPanel extends React.Component {
static propTypes = {
// The Room from the js-sdk we're going to show pinned events for
room: PropTypes.object.isRequired,
onCancelClick: PropTypes.func,
},
};
getInitialState: function() {
return {
loading: true,
};
},
state = {
loading: true,
};
componentDidMount: function() {
componentDidMount() {
this._updatePinnedMessages();
MatrixClientPeg.get().on("RoomState.events", this._onStateEvent);
},
}
componentWillUnmount: function() {
componentWillUnmount() {
if (MatrixClientPeg.get()) {
MatrixClientPeg.get().removeListener("RoomState.events", this._onStateEvent);
}
},
}
_onStateEvent: function(ev) {
_onStateEvent = ev => {
if (ev.getRoomId() === this.props.room.roomId && ev.getType() === "m.room.pinned_events") {
this._updatePinnedMessages();
}
},
};
_updatePinnedMessages: function() {
_updatePinnedMessages = () => {
const pinnedEvents = this.props.room.currentState.getStateEvents("m.room.pinned_events", "");
if (!pinnedEvents || !pinnedEvents.getContent().pinned) {
this.setState({ loading: false, pinned: [] });
@ -85,9 +81,9 @@ export default createReactClass({
}
this._updateReadState();
},
};
_updateReadState: function() {
_updateReadState() {
const pinnedEvents = this.props.room.currentState.getStateEvents("m.room.pinned_events", "");
if (!pinnedEvents) return; // nothing to read
@ -107,9 +103,9 @@ export default createReactClass({
event_ids: readStateEvents,
});
}
},
}
_getPinnedTiles: function() {
_getPinnedTiles() {
if (this.state.pinned.length === 0) {
return (<div>{ _t("No pinned messages.") }</div>);
}
@ -120,9 +116,9 @@ export default createReactClass({
mxEvent={context.event}
onUnpinned={this._updatePinnedMessages} />);
});
},
}
render: function() {
render() {
let tiles = <div>{ _t("Loading...") }</div>;
if (this.state && !this.state.loading) {
tiles = this._getPinnedTiles();
@ -139,5 +135,5 @@ export default createReactClass({
</div>
</div>
);
},
});
}
}

View file

@ -16,15 +16,12 @@ limitations under the License.
import React from 'react';
import PropTypes from 'prop-types';
import createReactClass from 'create-react-class';
import { _t } from '../../../languageHandler';
export default createReactClass({
displayName: 'PresenceLabel',
propTypes: {
export default class PresenceLabel extends React.Component {
static propTypes = {
// number of milliseconds ago this user was last active.
// zero = unknown
activeAgo: PropTypes.number,
@ -35,18 +32,16 @@ export default createReactClass({
// offline, online, etc
presenceState: PropTypes.string,
},
};
getDefaultProps: function() {
return {
ago: -1,
presenceState: null,
};
},
static defaultProps = {
activeAgo: -1,
presenceState: null,
};
// Return duration as a string using appropriate time units
// XXX: This would be better handled using a culture-aware library, but we don't use one yet.
getDuration: function(time) {
getDuration(time) {
if (!time) return;
const t = parseInt(time / 1000);
const s = t % 60;
@ -66,9 +61,9 @@ export default createReactClass({
return _t("%(duration)sh", {duration: h});
}
return _t("%(duration)sd", {duration: d});
},
}
getPrettyPresence: function(presence, activeAgo, currentlyActive) {
getPrettyPresence(presence, activeAgo, currentlyActive) {
if (!currentlyActive && activeAgo !== undefined && activeAgo > 0) {
const duration = this.getDuration(activeAgo);
if (presence === "online") return _t("Online for %(duration)s", { duration: duration });
@ -81,13 +76,13 @@ export default createReactClass({
if (presence === "offline") return _t("Offline");
return _t("Unknown");
}
},
}
render: function() {
render() {
return (
<div className="mx_PresenceLabel">
{ this.getPrettyPresence(this.props.presenceState, this.props.activeAgo, this.props.currentlyActive) }
</div>
);
},
});
}
}

View file

@ -17,7 +17,6 @@ limitations under the License.
import React, {createRef} from 'react';
import PropTypes from 'prop-types';
import createReactClass from 'create-react-class';
import '../../../VelocityBounce';
import { _t } from '../../../languageHandler';
import {formatDate} from '../../../DateUtils';
@ -33,10 +32,8 @@ try {
} catch (e) {
}
export default createReactClass({
displayName: 'ReadReceiptMarker',
propTypes: {
export default class ReadReceiptMarker extends React.Component {
static propTypes = {
// the RoomMember to show the RR for
member: PropTypes.object,
// userId to fallback the avatar to
@ -70,30 +67,27 @@ export default createReactClass({
// True to show twelve hour format, false otherwise
showTwelveHour: PropTypes.bool,
},
};
getDefaultProps: function() {
return {
leftOffset: 0,
};
},
static defaultProps = {
leftOffset: 0,
};
getInitialState: function() {
// if we are going to animate the RR, we don't show it on first render,
// and instead just add a placeholder to the DOM; once we've been
// mounted, we start an animation which moves the RR from its old
// position.
return {
constructor(props) {
super(props);
this._avatar = createRef();
this.state = {
// if we are going to animate the RR, we don't show it on first render,
// and instead just add a placeholder to the DOM; once we've been
// mounted, we start an animation which moves the RR from its old
// position.
suppressDisplay: !this.props.suppressAnimation,
};
},
}
// TODO: [REACT-WARNING] Replace component with real class, use constructor for refs
UNSAFE_componentWillMount: function() {
this._avatar = createRef();
},
componentWillUnmount: function() {
componentWillUnmount() {
// before we remove the rr, store its location in the map, so that if
// it reappears, it can be animated from the right place.
const rrInfo = this.props.readReceiptInfo;
@ -112,9 +106,9 @@ export default createReactClass({
rrInfo.top = avatarNode.offsetTop;
rrInfo.left = avatarNode.offsetLeft;
rrInfo.parent = avatarNode.offsetParent;
},
}
componentDidMount: function() {
componentDidMount() {
if (!this.state.suppressDisplay) {
// we've already done our display - nothing more to do.
return;
@ -172,10 +166,9 @@ export default createReactClass({
startStyles: startStyles,
enterTransitionOpts: enterTransitionOpts,
});
},
}
render: function() {
render() {
const MemberAvatar = sdk.getComponent('avatars.MemberAvatar');
if (this.state.suppressDisplay) {
return <div ref={this._avatar} />;
@ -222,5 +215,5 @@ export default createReactClass({
/>
</Velociraptor>
);
},
});
}
}

View file

@ -19,35 +19,32 @@ import dis from '../../../dispatcher/dispatcher';
import React from 'react';
import { _t } from '../../../languageHandler';
import PropTypes from 'prop-types';
import createReactClass from 'create-react-class';
import classNames from 'classnames';
import {roomShape} from './RoomDetailRow';
export default createReactClass({
displayName: 'RoomDetailList',
propTypes: {
export default class RoomDetailList extends React.Component {
static propTypes = {
rooms: PropTypes.arrayOf(roomShape),
className: PropTypes.string,
},
};
getRows: function() {
getRows() {
if (!this.props.rooms) return [];
const RoomDetailRow = sdk.getComponent('rooms.RoomDetailRow');
return this.props.rooms.map((room, index) => {
return <RoomDetailRow key={index} room={room} onClick={this.onDetailsClick} />;
});
},
}
onDetailsClick: function(ev, room) {
onDetailsClick = (ev, room) => {
dis.dispatch({
action: 'view_room',
room_id: room.roomId,
room_alias: room.canonicalAlias || (room.aliases || [])[0],
});
},
};
render() {
const rows = this.getRows();
@ -64,5 +61,5 @@ export default createReactClass({
return <div className={classNames("mx_RoomDetailList", this.props.className)}>
{ rooms }
</div>;
},
});
}
}

View file

@ -20,7 +20,6 @@ import { _t } from '../../../languageHandler';
import { linkifyElement } from '../../../HtmlUtils';
import {MatrixClientPeg} from '../../../MatrixClientPeg';
import PropTypes from 'prop-types';
import createReactClass from 'create-react-class';
import {getHttpUriForMxc} from "matrix-js-sdk/src/content-repo";
export function getDisplayAliasForRoom(room) {
@ -40,47 +39,48 @@ export const roomShape = PropTypes.shape({
guestCanJoin: PropTypes.bool,
});
export default createReactClass({
propTypes: {
export default class RoomDetailRow extends React.Component {
static propTypes = {
room: roomShape,
// passes ev, room as args
onClick: PropTypes.func,
onMouseDown: PropTypes.func,
},
};
_linkifyTopic: function() {
constructor(props) {
super(props);
this._topic = createRef();
}
componentDidMount() {
this._linkifyTopic();
}
componentDidUpdate() {
this._linkifyTopic();
}
_linkifyTopic() {
if (this._topic.current) {
linkifyElement(this._topic.current);
}
},
}
// TODO: [REACT-WARNING] Replace component with real class, use constructor for refs
UNSAFE_componentWillMount: function() {
this._topic = createRef();
},
componentDidMount: function() {
this._linkifyTopic();
},
componentDidUpdate: function() {
this._linkifyTopic();
},
onClick: function(ev) {
onClick = (ev) => {
ev.preventDefault();
if (this.props.onClick) {
this.props.onClick(ev, this.props.room);
}
},
};
onTopicClick: function(ev) {
onTopicClick = (ev) => {
// When clicking a link in the topic, prevent the event being propagated
// to `onClick`.
ev.stopPropagation();
},
};
render: function() {
render() {
const BaseAvatar = sdk.getComponent('avatars.BaseAvatar');
const room = this.props.room;
@ -118,5 +118,5 @@ export default createReactClass({
{ room.numJoinedMembers }
</td>
</tr>;
},
});
}
}

View file

@ -17,7 +17,6 @@ limitations under the License.
import React, {createRef} from 'react';
import PropTypes from 'prop-types';
import createReactClass from 'create-react-class';
import classNames from 'classnames';
import * as sdk from '../../../index';
import { _t } from '../../../languageHandler';
@ -35,10 +34,8 @@ import DecoratedRoomAvatar from "../avatars/DecoratedRoomAvatar";
import {DefaultTagID} from "../../../stores/room-list/models";
import AccessibleTooltipButton from "../elements/AccessibleTooltipButton";
export default createReactClass({
displayName: 'RoomHeader',
propTypes: {
export default class RoomHeader extends React.Component {
static propTypes = {
room: PropTypes.object,
oobData: PropTypes.object,
inRoom: PropTypes.bool,
@ -48,22 +45,21 @@ export default createReactClass({
onLeaveClick: PropTypes.func,
onCancelClick: PropTypes.func,
e2eStatus: PropTypes.string,
},
};
getDefaultProps: function() {
return {
editing: false,
inRoom: false,
onCancelClick: null,
};
},
static defaultProps = {
editing: false,
inRoom: false,
onCancelClick: null,
};
constructor(props) {
super(props);
// TODO: [REACT-WARNING] Replace component with real class, use constructor for refs
UNSAFE_componentWillMount: function() {
this._topic = createRef();
},
}
componentDidMount: function() {
componentDidMount() {
const cli = MatrixClientPeg.get();
cli.on("RoomState.events", this._onRoomStateEvents);
cli.on("Room.accountData", this._onRoomAccountData);
@ -74,15 +70,15 @@ export default createReactClass({
if (this.props.room) {
this.props.room.on("Room.name", this._onRoomNameChange);
}
},
}
componentDidUpdate: function() {
componentDidUpdate() {
if (this._topic.current) {
linkifyElement(this._topic.current);
}
},
}
componentWillUnmount: function() {
componentWillUnmount() {
if (this.props.room) {
this.props.room.removeListener("Room.name", this._onRoomNameChange);
}
@ -91,41 +87,41 @@ export default createReactClass({
cli.removeListener("RoomState.events", this._onRoomStateEvents);
cli.removeListener("Room.accountData", this._onRoomAccountData);
}
},
}
_onRoomStateEvents: function(event, state) {
_onRoomStateEvents = (event, state) => {
if (!this.props.room || event.getRoomId() !== this.props.room.roomId) {
return;
}
// redisplay the room name, topic, etc.
this._rateLimitedUpdate();
},
};
_onRoomAccountData: function(event, room) {
_onRoomAccountData = (event, room) => {
if (!this.props.room || room.roomId !== this.props.room.roomId) return;
if (event.getType() !== "im.vector.room.read_pins") return;
this._rateLimitedUpdate();
},
};
_rateLimitedUpdate: new RateLimitedFunc(function() {
_rateLimitedUpdate = new RateLimitedFunc(function() {
/* eslint-disable babel/no-invalid-this */
this.forceUpdate();
}, 500),
}, 500);
_onRoomNameChange: function(room) {
_onRoomNameChange = (room) => {
this.forceUpdate();
},
};
onShareRoomClick: function(ev) {
onShareRoomClick = (ev) => {
const ShareDialog = sdk.getComponent("dialogs.ShareDialog");
Modal.createTrackedDialog('share room dialog', '', ShareDialog, {
target: this.props.room,
});
},
};
_hasUnreadPins: function() {
_hasUnreadPins() {
const currentPinEvent = this.props.room.currentState.getStateEvents("m.room.pinned_events", '');
if (!currentPinEvent) return false;
if (currentPinEvent.getContent().pinned && currentPinEvent.getContent().pinned.length <= 0) {
@ -142,16 +138,16 @@ export default createReactClass({
// There's pins, and we haven't read any of them
return true;
},
}
_hasPins: function() {
_hasPins() {
const currentPinEvent = this.props.room.currentState.getStateEvents("m.room.pinned_events", '');
if (!currentPinEvent) return false;
return !(currentPinEvent.getContent().pinned && currentPinEvent.getContent().pinned.length <= 0);
},
}
render: function() {
render() {
let searchStatus = null;
let cancelButton = null;
let settingsButton = null;
@ -301,5 +297,5 @@ export default createReactClass({
</div>
</div>
);
},
});
}
}

View file

@ -18,7 +18,6 @@ limitations under the License.
import React from 'react';
import PropTypes from 'prop-types';
import createReactClass from 'create-react-class';
import * as sdk from '../../../index';
import {MatrixClientPeg} from '../../../MatrixClientPeg';
import dis from '../../../dispatcher/dispatcher';
@ -46,10 +45,8 @@ const MessageCase = Object.freeze({
OtherError: "OtherError",
});
export default createReactClass({
displayName: 'RoomPreviewBar',
propTypes: {
export default class RoomPreviewBar extends React.Component {
static propTypes = {
onJoinClick: PropTypes.func,
onRejectClick: PropTypes.func,
onRejectAndIgnoreClick: PropTypes.func,
@ -86,36 +83,32 @@ export default createReactClass({
// If given, this will be how the room is referred to (eg.
// in error messages).
roomAlias: PropTypes.string,
},
};
getDefaultProps: function() {
return {
onJoinClick: function() {},
};
},
static defaultProps = {
onJoinClick() {},
};
getInitialState: function() {
return {
busy: false,
};
},
state = {
busy: false,
};
componentDidMount: function() {
componentDidMount() {
this._checkInvitedEmail();
CommunityPrototypeStore.instance.on(UPDATE_EVENT, this._onCommunityUpdate);
},
}
componentDidUpdate: function(prevProps, prevState) {
componentDidUpdate(prevProps, prevState) {
if (this.props.invitedEmail !== prevProps.invitedEmail || this.props.inviterName !== prevProps.inviterName) {
this._checkInvitedEmail();
}
},
}
componentWillUnmount: function() {
componentWillUnmount() {
CommunityPrototypeStore.instance.off(UPDATE_EVENT, this._onCommunityUpdate);
},
}
_checkInvitedEmail: async function() {
async _checkInvitedEmail() {
// If this is an invite and we've been told what email address was
// invited, fetch the user's account emails and discovery bindings so we
// can check them against the email that was invited.
@ -148,14 +141,14 @@ export default createReactClass({
}
this.setState({busy: false});
}
},
}
_onCommunityUpdate: function (roomId) {
_onCommunityUpdate = (roomId) => {
if (this.props.room && this.props.room.roomId !== roomId) {
return;
}
this.forceUpdate(); // we have nothing to update
},
};
_getMessageCase() {
const isGuest = MatrixClientPeg.get().isGuest();
@ -207,7 +200,7 @@ export default createReactClass({
} else {
return MessageCase.ViewingRoom;
}
},
}
_getKickOrBanInfo() {
const myMember = this._getMyMember();
@ -221,9 +214,9 @@ export default createReactClass({
kickerMember.name : myMember.events.member.getSender();
const reason = myMember.events.member.getContent().reason;
return {memberName, reason};
},
}
_joinRule: function() {
_joinRule() {
const room = this.props.room;
if (room) {
const joinRules = room.currentState.getStateEvents('m.room.join_rules', '');
@ -231,14 +224,14 @@ export default createReactClass({
return joinRules.getContent().join_rule;
}
}
},
}
_communityProfile: function() {
_communityProfile() {
if (this.props.room) return CommunityPrototypeStore.instance.getInviteProfile(this.props.room.roomId);
return {displayName: null, avatarMxc: null};
},
}
_roomName: function(atStart = false) {
_roomName(atStart = false) {
let name = this.props.room ? this.props.room.name : this.props.roomAlias;
const profile = this._communityProfile();
if (profile.displayName) name = profile.displayName;
@ -249,16 +242,16 @@ export default createReactClass({
} else {
return _t("this room");
}
},
}
_getMyMember() {
return (
this.props.room &&
this.props.room.getMember(MatrixClientPeg.get().getUserId())
);
},
}
_getInviteMember: function() {
_getInviteMember() {
const {room} = this.props;
if (!room) {
return;
@ -270,7 +263,7 @@ export default createReactClass({
}
const inviterUserId = inviteEvent.events.member.getSender();
return room.currentState.getMember(inviterUserId);
},
}
_isDMInvite() {
const myMember = this._getMyMember();
@ -280,7 +273,7 @@ export default createReactClass({
const memberEvent = myMember.events.member;
const memberContent = memberEvent.getContent();
return memberContent.membership === "invite" && memberContent.is_direct;
},
}
_makeScreenAfterLogin() {
return {
@ -293,17 +286,17 @@ export default createReactClass({
inviter_name: this.props.oobData ? this.props.oobData.inviterName : null,
}
};
},
}
onLoginClick: function() {
onLoginClick = () => {
dis.dispatch({ action: 'start_login', screenAfterLogin: this._makeScreenAfterLogin() });
},
};
onRegisterClick: function() {
onRegisterClick = () => {
dis.dispatch({ action: 'start_registration', screenAfterLogin: this._makeScreenAfterLogin() });
},
};
render: function() {
render() {
const brand = SdkConfig.get().brand;
const Spinner = sdk.getComponent('elements.Spinner');
const AccessibleButton = sdk.getComponent('elements.AccessibleButton');
@ -597,5 +590,5 @@ export default createReactClass({
</div>
</div>
);
},
});
}
}

View file

@ -16,29 +16,26 @@ limitations under the License.
import React from 'react';
import PropTypes from 'prop-types';
import createReactClass from 'create-react-class';
import * as sdk from '../../../index';
import Modal from '../../../Modal';
import { _t } from '../../../languageHandler';
import {MatrixClientPeg} from "../../../MatrixClientPeg";
export default createReactClass({
displayName: 'RoomUpgradeWarningBar',
propTypes: {
export default class RoomUpgradeWarningBar extends React.Component {
static propTypes = {
room: PropTypes.object.isRequired,
recommendation: PropTypes.object.isRequired,
},
};
componentDidMount: function() {
componentDidMount() {
const tombstone = this.props.room.currentState.getStateEvents("m.room.tombstone", "");
this.setState({upgraded: tombstone && tombstone.getContent().replacement_room});
MatrixClientPeg.get().on("RoomState.events", this._onStateEvents);
},
}
_onStateEvents: function(event, state) {
_onStateEvents = (event, state) => {
if (!this.props.room || event.getRoomId() !== this.props.room.roomId) {
return;
}
@ -47,14 +44,14 @@ export default createReactClass({
const tombstone = this.props.room.currentState.getStateEvents("m.room.tombstone", "");
this.setState({upgraded: tombstone && tombstone.getContent().replacement_room});
},
};
onUpgradeClick: function() {
onUpgradeClick = () => {
const RoomUpgradeDialog = sdk.getComponent('dialogs.RoomUpgradeDialog');
Modal.createTrackedDialog('Upgrade Room Version', '', RoomUpgradeDialog, {room: this.props.room});
},
};
render: function() {
render() {
const AccessibleButton = sdk.getComponent('elements.AccessibleButton');
let doUpgradeWarnings = (
@ -117,5 +114,5 @@ export default createReactClass({
</div>
</div>
);
},
});
}
}

View file

@ -15,35 +15,31 @@ limitations under the License.
*/
import React, {createRef} from 'react';
import createReactClass from 'create-react-class';
import AccessibleButton from "../elements/AccessibleButton";
import classNames from "classnames";
import { _t } from '../../../languageHandler';
import {Key} from "../../../Keyboard";
export default createReactClass({
displayName: 'SearchBar',
export default class SearchBar extends React.Component {
constructor(props) {
super(props);
getInitialState: function() {
return ({
scope: 'Room',
});
},
// TODO: [REACT-WARNING] Replace component with real class, use constructor for refs
UNSAFE_componentWillMount: function() {
this._search_term = createRef();
},
onThisRoomClick: function() {
this.state = {
scope: 'Room',
};
}
onThisRoomClick = () => {
this.setState({ scope: 'Room' }, () => this._searchIfQuery());
},
};
onAllRoomsClick: function() {
onAllRoomsClick = () => {
this.setState({ scope: 'All' }, () => this._searchIfQuery());
},
};
onSearchChange: function(e) {
onSearchChange = (e) => {
switch (e.key) {
case Key.ENTER:
this.onSearch();
@ -52,19 +48,19 @@ export default createReactClass({
this.props.onCancelClick();
break;
}
},
};
_searchIfQuery: function() {
_searchIfQuery() {
if (this._search_term.current.value) {
this.onSearch();
}
},
}
onSearch: function() {
onSearch = () => {
this.props.onSearch(this._search_term.current.value, this.state.scope);
},
};
render: function() {
render() {
const searchButtonClasses = classNames("mx_SearchBar_searchButton", {
mx_SearchBar_searching: this.props.searchInProgress,
});
@ -92,5 +88,5 @@ export default createReactClass({
<AccessibleButton className="mx_SearchBar_cancel" onClick={this.props.onCancelClick} />
</div>
);
},
});
}
}

View file

@ -17,14 +17,11 @@ limitations under the License.
import React from 'react';
import PropTypes from 'prop-types';
import createReactClass from 'create-react-class';
import * as sdk from '../../../index';
import {haveTileForEvent} from "./EventTile";
export default createReactClass({
displayName: 'SearchResult',
propTypes: {
export default class SearchResultTile extends React.Component {
static propTypes = {
// a matrix-js-sdk SearchResult containing the details of this result
searchResult: PropTypes.object.isRequired,
@ -35,9 +32,9 @@ export default createReactClass({
resultLink: PropTypes.string,
onHeightChanged: PropTypes.func,
},
};
render: function() {
render() {
const DateSeparator = sdk.getComponent('messages.DateSeparator');
const EventTile = sdk.getComponent('rooms.EventTile');
const result = this.props.searchResult;
@ -66,5 +63,5 @@ export default createReactClass({
<li data-scroll-tokens={eventId+"+"+j}>
{ ret }
</li>);
},
});
}
}

View file

@ -16,7 +16,6 @@ limitations under the License.
import React from 'react';
import PropTypes from 'prop-types';
import createReactClass from 'create-react-class';
import AccessibleButton from '../elements/AccessibleButton';
import * as sdk from '../../../index';
import { _t } from '../../../languageHandler';
@ -37,18 +36,16 @@ export function CancelButton(props) {
* A stripped-down room header used for things like the user settings
* and room directory.
*/
export default createReactClass({
displayName: 'SimpleRoomHeader',
propTypes: {
export default class SimpleRoomHeader extends React.Component {
static propTypes = {
title: PropTypes.string,
onCancelClick: PropTypes.func,
// `src` to a TintableSvg. Optional.
icon: PropTypes.string,
},
};
render: function() {
render() {
let cancelButton;
let icon;
if (this.props.onCancelClick) {
@ -73,5 +70,5 @@ export default createReactClass({
</div>
</div>
);
},
});
}
}

View file

@ -18,19 +18,16 @@ limitations under the License.
import React from 'react';
import PropTypes from 'prop-types';
import createReactClass from 'create-react-class';
import { _t } from '../../../languageHandler';
import AccessibleButton from '../elements/AccessibleButton';
export default createReactClass({
displayName: 'TopUnreadMessagesBar',
propTypes: {
export default class TopUnreadMessagesBar extends React.Component {
static propTypes = {
onScrollUpClick: PropTypes.func,
onCloseClick: PropTypes.func,
},
};
render: function() {
render() {
return (
<div className="mx_TopUnreadMessagesBar">
<AccessibleButton className="mx_TopUnreadMessagesBar_scrollUp"
@ -43,5 +40,5 @@ export default createReactClass({
</AccessibleButton>
</div>
);
},
});
}
}

View file

@ -17,16 +17,13 @@ limitations under the License.
import React from 'react';
import PropTypes from 'prop-types';
import createReactClass from 'create-react-class';
import * as WhoIsTyping from '../../../WhoIsTyping';
import Timer from '../../../utils/Timer';
import {MatrixClientPeg} from '../../../MatrixClientPeg';
import MemberAvatar from '../avatars/MemberAvatar';
export default createReactClass({
displayName: 'WhoIsTypingTile',
propTypes: {
export default class WhoIsTypingTile extends React.Component {
static propTypes = {
// the room this statusbar is representing.
room: PropTypes.object.isRequired,
onShown: PropTypes.func,
@ -34,32 +31,28 @@ export default createReactClass({
// Number of names to display in typing indication. E.g. set to 3, will
// result in "X, Y, Z and 100 others are typing."
whoIsTypingLimit: PropTypes.number,
},
};
getDefaultProps: function() {
return {
whoIsTypingLimit: 3,
};
},
static defaultProps = {
whoIsTypingLimit: 3,
};
getInitialState: function() {
return {
usersTyping: WhoIsTyping.usersTypingApartFromMe(this.props.room),
// a map with userid => Timer to delay
// hiding the "x is typing" message for a
// user so hiding it can coincide
// with the sent message by the other side
// resulting in less timeline jumpiness
delayedStopTypingTimers: {},
};
},
state = {
usersTyping: WhoIsTyping.usersTypingApartFromMe(this.props.room),
// a map with userid => Timer to delay
// hiding the "x is typing" message for a
// user so hiding it can coincide
// with the sent message by the other side
// resulting in less timeline jumpiness
delayedStopTypingTimers: {},
};
componentDidMount: function() {
componentDidMount() {
MatrixClientPeg.get().on("RoomMember.typing", this.onRoomMemberTyping);
MatrixClientPeg.get().on("Room.timeline", this.onRoomTimeline);
},
}
componentDidUpdate: function(_, prevState) {
componentDidUpdate(_, prevState) {
const wasVisible = this._isVisible(prevState);
const isVisible = this._isVisible(this.state);
if (this.props.onShown && !wasVisible && isVisible) {
@ -67,9 +60,9 @@ export default createReactClass({
} else if (this.props.onHidden && wasVisible && !isVisible) {
this.props.onHidden();
}
},
}
componentWillUnmount: function() {
componentWillUnmount() {
// we may have entirely lost our client as we're logging out before clicking login on the guest bar...
const client = MatrixClientPeg.get();
if (client) {
@ -77,17 +70,17 @@ export default createReactClass({
client.removeListener("Room.timeline", this.onRoomTimeline);
}
Object.values(this.state.delayedStopTypingTimers).forEach((t) => t.abort());
},
}
_isVisible: function(state) {
_isVisible(state) {
return state.usersTyping.length !== 0 || Object.keys(state.delayedStopTypingTimers).length !== 0;
},
}
isVisible: function() {
isVisible = () => {
return this._isVisible(this.state);
},
};
onRoomTimeline: function(event, room) {
onRoomTimeline = (event, room) => {
if (room && room.roomId === this.props.room.roomId) {
const userId = event.getSender();
// remove user from usersTyping
@ -96,15 +89,15 @@ export default createReactClass({
// abort timer if any
this._abortUserTimer(userId);
}
},
};
onRoomMemberTyping: function(ev, member) {
onRoomMemberTyping = (ev, member) => {
const usersTyping = WhoIsTyping.usersTypingApartFromMeAndIgnored(this.props.room);
this.setState({
delayedStopTypingTimers: this._updateDelayedStopTypingTimers(usersTyping),
usersTyping,
});
},
};
_updateDelayedStopTypingTimers(usersTyping) {
const usersThatStoppedTyping = this.state.usersTyping.filter((a) => {
@ -142,26 +135,26 @@ export default createReactClass({
}, delayedStopTypingTimers);
return delayedStopTypingTimers;
},
}
_abortUserTimer: function(userId) {
_abortUserTimer(userId) {
const timer = this.state.delayedStopTypingTimers[userId];
if (timer) {
timer.abort();
this._removeUserTimer(userId);
}
},
}
_removeUserTimer: function(userId) {
_removeUserTimer(userId) {
const timer = this.state.delayedStopTypingTimers[userId];
if (timer) {
const delayedStopTypingTimers = Object.assign({}, this.state.delayedStopTypingTimers);
delete delayedStopTypingTimers[userId];
this.setState({delayedStopTypingTimers});
}
},
}
_renderTypingIndicatorAvatars: function(users, limit) {
_renderTypingIndicatorAvatars(users, limit) {
let othersCount = 0;
if (users.length > limit) {
othersCount = users.length - limit + 1;
@ -190,9 +183,9 @@ export default createReactClass({
}
return avatars;
},
}
render: function() {
render() {
let usersTyping = this.state.usersTyping;
const stoppedUsersOnTimer = Object.keys(this.state.delayedStopTypingTimers)
.map((userId) => this.props.room.getMember(userId));
@ -222,5 +215,5 @@ export default createReactClass({
</div>
</li>
);
},
});
}
}