Merge pull request #5157 from matrix-org/t3chguy/crc1
Remove create-react-class
This commit is contained in:
commit
e624ce11b4
112 changed files with 3113 additions and 3578 deletions
|
@ -43,8 +43,8 @@ export default class EmbeddedPage extends React.PureComponent {
|
|||
|
||||
static contextType = MatrixClientContext;
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
constructor(props, context) {
|
||||
super(props, context);
|
||||
|
||||
this._dispatcherRef = null;
|
||||
|
||||
|
|
|
@ -16,7 +16,6 @@ limitations under the License.
|
|||
*/
|
||||
|
||||
import React from 'react';
|
||||
import createReactClass from 'create-react-class';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
import {Filter} from 'matrix-js-sdk';
|
||||
|
@ -28,23 +27,20 @@ import { _t } from '../../languageHandler';
|
|||
/*
|
||||
* Component which shows the filtered file using a TimelinePanel
|
||||
*/
|
||||
const FilePanel = createReactClass({
|
||||
displayName: 'FilePanel',
|
||||
class FilePanel extends React.Component {
|
||||
static propTypes = {
|
||||
roomId: PropTypes.string.isRequired,
|
||||
};
|
||||
|
||||
// This is used to track if a decrypted event was a live event and should be
|
||||
// added to the timeline.
|
||||
decryptingEvents: new Set(),
|
||||
decryptingEvents = new Set();
|
||||
|
||||
propTypes: {
|
||||
roomId: PropTypes.string.isRequired,
|
||||
},
|
||||
state = {
|
||||
timelineSet: null,
|
||||
};
|
||||
|
||||
getInitialState: function() {
|
||||
return {
|
||||
timelineSet: null,
|
||||
};
|
||||
},
|
||||
|
||||
onRoomTimeline(ev, room, toStartOfTimeline, removed, data) {
|
||||
onRoomTimeline = (ev, room, toStartOfTimeline, removed, data) => {
|
||||
if (room.roomId !== this.props.roomId) return;
|
||||
if (toStartOfTimeline || !data || !data.liveEvent || ev.isRedacted()) return;
|
||||
|
||||
|
@ -53,9 +49,9 @@ const FilePanel = createReactClass({
|
|||
} else {
|
||||
this.addEncryptedLiveEvent(ev);
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
onEventDecrypted(ev, err) {
|
||||
onEventDecrypted = (ev, err) => {
|
||||
if (ev.getRoomId() !== this.props.roomId) return;
|
||||
const eventId = ev.getId();
|
||||
|
||||
|
@ -63,7 +59,7 @@ const FilePanel = createReactClass({
|
|||
if (err) return;
|
||||
|
||||
this.addEncryptedLiveEvent(ev);
|
||||
},
|
||||
};
|
||||
|
||||
addEncryptedLiveEvent(ev, toStartOfTimeline) {
|
||||
if (!this.state.timelineSet) return;
|
||||
|
@ -77,7 +73,7 @@ const FilePanel = createReactClass({
|
|||
if (!this.state.timelineSet.eventIdToTimeline(ev.getId())) {
|
||||
this.state.timelineSet.addEventToTimeline(ev, timeline, false);
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
async componentDidMount() {
|
||||
const client = MatrixClientPeg.get();
|
||||
|
@ -98,7 +94,7 @@ const FilePanel = createReactClass({
|
|||
client.on('Room.timeline', this.onRoomTimeline);
|
||||
client.on('Event.decrypted', this.onEventDecrypted);
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
const client = MatrixClientPeg.get();
|
||||
|
@ -110,7 +106,7 @@ const FilePanel = createReactClass({
|
|||
client.removeListener('Room.timeline', this.onRoomTimeline);
|
||||
client.removeListener('Event.decrypted', this.onEventDecrypted);
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
async fetchFileEventsServer(room) {
|
||||
const client = MatrixClientPeg.get();
|
||||
|
@ -134,9 +130,9 @@ const FilePanel = createReactClass({
|
|||
const timelineSet = room.getOrCreateFilteredTimelineSet(filter);
|
||||
|
||||
return timelineSet;
|
||||
},
|
||||
}
|
||||
|
||||
onPaginationRequest(timelineWindow, direction, limit) {
|
||||
onPaginationRequest = (timelineWindow, direction, limit) => {
|
||||
const client = MatrixClientPeg.get();
|
||||
const eventIndex = EventIndexPeg.get();
|
||||
const roomId = this.props.roomId;
|
||||
|
@ -152,7 +148,7 @@ const FilePanel = createReactClass({
|
|||
} else {
|
||||
return timelineWindow.paginate(direction, limit);
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
async updateTimelineSet(roomId: string) {
|
||||
const client = MatrixClientPeg.get();
|
||||
|
@ -188,9 +184,9 @@ const FilePanel = createReactClass({
|
|||
} else {
|
||||
console.error("Failed to add filtered timelineSet for FilePanel as no room!");
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
render: function() {
|
||||
render() {
|
||||
if (MatrixClientPeg.get().isGuest()) {
|
||||
return <div className="mx_FilePanel mx_RoomView_messageListWrapper">
|
||||
<div className="mx_RoomView_empty">
|
||||
|
@ -220,7 +216,7 @@ const FilePanel = createReactClass({
|
|||
// "(" + this.state.timelineSet._timelines.join(", ") + ")" + " with key " + this.props.roomId);
|
||||
return (
|
||||
<div className="mx_FilePanel" role="tabpanel">
|
||||
<TimelinePanel key={"filepanel_" + this.props.roomId}
|
||||
<TimelinePanel
|
||||
manageReadReceipts={false}
|
||||
manageReadMarkers={false}
|
||||
timelineSet={this.state.timelineSet}
|
||||
|
@ -239,7 +235,7 @@ const FilePanel = createReactClass({
|
|||
</div>
|
||||
);
|
||||
}
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export default FilePanel;
|
||||
|
|
|
@ -17,7 +17,6 @@ limitations under the License.
|
|||
*/
|
||||
|
||||
import React from 'react';
|
||||
import createReactClass from 'create-react-class';
|
||||
import PropTypes from 'prop-types';
|
||||
import {MatrixClientPeg} from '../../MatrixClientPeg';
|
||||
import * as sdk from '../../index';
|
||||
|
@ -70,10 +69,8 @@ const UserSummaryType = PropTypes.shape({
|
|||
}).isRequired,
|
||||
});
|
||||
|
||||
const CategoryRoomList = createReactClass({
|
||||
displayName: 'CategoryRoomList',
|
||||
|
||||
props: {
|
||||
class CategoryRoomList extends React.Component {
|
||||
static propTypes = {
|
||||
rooms: PropTypes.arrayOf(RoomSummaryType).isRequired,
|
||||
category: PropTypes.shape({
|
||||
profile: PropTypes.shape({
|
||||
|
@ -84,9 +81,9 @@ const CategoryRoomList = createReactClass({
|
|||
|
||||
// Whether the list should be editable
|
||||
editing: PropTypes.bool.isRequired,
|
||||
},
|
||||
};
|
||||
|
||||
onAddRoomsToSummaryClicked: function(ev) {
|
||||
onAddRoomsToSummaryClicked = (ev) => {
|
||||
ev.preventDefault();
|
||||
const AddressPickerDialog = sdk.getComponent("dialogs.AddressPickerDialog");
|
||||
Modal.createTrackedDialog('Add Rooms to Group Summary', '', AddressPickerDialog, {
|
||||
|
@ -122,9 +119,9 @@ const CategoryRoomList = createReactClass({
|
|||
});
|
||||
},
|
||||
}, /*className=*/null, /*isPriority=*/false, /*isStatic=*/true);
|
||||
},
|
||||
};
|
||||
|
||||
render: function() {
|
||||
render() {
|
||||
const TintableSvg = sdk.getComponent("elements.TintableSvg");
|
||||
const addButton = this.props.editing ?
|
||||
(<AccessibleButton className="mx_GroupView_featuredThings_addButton"
|
||||
|
@ -155,19 +152,17 @@ const CategoryRoomList = createReactClass({
|
|||
{ roomNodes }
|
||||
{ addButton }
|
||||
</div>;
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
const FeaturedRoom = createReactClass({
|
||||
displayName: 'FeaturedRoom',
|
||||
|
||||
props: {
|
||||
class FeaturedRoom extends React.Component {
|
||||
static propTypes = {
|
||||
summaryInfo: RoomSummaryType.isRequired,
|
||||
editing: PropTypes.bool.isRequired,
|
||||
groupId: PropTypes.string.isRequired,
|
||||
},
|
||||
};
|
||||
|
||||
onClick: function(e) {
|
||||
onClick = (e) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
|
||||
|
@ -176,9 +171,9 @@ const FeaturedRoom = createReactClass({
|
|||
room_alias: this.props.summaryInfo.profile.canonical_alias,
|
||||
room_id: this.props.summaryInfo.room_id,
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
onDeleteClicked: function(e) {
|
||||
onDeleteClicked = (e) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
GroupStore.removeRoomFromGroupSummary(
|
||||
|
@ -201,9 +196,9 @@ const FeaturedRoom = createReactClass({
|
|||
description: _t("The room '%(roomName)s' could not be removed from the summary.", {roomName}),
|
||||
});
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
render: function() {
|
||||
render() {
|
||||
const RoomAvatar = sdk.getComponent("avatars.RoomAvatar");
|
||||
|
||||
const roomName = this.props.summaryInfo.profile.name ||
|
||||
|
@ -243,13 +238,11 @@ const FeaturedRoom = createReactClass({
|
|||
<div className="mx_GroupView_featuredThing_name">{ roomNameNode }</div>
|
||||
{ deleteButton }
|
||||
</AccessibleButton>;
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
const RoleUserList = createReactClass({
|
||||
displayName: 'RoleUserList',
|
||||
|
||||
props: {
|
||||
class RoleUserList extends React.Component {
|
||||
static propTypes = {
|
||||
users: PropTypes.arrayOf(UserSummaryType).isRequired,
|
||||
role: PropTypes.shape({
|
||||
profile: PropTypes.shape({
|
||||
|
@ -260,9 +253,9 @@ const RoleUserList = createReactClass({
|
|||
|
||||
// Whether the list should be editable
|
||||
editing: PropTypes.bool.isRequired,
|
||||
},
|
||||
};
|
||||
|
||||
onAddUsersClicked: function(ev) {
|
||||
onAddUsersClicked = (ev) => {
|
||||
ev.preventDefault();
|
||||
const AddressPickerDialog = sdk.getComponent("dialogs.AddressPickerDialog");
|
||||
Modal.createTrackedDialog('Add Users to Group Summary', '', AddressPickerDialog, {
|
||||
|
@ -298,9 +291,9 @@ const RoleUserList = createReactClass({
|
|||
});
|
||||
},
|
||||
}, /*className=*/null, /*isPriority=*/false, /*isStatic=*/true);
|
||||
},
|
||||
};
|
||||
|
||||
render: function() {
|
||||
render() {
|
||||
const TintableSvg = sdk.getComponent("elements.TintableSvg");
|
||||
const addButton = this.props.editing ?
|
||||
(<AccessibleButton className="mx_GroupView_featuredThings_addButton" onClick={this.onAddUsersClicked}>
|
||||
|
@ -325,19 +318,17 @@ const RoleUserList = createReactClass({
|
|||
{ userNodes }
|
||||
{ addButton }
|
||||
</div>;
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
const FeaturedUser = createReactClass({
|
||||
displayName: 'FeaturedUser',
|
||||
|
||||
props: {
|
||||
class FeaturedUser extends React.Component {
|
||||
static propTypes = {
|
||||
summaryInfo: UserSummaryType.isRequired,
|
||||
editing: PropTypes.bool.isRequired,
|
||||
groupId: PropTypes.string.isRequired,
|
||||
},
|
||||
};
|
||||
|
||||
onClick: function(e) {
|
||||
onClick = (e) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
|
||||
|
@ -345,9 +336,9 @@ const FeaturedUser = createReactClass({
|
|||
action: 'view_start_chat_or_reuse',
|
||||
user_id: this.props.summaryInfo.user_id,
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
onDeleteClicked: function(e) {
|
||||
onDeleteClicked = (e) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
GroupStore.removeUserFromGroupSummary(
|
||||
|
@ -368,9 +359,9 @@ const FeaturedUser = createReactClass({
|
|||
description: _t("The user '%(displayName)s' could not be removed from the summary.", {displayName}),
|
||||
});
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
render: function() {
|
||||
render() {
|
||||
const BaseAvatar = sdk.getComponent("avatars.BaseAvatar");
|
||||
const name = this.props.summaryInfo.displayname || this.props.summaryInfo.user_id;
|
||||
|
||||
|
@ -394,41 +385,37 @@ const FeaturedUser = createReactClass({
|
|||
<div className="mx_GroupView_featuredThing_name">{ userNameNode }</div>
|
||||
{ deleteButton }
|
||||
</AccessibleButton>;
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
const GROUP_JOINPOLICY_OPEN = "open";
|
||||
const GROUP_JOINPOLICY_INVITE = "invite";
|
||||
|
||||
export default createReactClass({
|
||||
displayName: 'GroupView',
|
||||
|
||||
propTypes: {
|
||||
export default class GroupView extends React.Component {
|
||||
static propTypes = {
|
||||
groupId: PropTypes.string.isRequired,
|
||||
// Whether this is the first time the group admin is viewing the group
|
||||
groupIsNew: PropTypes.bool,
|
||||
},
|
||||
};
|
||||
|
||||
getInitialState: function() {
|
||||
return {
|
||||
summary: null,
|
||||
isGroupPublicised: null,
|
||||
isUserPrivileged: null,
|
||||
groupRooms: null,
|
||||
groupRoomsLoading: null,
|
||||
error: null,
|
||||
editing: false,
|
||||
saving: false,
|
||||
uploadingAvatar: false,
|
||||
avatarChanged: false,
|
||||
membershipBusy: false,
|
||||
publicityBusy: false,
|
||||
inviterProfile: null,
|
||||
showRightPanel: RightPanelStore.getSharedInstance().isOpenForGroup,
|
||||
};
|
||||
},
|
||||
state = {
|
||||
summary: null,
|
||||
isGroupPublicised: null,
|
||||
isUserPrivileged: null,
|
||||
groupRooms: null,
|
||||
groupRoomsLoading: null,
|
||||
error: null,
|
||||
editing: false,
|
||||
saving: false,
|
||||
uploadingAvatar: false,
|
||||
avatarChanged: false,
|
||||
membershipBusy: false,
|
||||
publicityBusy: false,
|
||||
inviterProfile: null,
|
||||
showRightPanel: RightPanelStore.getSharedInstance().isOpenForGroup,
|
||||
};
|
||||
|
||||
componentDidMount: function() {
|
||||
componentDidMount() {
|
||||
this._unmounted = false;
|
||||
this._matrixClient = MatrixClientPeg.get();
|
||||
this._matrixClient.on("Group.myMembership", this._onGroupMyMembership);
|
||||
|
@ -437,9 +424,9 @@ export default createReactClass({
|
|||
|
||||
this._dispatcherRef = dis.register(this._onAction);
|
||||
this._rightPanelStoreToken = RightPanelStore.getSharedInstance().addListener(this._onRightPanelStoreUpdate);
|
||||
},
|
||||
}
|
||||
|
||||
componentWillUnmount: function() {
|
||||
componentWillUnmount() {
|
||||
this._unmounted = true;
|
||||
this._matrixClient.removeListener("Group.myMembership", this._onGroupMyMembership);
|
||||
dis.unregister(this._dispatcherRef);
|
||||
|
@ -448,10 +435,11 @@ export default createReactClass({
|
|||
if (this._rightPanelStoreToken) {
|
||||
this._rightPanelStoreToken.remove();
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
// TODO: [REACT-WARNING] Replace with appropriate lifecycle event
|
||||
UNSAFE_componentWillReceiveProps: function(newProps) {
|
||||
// eslint-disable-next-line camelcase
|
||||
UNSAFE_componentWillReceiveProps(newProps) {
|
||||
if (this.props.groupId !== newProps.groupId) {
|
||||
this.setState({
|
||||
summary: null,
|
||||
|
@ -460,24 +448,24 @@ export default createReactClass({
|
|||
this._initGroupStore(newProps.groupId);
|
||||
});
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
_onRightPanelStoreUpdate: function() {
|
||||
_onRightPanelStoreUpdate = () => {
|
||||
this.setState({
|
||||
showRightPanel: RightPanelStore.getSharedInstance().isOpenForGroup,
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
_onGroupMyMembership: function(group) {
|
||||
_onGroupMyMembership = (group) => {
|
||||
if (this._unmounted || group.groupId !== this.props.groupId) return;
|
||||
if (group.myMembership === 'leave') {
|
||||
// Leave settings - the user might have clicked the "Leave" button
|
||||
this._closeSettings();
|
||||
}
|
||||
this.setState({membershipBusy: false});
|
||||
},
|
||||
};
|
||||
|
||||
_initGroupStore: function(groupId, firstInit) {
|
||||
_initGroupStore(groupId, firstInit) {
|
||||
const group = this._matrixClient.getGroup(groupId);
|
||||
if (group && group.inviter && group.inviter.userId) {
|
||||
this._fetchInviterProfile(group.inviter.userId);
|
||||
|
@ -506,9 +494,9 @@ export default createReactClass({
|
|||
});
|
||||
}
|
||||
});
|
||||
},
|
||||
}
|
||||
|
||||
onGroupStoreUpdated(firstInit) {
|
||||
onGroupStoreUpdated = (firstInit) => {
|
||||
if (this._unmounted) return;
|
||||
const summary = GroupStore.getSummary(this.props.groupId);
|
||||
if (summary.profile) {
|
||||
|
@ -533,7 +521,7 @@ export default createReactClass({
|
|||
if (this.props.groupIsNew && firstInit) {
|
||||
this._onEditClick();
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
_fetchInviterProfile(userId) {
|
||||
this.setState({
|
||||
|
@ -555,9 +543,9 @@ export default createReactClass({
|
|||
inviterProfileBusy: false,
|
||||
});
|
||||
});
|
||||
},
|
||||
}
|
||||
|
||||
_onEditClick: function() {
|
||||
_onEditClick = () => {
|
||||
this.setState({
|
||||
editing: true,
|
||||
profileForm: Object.assign({}, this.state.summary.profile),
|
||||
|
@ -568,20 +556,20 @@ export default createReactClass({
|
|||
GROUP_JOINPOLICY_INVITE,
|
||||
},
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
_onShareClick: function() {
|
||||
_onShareClick = () => {
|
||||
const ShareDialog = sdk.getComponent("dialogs.ShareDialog");
|
||||
Modal.createTrackedDialog('share community dialog', '', ShareDialog, {
|
||||
target: this._matrixClient.getGroup(this.props.groupId) || new Group(this.props.groupId),
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
_onCancelClick: function() {
|
||||
_onCancelClick = () => {
|
||||
this._closeSettings();
|
||||
},
|
||||
};
|
||||
|
||||
_onAction(payload) {
|
||||
_onAction = (payload) => {
|
||||
switch (payload.action) {
|
||||
// NOTE: close_settings is an app-wide dispatch; as it is dispatched from MatrixChat
|
||||
case 'close_settings':
|
||||
|
@ -593,34 +581,34 @@ export default createReactClass({
|
|||
default:
|
||||
break;
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
_closeSettings() {
|
||||
_closeSettings = () => {
|
||||
dis.dispatch({action: 'close_settings'});
|
||||
},
|
||||
};
|
||||
|
||||
_onNameChange: function(value) {
|
||||
_onNameChange = (value) => {
|
||||
const newProfileForm = Object.assign(this.state.profileForm, { name: value });
|
||||
this.setState({
|
||||
profileForm: newProfileForm,
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
_onShortDescChange: function(value) {
|
||||
_onShortDescChange = (value) => {
|
||||
const newProfileForm = Object.assign(this.state.profileForm, { short_description: value });
|
||||
this.setState({
|
||||
profileForm: newProfileForm,
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
_onLongDescChange: function(e) {
|
||||
_onLongDescChange = (e) => {
|
||||
const newProfileForm = Object.assign(this.state.profileForm, { long_description: e.target.value });
|
||||
this.setState({
|
||||
profileForm: newProfileForm,
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
_onAvatarSelected: function(ev) {
|
||||
_onAvatarSelected = ev => {
|
||||
const file = ev.target.files[0];
|
||||
if (!file) return;
|
||||
|
||||
|
@ -644,15 +632,15 @@ export default createReactClass({
|
|||
description: _t('Failed to upload image'),
|
||||
});
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
_onJoinableChange: function(ev) {
|
||||
_onJoinableChange = ev => {
|
||||
this.setState({
|
||||
joinableForm: { policyType: ev.target.value },
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
_onSaveClick: function() {
|
||||
_onSaveClick = () => {
|
||||
this.setState({saving: true});
|
||||
const savePromise = this.state.isUserPrivileged ? this._saveGroup() : Promise.resolve();
|
||||
savePromise.then((result) => {
|
||||
|
@ -683,16 +671,16 @@ export default createReactClass({
|
|||
avatarChanged: false,
|
||||
});
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
_saveGroup: async function() {
|
||||
async _saveGroup() {
|
||||
await this._matrixClient.setGroupProfile(this.props.groupId, this.state.profileForm);
|
||||
await this._matrixClient.setGroupJoinPolicy(this.props.groupId, {
|
||||
type: this.state.joinableForm.policyType,
|
||||
});
|
||||
},
|
||||
}
|
||||
|
||||
_onAcceptInviteClick: async function() {
|
||||
_onAcceptInviteClick = async () => {
|
||||
this.setState({membershipBusy: true});
|
||||
|
||||
// Wait 500ms to prevent flashing. Do this before sending a request otherwise we risk the
|
||||
|
@ -709,9 +697,9 @@ export default createReactClass({
|
|||
description: _t("Unable to accept invite"),
|
||||
});
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
_onRejectInviteClick: async function() {
|
||||
_onRejectInviteClick = async () => {
|
||||
this.setState({membershipBusy: true});
|
||||
|
||||
// Wait 500ms to prevent flashing. Do this before sending a request otherwise we risk the
|
||||
|
@ -728,9 +716,9 @@ export default createReactClass({
|
|||
description: _t("Unable to reject invite"),
|
||||
});
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
_onJoinClick: async function() {
|
||||
_onJoinClick = async () => {
|
||||
if (this._matrixClient.isGuest()) {
|
||||
dis.dispatch({action: 'require_registration', screen_after: {screen: `group/${this.props.groupId}`}});
|
||||
return;
|
||||
|
@ -752,9 +740,9 @@ export default createReactClass({
|
|||
description: _t("Unable to join community"),
|
||||
});
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
_leaveGroupWarnings: function() {
|
||||
_leaveGroupWarnings() {
|
||||
const warnings = [];
|
||||
|
||||
if (this.state.isUserPrivileged) {
|
||||
|
@ -768,10 +756,9 @@ export default createReactClass({
|
|||
}
|
||||
|
||||
return warnings;
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
_onLeaveClick: function() {
|
||||
_onLeaveClick = () => {
|
||||
const QuestionDialog = sdk.getComponent("dialogs.QuestionDialog");
|
||||
const warnings = this._leaveGroupWarnings();
|
||||
|
||||
|
@ -806,13 +793,13 @@ export default createReactClass({
|
|||
});
|
||||
},
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
_onAddRoomsClick: function() {
|
||||
_onAddRoomsClick = () => {
|
||||
showGroupAddRoomDialog(this.props.groupId);
|
||||
},
|
||||
};
|
||||
|
||||
_getGroupSection: function() {
|
||||
_getGroupSection() {
|
||||
const groupSettingsSectionClasses = classnames({
|
||||
"mx_GroupView_group": this.state.editing,
|
||||
"mx_GroupView_group_disabled": this.state.editing && !this.state.isUserPrivileged,
|
||||
|
@ -856,9 +843,9 @@ export default createReactClass({
|
|||
{ this._getLongDescriptionNode() }
|
||||
{ this._getRoomsNode() }
|
||||
</div>;
|
||||
},
|
||||
}
|
||||
|
||||
_getRoomsNode: function() {
|
||||
_getRoomsNode() {
|
||||
const RoomDetailList = sdk.getComponent('rooms.RoomDetailList');
|
||||
const AccessibleButton = sdk.getComponent('elements.AccessibleButton');
|
||||
const TintableSvg = sdk.getComponent('elements.TintableSvg');
|
||||
|
@ -902,9 +889,9 @@ export default createReactClass({
|
|||
className={roomDetailListClassName} />
|
||||
}
|
||||
</div>;
|
||||
},
|
||||
}
|
||||
|
||||
_getFeaturedRoomsNode: function() {
|
||||
_getFeaturedRoomsNode() {
|
||||
const summary = this.state.summary;
|
||||
|
||||
const defaultCategoryRooms = [];
|
||||
|
@ -943,9 +930,9 @@ export default createReactClass({
|
|||
{ defaultCategoryNode }
|
||||
{ categoryRoomNodes }
|
||||
</div>;
|
||||
},
|
||||
}
|
||||
|
||||
_getFeaturedUsersNode: function() {
|
||||
_getFeaturedUsersNode() {
|
||||
const summary = this.state.summary;
|
||||
|
||||
const noRoleUsers = [];
|
||||
|
@ -984,9 +971,9 @@ export default createReactClass({
|
|||
{ noRoleNode }
|
||||
{ roleUserNodes }
|
||||
</div>;
|
||||
},
|
||||
}
|
||||
|
||||
_getMembershipSection: function() {
|
||||
_getMembershipSection() {
|
||||
const Spinner = sdk.getComponent("elements.Spinner");
|
||||
const BaseAvatar = sdk.getComponent("avatars.BaseAvatar");
|
||||
|
||||
|
@ -1100,9 +1087,9 @@ export default createReactClass({
|
|||
</div>
|
||||
</div>
|
||||
</div>;
|
||||
},
|
||||
}
|
||||
|
||||
_getJoinableNode: function() {
|
||||
_getJoinableNode() {
|
||||
const InlineSpinner = sdk.getComponent('elements.InlineSpinner');
|
||||
return this.state.editing ? <div>
|
||||
<h3>
|
||||
|
@ -1136,9 +1123,9 @@ export default createReactClass({
|
|||
</label>
|
||||
</div>
|
||||
</div> : null;
|
||||
},
|
||||
}
|
||||
|
||||
_getLongDescriptionNode: function() {
|
||||
_getLongDescriptionNode() {
|
||||
const summary = this.state.summary;
|
||||
let description = null;
|
||||
if (summary.profile && summary.profile.long_description) {
|
||||
|
@ -1175,9 +1162,9 @@ export default createReactClass({
|
|||
<div className="mx_GroupView_groupDesc">
|
||||
{ description }
|
||||
</div>;
|
||||
},
|
||||
}
|
||||
|
||||
render: function() {
|
||||
render() {
|
||||
const GroupAvatar = sdk.getComponent("avatars.GroupAvatar");
|
||||
const Spinner = sdk.getComponent("elements.Spinner");
|
||||
|
||||
|
@ -1366,5 +1353,5 @@ export default createReactClass({
|
|||
console.error("Invalid state for GroupView");
|
||||
return <div />;
|
||||
}
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,7 +17,6 @@ limitations under the License.
|
|||
|
||||
import {InteractiveAuth} from "matrix-js-sdk";
|
||||
import React, {createRef} from 'react';
|
||||
import createReactClass from 'create-react-class';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
import getEntryComponentForLoginType from '../views/auth/InteractiveAuthEntryComponents';
|
||||
|
@ -26,10 +25,8 @@ import * as sdk from '../../index';
|
|||
|
||||
export const ERROR_USER_CANCELLED = new Error("User cancelled auth session");
|
||||
|
||||
export default createReactClass({
|
||||
displayName: 'InteractiveAuth',
|
||||
|
||||
propTypes: {
|
||||
export default class InteractiveAuthComponent extends React.Component {
|
||||
static propTypes = {
|
||||
// matrix client to use for UI auth requests
|
||||
matrixClient: PropTypes.object.isRequired,
|
||||
|
||||
|
@ -86,20 +83,19 @@ export default createReactClass({
|
|||
// continueText and continueKind are passed straight through to the AuthEntryComponent.
|
||||
continueText: PropTypes.string,
|
||||
continueKind: PropTypes.string,
|
||||
},
|
||||
};
|
||||
|
||||
getInitialState: function() {
|
||||
return {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
authStage: null,
|
||||
busy: false,
|
||||
errorText: null,
|
||||
stageErrorText: null,
|
||||
submitButtonEnabled: false,
|
||||
};
|
||||
},
|
||||
|
||||
// TODO: [REACT-WARNING] Replace component with real class, use constructor for refs
|
||||
UNSAFE_componentWillMount: function() {
|
||||
this._unmounted = false;
|
||||
this._authLogic = new InteractiveAuth({
|
||||
authData: this.props.authData,
|
||||
|
@ -114,6 +110,18 @@ export default createReactClass({
|
|||
requestEmailToken: this._requestEmailToken,
|
||||
});
|
||||
|
||||
this._intervalId = null;
|
||||
if (this.props.poll) {
|
||||
this._intervalId = setInterval(() => {
|
||||
this._authLogic.poll();
|
||||
}, 2000);
|
||||
}
|
||||
|
||||
this._stageComponent = createRef();
|
||||
}
|
||||
|
||||
// TODO: [REACT-WARNING] Replace component with real class, use constructor for refs
|
||||
UNSAFE_componentWillMount() { // eslint-disable-line camelcase
|
||||
this._authLogic.attemptAuth().then((result) => {
|
||||
const extra = {
|
||||
emailSid: this._authLogic.getEmailSid(),
|
||||
|
@ -132,26 +140,17 @@ export default createReactClass({
|
|||
errorText: msg,
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
this._intervalId = null;
|
||||
if (this.props.poll) {
|
||||
this._intervalId = setInterval(() => {
|
||||
this._authLogic.poll();
|
||||
}, 2000);
|
||||
}
|
||||
|
||||
this._stageComponent = createRef();
|
||||
},
|
||||
|
||||
componentWillUnmount: function() {
|
||||
componentWillUnmount() {
|
||||
this._unmounted = true;
|
||||
|
||||
if (this._intervalId !== null) {
|
||||
clearInterval(this._intervalId);
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
_requestEmailToken: async function(...args) {
|
||||
_requestEmailToken = async (...args) => {
|
||||
this.setState({
|
||||
busy: true,
|
||||
});
|
||||
|
@ -162,15 +161,15 @@ export default createReactClass({
|
|||
busy: false,
|
||||
});
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
tryContinue: function() {
|
||||
tryContinue = () => {
|
||||
if (this._stageComponent.current && this._stageComponent.current.tryContinue) {
|
||||
this._stageComponent.current.tryContinue();
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
_authStateUpdated: function(stageType, stageState) {
|
||||
_authStateUpdated = (stageType, stageState) => {
|
||||
const oldStage = this.state.authStage;
|
||||
this.setState({
|
||||
busy: false,
|
||||
|
@ -180,16 +179,16 @@ export default createReactClass({
|
|||
}, () => {
|
||||
if (oldStage != stageType) this._setFocus();
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
_requestCallback: function(auth) {
|
||||
_requestCallback = (auth) => {
|
||||
// This wrapper just exists because the js-sdk passes a second
|
||||
// 'busy' param for backwards compat. This throws the tests off
|
||||
// so discard it here.
|
||||
return this.props.makeRequest(auth);
|
||||
},
|
||||
};
|
||||
|
||||
_onBusyChanged: function(busy) {
|
||||
_onBusyChanged = (busy) => {
|
||||
// if we've started doing stuff, reset the error messages
|
||||
if (busy) {
|
||||
this.setState({
|
||||
|
@ -204,29 +203,29 @@ export default createReactClass({
|
|||
// there's a new screen to show the user. This is implemented by setting
|
||||
// `busy: false` in `_authStateUpdated`.
|
||||
// See also https://github.com/vector-im/element-web/issues/12546
|
||||
},
|
||||
};
|
||||
|
||||
_setFocus: function() {
|
||||
_setFocus() {
|
||||
if (this._stageComponent.current && this._stageComponent.current.focus) {
|
||||
this._stageComponent.current.focus();
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
_submitAuthDict: function(authData) {
|
||||
_submitAuthDict = authData => {
|
||||
this._authLogic.submitAuthDict(authData);
|
||||
},
|
||||
};
|
||||
|
||||
_onPhaseChange: function(newPhase) {
|
||||
_onPhaseChange = newPhase => {
|
||||
if (this.props.onStagePhaseChange) {
|
||||
this.props.onStagePhaseChange(this.state.authStage, newPhase || 0);
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
_onStageCancel: function() {
|
||||
_onStageCancel = () => {
|
||||
this.props.onAuthFinished(false, ERROR_USER_CANCELLED);
|
||||
},
|
||||
};
|
||||
|
||||
_renderCurrentStage: function() {
|
||||
_renderCurrentStage() {
|
||||
const stage = this.state.authStage;
|
||||
if (!stage) {
|
||||
if (this.state.busy) {
|
||||
|
@ -260,16 +259,17 @@ export default createReactClass({
|
|||
onCancel={this._onStageCancel}
|
||||
/>
|
||||
);
|
||||
},
|
||||
}
|
||||
|
||||
_onAuthStageFailed: function(e) {
|
||||
_onAuthStageFailed = e => {
|
||||
this.props.onAuthFinished(false, e);
|
||||
},
|
||||
_setEmailSid: function(sid) {
|
||||
this._authLogic.setEmailSid(sid);
|
||||
},
|
||||
};
|
||||
|
||||
render: function() {
|
||||
_setEmailSid = sid => {
|
||||
this._authLogic.setEmailSid(sid);
|
||||
};
|
||||
|
||||
render() {
|
||||
let error = null;
|
||||
if (this.state.errorText) {
|
||||
error = (
|
||||
|
@ -287,5 +287,5 @@ export default createReactClass({
|
|||
</div>
|
||||
</div>
|
||||
);
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,7 +17,6 @@ limitations under the License.
|
|||
*/
|
||||
|
||||
import React from 'react';
|
||||
import createReactClass from 'create-react-class';
|
||||
import * as sdk from '../../index';
|
||||
import { _t } from '../../languageHandler';
|
||||
import SdkConfig from '../../SdkConfig';
|
||||
|
@ -26,29 +25,23 @@ import AccessibleButton from '../views/elements/AccessibleButton';
|
|||
import MatrixClientContext from "../../contexts/MatrixClientContext";
|
||||
import AutoHideScrollbar from "./AutoHideScrollbar";
|
||||
|
||||
export default createReactClass({
|
||||
displayName: 'MyGroups',
|
||||
export default class MyGroups extends React.Component {
|
||||
static contextType = MatrixClientContext;
|
||||
|
||||
getInitialState: function() {
|
||||
return {
|
||||
groups: null,
|
||||
error: null,
|
||||
};
|
||||
},
|
||||
state = {
|
||||
groups: null,
|
||||
error: null,
|
||||
};
|
||||
|
||||
statics: {
|
||||
contextType: MatrixClientContext,
|
||||
},
|
||||
|
||||
componentDidMount: function() {
|
||||
componentDidMount() {
|
||||
this._fetch();
|
||||
},
|
||||
}
|
||||
|
||||
_onCreateGroupClick: function() {
|
||||
_onCreateGroupClick = () => {
|
||||
dis.dispatch({action: 'view_create_group'});
|
||||
},
|
||||
};
|
||||
|
||||
_fetch: function() {
|
||||
_fetch() {
|
||||
this.context.getJoinedGroups().then((result) => {
|
||||
this.setState({groups: result.groups, error: null});
|
||||
}, (err) => {
|
||||
|
@ -59,9 +52,9 @@ export default createReactClass({
|
|||
}
|
||||
this.setState({groups: null, error: err});
|
||||
});
|
||||
},
|
||||
}
|
||||
|
||||
render: function() {
|
||||
render() {
|
||||
const brand = SdkConfig.get().brand;
|
||||
const Loader = sdk.getComponent("elements.Spinner");
|
||||
const SimpleRoomHeader = sdk.getComponent('rooms.SimpleRoomHeader');
|
||||
|
@ -149,5 +142,5 @@ export default createReactClass({
|
|||
{ content }
|
||||
</div>
|
||||
</div>;
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,7 +17,6 @@ limitations under the License.
|
|||
*/
|
||||
|
||||
import React from 'react';
|
||||
import createReactClass from 'create-react-class';
|
||||
import { _t } from '../../languageHandler';
|
||||
import {MatrixClientPeg} from "../../MatrixClientPeg";
|
||||
import * as sdk from "../../index";
|
||||
|
@ -25,13 +24,8 @@ import * as sdk from "../../index";
|
|||
/*
|
||||
* Component which shows the global notification list using a TimelinePanel
|
||||
*/
|
||||
const NotificationPanel = createReactClass({
|
||||
displayName: 'NotificationPanel',
|
||||
|
||||
propTypes: {
|
||||
},
|
||||
|
||||
render: function() {
|
||||
class NotificationPanel extends React.Component {
|
||||
render() {
|
||||
// wrap a TimelinePanel with the jump-to-event bits turned off.
|
||||
const TimelinePanel = sdk.getComponent("structures.TimelinePanel");
|
||||
const Loader = sdk.getComponent("elements.Spinner");
|
||||
|
@ -45,7 +39,7 @@ const NotificationPanel = createReactClass({
|
|||
if (timelineSet) {
|
||||
return (
|
||||
<div className="mx_NotificationPanel" role="tabpanel">
|
||||
<TimelinePanel key={"NotificationPanel_" + this.props.roomId}
|
||||
<TimelinePanel
|
||||
manageReadReceipts={false}
|
||||
manageReadMarkers={false}
|
||||
timelineSet={timelineSet}
|
||||
|
@ -63,7 +57,7 @@ const NotificationPanel = createReactClass({
|
|||
</div>
|
||||
);
|
||||
}
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export default NotificationPanel;
|
||||
|
|
|
@ -42,8 +42,8 @@ export default class RightPanel extends React.Component {
|
|||
|
||||
static contextType = MatrixClientContext;
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
constructor(props, context) {
|
||||
super(props, context);
|
||||
this.state = {
|
||||
phase: this._getPhaseFromProps(),
|
||||
isUserPrivilegedInGroup: null,
|
||||
|
|
|
@ -17,7 +17,6 @@ limitations under the License.
|
|||
*/
|
||||
|
||||
import React from 'react';
|
||||
import createReactClass from 'create-react-class';
|
||||
import {MatrixClientPeg} from "../../MatrixClientPeg";
|
||||
import * as sdk from "../../index";
|
||||
import dis from "../../dispatcher/dispatcher";
|
||||
|
@ -42,16 +41,16 @@ function track(action) {
|
|||
Analytics.trackEvent('RoomDirectory', action);
|
||||
}
|
||||
|
||||
export default createReactClass({
|
||||
displayName: 'RoomDirectory',
|
||||
|
||||
propTypes: {
|
||||
export default class RoomDirectory extends React.Component {
|
||||
static propTypes = {
|
||||
onFinished: PropTypes.func.isRequired,
|
||||
},
|
||||
};
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
getInitialState: function() {
|
||||
const selectedCommunityId = TagOrderStore.getSelectedTags()[0];
|
||||
return {
|
||||
this.state = {
|
||||
publicRooms: [],
|
||||
loading: true,
|
||||
protocolsLoading: true,
|
||||
|
@ -64,10 +63,7 @@ export default createReactClass({
|
|||
: null,
|
||||
communityName: null,
|
||||
};
|
||||
},
|
||||
|
||||
// TODO: [REACT-WARNING] Move this to constructor
|
||||
UNSAFE_componentWillMount: function() {
|
||||
this._unmounted = false;
|
||||
this.nextBatch = null;
|
||||
this.filterTimeout = null;
|
||||
|
@ -115,16 +111,16 @@ export default createReactClass({
|
|||
}
|
||||
|
||||
this.refreshRoomList();
|
||||
},
|
||||
}
|
||||
|
||||
componentWillUnmount: function() {
|
||||
componentWillUnmount() {
|
||||
if (this.filterTimeout) {
|
||||
clearTimeout(this.filterTimeout);
|
||||
}
|
||||
this._unmounted = true;
|
||||
},
|
||||
}
|
||||
|
||||
refreshRoomList: function() {
|
||||
refreshRoomList = () => {
|
||||
if (this.state.selectedCommunityId) {
|
||||
this.setState({
|
||||
publicRooms: GroupStore.getGroupRooms(this.state.selectedCommunityId).map(r => {
|
||||
|
@ -158,9 +154,9 @@ export default createReactClass({
|
|||
loading: true,
|
||||
});
|
||||
this.getMoreRooms();
|
||||
},
|
||||
};
|
||||
|
||||
getMoreRooms: function() {
|
||||
getMoreRooms() {
|
||||
if (this.state.selectedCommunityId) return Promise.resolve(); // no more rooms
|
||||
if (!MatrixClientPeg.get()) return Promise.resolve();
|
||||
|
||||
|
@ -233,7 +229,7 @@ export default createReactClass({
|
|||
),
|
||||
});
|
||||
});
|
||||
},
|
||||
}
|
||||
|
||||
/**
|
||||
* A limited interface for removing rooms from the directory.
|
||||
|
@ -242,7 +238,7 @@ export default createReactClass({
|
|||
* HS admins to do this through the RoomSettings interface, but
|
||||
* this needs SPEC-417.
|
||||
*/
|
||||
removeFromDirectory: function(room) {
|
||||
removeFromDirectory(room) {
|
||||
const alias = get_display_alias_for_room(room);
|
||||
const name = room.name || alias || _t('Unnamed room');
|
||||
|
||||
|
@ -284,18 +280,18 @@ export default createReactClass({
|
|||
});
|
||||
},
|
||||
});
|
||||
},
|
||||
}
|
||||
|
||||
onRoomClicked: function(room, ev) {
|
||||
onRoomClicked = (room, ev) => {
|
||||
if (ev.shiftKey && !this.state.selectedCommunityId) {
|
||||
ev.preventDefault();
|
||||
this.removeFromDirectory(room);
|
||||
} else {
|
||||
this.showRoom(room);
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
onOptionChange: function(server, instanceId) {
|
||||
onOptionChange = (server, instanceId) => {
|
||||
// clear next batch so we don't try to load more rooms
|
||||
this.nextBatch = null;
|
||||
this.setState({
|
||||
|
@ -313,15 +309,15 @@ export default createReactClass({
|
|||
// find the five gitter ones, at which point we do not want
|
||||
// to render all those rooms when switching back to 'all networks'.
|
||||
// Easiest to just blow away the state & re-fetch.
|
||||
},
|
||||
};
|
||||
|
||||
onFillRequest: function(backwards) {
|
||||
onFillRequest = (backwards) => {
|
||||
if (backwards || !this.nextBatch) return Promise.resolve(false);
|
||||
|
||||
return this.getMoreRooms();
|
||||
},
|
||||
};
|
||||
|
||||
onFilterChange: function(alias) {
|
||||
onFilterChange = (alias) => {
|
||||
this.setState({
|
||||
filterString: alias || null,
|
||||
});
|
||||
|
@ -337,9 +333,9 @@ export default createReactClass({
|
|||
this.filterTimeout = null;
|
||||
this.refreshRoomList();
|
||||
}, 700);
|
||||
},
|
||||
};
|
||||
|
||||
onFilterClear: function() {
|
||||
onFilterClear = () => {
|
||||
// update immediately
|
||||
this.setState({
|
||||
filterString: null,
|
||||
|
@ -348,9 +344,9 @@ export default createReactClass({
|
|||
if (this.filterTimeout) {
|
||||
clearTimeout(this.filterTimeout);
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
onJoinFromSearchClick: function(alias) {
|
||||
onJoinFromSearchClick = (alias) => {
|
||||
// If we don't have a particular instance id selected, just show that rooms alias
|
||||
if (!this.state.instanceId || this.state.instanceId === ALL_ROOMS) {
|
||||
// If the user specified an alias without a domain, add on whichever server is selected
|
||||
|
@ -391,9 +387,9 @@ export default createReactClass({
|
|||
});
|
||||
});
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
onPreviewClick: function(ev, room) {
|
||||
onPreviewClick = (ev, room) => {
|
||||
this.props.onFinished();
|
||||
dis.dispatch({
|
||||
action: 'view_room',
|
||||
|
@ -401,9 +397,9 @@ export default createReactClass({
|
|||
should_peek: true,
|
||||
});
|
||||
ev.stopPropagation();
|
||||
},
|
||||
};
|
||||
|
||||
onViewClick: function(ev, room) {
|
||||
onViewClick = (ev, room) => {
|
||||
this.props.onFinished();
|
||||
dis.dispatch({
|
||||
action: 'view_room',
|
||||
|
@ -411,26 +407,26 @@ export default createReactClass({
|
|||
should_peek: false,
|
||||
});
|
||||
ev.stopPropagation();
|
||||
},
|
||||
};
|
||||
|
||||
onJoinClick: function(ev, room) {
|
||||
onJoinClick = (ev, room) => {
|
||||
this.showRoom(room, null, true);
|
||||
ev.stopPropagation();
|
||||
},
|
||||
};
|
||||
|
||||
onCreateRoomClick: function(room) {
|
||||
onCreateRoomClick = room => {
|
||||
this.props.onFinished();
|
||||
dis.dispatch({
|
||||
action: 'view_create_room',
|
||||
public: true,
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
showRoomAlias: function(alias, autoJoin=false) {
|
||||
showRoomAlias(alias, autoJoin=false) {
|
||||
this.showRoom(null, alias, autoJoin);
|
||||
},
|
||||
}
|
||||
|
||||
showRoom: function(room, room_alias, autoJoin=false) {
|
||||
showRoom(room, room_alias, autoJoin=false) {
|
||||
this.props.onFinished();
|
||||
const payload = {
|
||||
action: 'view_room',
|
||||
|
@ -474,7 +470,7 @@ export default createReactClass({
|
|||
payload.room_id = room.room_id;
|
||||
}
|
||||
dis.dispatch(payload);
|
||||
},
|
||||
}
|
||||
|
||||
getRow(room) {
|
||||
const client = MatrixClientPeg.get();
|
||||
|
@ -540,22 +536,22 @@ export default createReactClass({
|
|||
<td className="mx_RoomDirectory_join">{joinOrViewButton}</td>
|
||||
</tr>
|
||||
);
|
||||
},
|
||||
}
|
||||
|
||||
collectScrollPanel: function(element) {
|
||||
collectScrollPanel = (element) => {
|
||||
this.scrollPanel = element;
|
||||
},
|
||||
};
|
||||
|
||||
_stringLooksLikeId: function(s, field_type) {
|
||||
_stringLooksLikeId(s, field_type) {
|
||||
let pat = /^#[^\s]+:[^\s]/;
|
||||
if (field_type && field_type.regexp) {
|
||||
pat = new RegExp(field_type.regexp);
|
||||
}
|
||||
|
||||
return pat.test(s);
|
||||
},
|
||||
}
|
||||
|
||||
_getFieldsForThirdPartyLocation: function(userInput, protocol, instance) {
|
||||
_getFieldsForThirdPartyLocation(userInput, protocol, instance) {
|
||||
// make an object with the fields specified by that protocol. We
|
||||
// require that the values of all but the last field come from the
|
||||
// instance. The last is the user input.
|
||||
|
@ -569,20 +565,20 @@ export default createReactClass({
|
|||
}
|
||||
fields[requiredFields[requiredFields.length - 1]] = userInput;
|
||||
return fields;
|
||||
},
|
||||
}
|
||||
|
||||
/**
|
||||
* called by the parent component when PageUp/Down/etc is pressed.
|
||||
*
|
||||
* We pass it down to the scroll panel.
|
||||
*/
|
||||
handleScrollKey: function(ev) {
|
||||
handleScrollKey = ev => {
|
||||
if (this.scrollPanel) {
|
||||
this.scrollPanel.handleScrollKey(ev);
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
render: function() {
|
||||
render() {
|
||||
const Loader = sdk.getComponent("elements.Spinner");
|
||||
const BaseDialog = sdk.getComponent('views.dialogs.BaseDialog');
|
||||
const AccessibleButton = sdk.getComponent('elements.AccessibleButton');
|
||||
|
@ -712,8 +708,8 @@ export default createReactClass({
|
|||
</div>
|
||||
</BaseDialog>
|
||||
);
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Similar to matrix-react-sdk's MatrixTools.getDisplayAliasForRoom
|
||||
// but works with the objects we get from the public room list
|
||||
|
|
|
@ -17,7 +17,6 @@ limitations under the License.
|
|||
*/
|
||||
|
||||
import React from 'react';
|
||||
import createReactClass from 'create-react-class';
|
||||
import PropTypes from 'prop-types';
|
||||
import Matrix from 'matrix-js-sdk';
|
||||
import { _t, _td } from '../../languageHandler';
|
||||
|
@ -39,10 +38,8 @@ function getUnsentMessages(room) {
|
|||
});
|
||||
}
|
||||
|
||||
export default createReactClass({
|
||||
displayName: 'RoomStatusBar',
|
||||
|
||||
propTypes: {
|
||||
export default class RoomStatusBar extends React.Component {
|
||||
static propTypes = {
|
||||
// the room this statusbar is representing.
|
||||
room: PropTypes.object.isRequired,
|
||||
// This is true when the user is alone in the room, but has also sent a message.
|
||||
|
@ -86,37 +83,35 @@ export default createReactClass({
|
|||
// callback for when the status bar is displaying something and should
|
||||
// be visible
|
||||
onVisible: PropTypes.func,
|
||||
},
|
||||
};
|
||||
|
||||
getInitialState: function() {
|
||||
return {
|
||||
syncState: MatrixClientPeg.get().getSyncState(),
|
||||
syncStateData: MatrixClientPeg.get().getSyncStateData(),
|
||||
unsentMessages: getUnsentMessages(this.props.room),
|
||||
};
|
||||
},
|
||||
state = {
|
||||
syncState: MatrixClientPeg.get().getSyncState(),
|
||||
syncStateData: MatrixClientPeg.get().getSyncStateData(),
|
||||
unsentMessages: getUnsentMessages(this.props.room),
|
||||
};
|
||||
|
||||
componentDidMount: function() {
|
||||
componentDidMount() {
|
||||
MatrixClientPeg.get().on("sync", this.onSyncStateChange);
|
||||
MatrixClientPeg.get().on("Room.localEchoUpdated", this._onRoomLocalEchoUpdated);
|
||||
|
||||
this._checkSize();
|
||||
},
|
||||
}
|
||||
|
||||
componentDidUpdate: function() {
|
||||
componentDidUpdate() {
|
||||
this._checkSize();
|
||||
},
|
||||
}
|
||||
|
||||
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) {
|
||||
client.removeListener("sync", this.onSyncStateChange);
|
||||
client.removeListener("Room.localEchoUpdated", this._onRoomLocalEchoUpdated);
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
onSyncStateChange: function(state, prevState, data) {
|
||||
onSyncStateChange = (state, prevState, data) => {
|
||||
if (state === "SYNCING" && prevState === "SYNCING") {
|
||||
return;
|
||||
}
|
||||
|
@ -124,39 +119,39 @@ export default createReactClass({
|
|||
syncState: state,
|
||||
syncStateData: data,
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
_onResendAllClick: function() {
|
||||
_onResendAllClick = () => {
|
||||
Resend.resendUnsentEvents(this.props.room);
|
||||
dis.fire(Action.FocusComposer);
|
||||
},
|
||||
};
|
||||
|
||||
_onCancelAllClick: function() {
|
||||
_onCancelAllClick = () => {
|
||||
Resend.cancelUnsentEvents(this.props.room);
|
||||
dis.fire(Action.FocusComposer);
|
||||
},
|
||||
};
|
||||
|
||||
_onRoomLocalEchoUpdated: function(event, room, oldEventId, oldStatus) {
|
||||
_onRoomLocalEchoUpdated = (event, room, oldEventId, oldStatus) => {
|
||||
if (room.roomId !== this.props.room.roomId) return;
|
||||
|
||||
this.setState({
|
||||
unsentMessages: getUnsentMessages(this.props.room),
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
// Check whether current size is greater than 0, if yes call props.onVisible
|
||||
_checkSize: function() {
|
||||
_checkSize() {
|
||||
if (this._getSize()) {
|
||||
if (this.props.onVisible) this.props.onVisible();
|
||||
} else {
|
||||
if (this.props.onHidden) this.props.onHidden();
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
// We don't need the actual height - just whether it is likely to have
|
||||
// changed - so we use '0' to indicate normal size, and other values to
|
||||
// indicate other sizes.
|
||||
_getSize: function() {
|
||||
_getSize() {
|
||||
if (this._shouldShowConnectionError() ||
|
||||
this.props.hasActiveCall ||
|
||||
this.props.sentMessageAndIsAlone
|
||||
|
@ -166,10 +161,10 @@ export default createReactClass({
|
|||
return STATUS_BAR_EXPANDED_LARGE;
|
||||
}
|
||||
return STATUS_BAR_HIDDEN;
|
||||
},
|
||||
}
|
||||
|
||||
// return suitable content for the image on the left of the status bar.
|
||||
_getIndicator: function() {
|
||||
_getIndicator() {
|
||||
if (this.props.hasActiveCall) {
|
||||
const TintableSvg = sdk.getComponent("elements.TintableSvg");
|
||||
return (
|
||||
|
@ -182,9 +177,9 @@ export default createReactClass({
|
|||
}
|
||||
|
||||
return null;
|
||||
},
|
||||
}
|
||||
|
||||
_shouldShowConnectionError: function() {
|
||||
_shouldShowConnectionError() {
|
||||
// no conn bar trumps the "some not sent" msg since you can't resend without
|
||||
// a connection!
|
||||
// There's one situation in which we don't show this 'no connection' bar, and that's
|
||||
|
@ -195,9 +190,9 @@ export default createReactClass({
|
|||
this.state.syncStateData.error.errcode === 'M_RESOURCE_LIMIT_EXCEEDED',
|
||||
);
|
||||
return this.state.syncState === "ERROR" && !errorIsMauError;
|
||||
},
|
||||
}
|
||||
|
||||
_getUnsentMessageContent: function() {
|
||||
_getUnsentMessageContent() {
|
||||
const unsentMessages = this.state.unsentMessages;
|
||||
if (!unsentMessages.length) return null;
|
||||
|
||||
|
@ -272,10 +267,10 @@ export default createReactClass({
|
|||
</div>
|
||||
</div>
|
||||
</div>;
|
||||
},
|
||||
}
|
||||
|
||||
// return suitable content for the main (text) part of the status bar.
|
||||
_getContent: function() {
|
||||
_getContent() {
|
||||
if (this._shouldShowConnectionError()) {
|
||||
return (
|
||||
<div className="mx_RoomStatusBar_connectionLostBar">
|
||||
|
@ -323,9 +318,9 @@ export default createReactClass({
|
|||
}
|
||||
|
||||
return null;
|
||||
},
|
||||
}
|
||||
|
||||
render: function() {
|
||||
render() {
|
||||
const content = this._getContent();
|
||||
const indicator = this._getIndicator();
|
||||
|
||||
|
@ -339,5 +334,5 @@ export default createReactClass({
|
|||
</div>
|
||||
</div>
|
||||
);
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -24,7 +24,6 @@ limitations under the License.
|
|||
import shouldHideEvent from '../../shouldHideEvent';
|
||||
|
||||
import React, {createRef} from 'react';
|
||||
import createReactClass from 'create-react-class';
|
||||
import PropTypes from 'prop-types';
|
||||
import classNames from 'classnames';
|
||||
import { _t } from '../../languageHandler';
|
||||
|
@ -68,9 +67,8 @@ if (DEBUG) {
|
|||
debuglog = console.log.bind(console);
|
||||
}
|
||||
|
||||
export default createReactClass({
|
||||
displayName: 'RoomView',
|
||||
propTypes: {
|
||||
export default class RoomView extends React.Component {
|
||||
static propTypes = {
|
||||
ConferenceHandler: PropTypes.any,
|
||||
|
||||
// Called with the credentials of a registered user (if they were a ROU that
|
||||
|
@ -97,15 +95,15 @@ export default createReactClass({
|
|||
|
||||
// Servers the RoomView can use to try and assist joins
|
||||
viaServers: PropTypes.arrayOf(PropTypes.string),
|
||||
},
|
||||
};
|
||||
|
||||
statics: {
|
||||
contextType: MatrixClientContext,
|
||||
},
|
||||
static contextType = MatrixClientContext;
|
||||
|
||||
constructor(props, context) {
|
||||
super(props, context);
|
||||
|
||||
getInitialState: function() {
|
||||
const llMembers = this.context.hasLazyLoadMembersEnabled();
|
||||
return {
|
||||
this.state = {
|
||||
room: null,
|
||||
roomId: null,
|
||||
roomLoading: true,
|
||||
|
@ -171,10 +169,7 @@ export default createReactClass({
|
|||
|
||||
matrixClientIsReady: this.context && this.context.isInitialSyncComplete(),
|
||||
};
|
||||
},
|
||||
|
||||
// TODO: [REACT-WARNING] Replace component with real class, use constructor for refs
|
||||
UNSAFE_componentWillMount: function() {
|
||||
this.dispatcherRef = dis.register(this.onAction);
|
||||
this.context.on("Room", this.onRoom);
|
||||
this.context.on("Room.timeline", this.onRoomTimeline);
|
||||
|
@ -191,7 +186,6 @@ export default createReactClass({
|
|||
// Start listening for RoomViewStore updates
|
||||
this._roomStoreToken = RoomViewStore.addListener(this._onRoomViewStoreUpdate);
|
||||
this._rightPanelStoreToken = RightPanelStore.getSharedInstance().addListener(this._onRightPanelStoreUpdate);
|
||||
this._onRoomViewStoreUpdate(true);
|
||||
|
||||
WidgetEchoStore.on('update', this._onWidgetEchoStoreUpdate);
|
||||
this._showReadReceiptsWatchRef = SettingsStore.watchSetting("showReadReceipts", null,
|
||||
|
@ -201,15 +195,20 @@ export default createReactClass({
|
|||
this._searchResultsPanel = createRef();
|
||||
|
||||
this._layoutWatcherRef = SettingsStore.watchSetting("useIRCLayout", null, this.onLayoutChange);
|
||||
},
|
||||
}
|
||||
|
||||
_onReadReceiptsChange: function() {
|
||||
// TODO: [REACT-WARNING] Move into constructor
|
||||
UNSAFE_componentWillMount() {
|
||||
this._onRoomViewStoreUpdate(true);
|
||||
}
|
||||
|
||||
_onReadReceiptsChange = () => {
|
||||
this.setState({
|
||||
showReadReceipts: SettingsStore.getValue("showReadReceipts", this.state.roomId),
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
_onRoomViewStoreUpdate: function(initial) {
|
||||
_onRoomViewStoreUpdate = initial => {
|
||||
if (this.unmounted) {
|
||||
return;
|
||||
}
|
||||
|
@ -303,7 +302,7 @@ export default createReactClass({
|
|||
if (initial) {
|
||||
this._setupRoom(newState.room, newState.roomId, newState.joining, newState.shouldPeek);
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
_getRoomId() {
|
||||
// According to `_onRoomViewStoreUpdate`, `state.roomId` can be null
|
||||
|
@ -312,9 +311,9 @@ export default createReactClass({
|
|||
// the bare room ID. (We may want to update `state.roomId` after
|
||||
// resolving aliases, so we could always trust it.)
|
||||
return this.state.room ? this.state.room.roomId : this.state.roomId;
|
||||
},
|
||||
}
|
||||
|
||||
_getPermalinkCreatorForRoom: function(room) {
|
||||
_getPermalinkCreatorForRoom(room) {
|
||||
if (!this._permalinkCreators) this._permalinkCreators = {};
|
||||
if (this._permalinkCreators[room.roomId]) return this._permalinkCreators[room.roomId];
|
||||
|
||||
|
@ -327,22 +326,22 @@ export default createReactClass({
|
|||
this._permalinkCreators[room.roomId].load();
|
||||
}
|
||||
return this._permalinkCreators[room.roomId];
|
||||
},
|
||||
}
|
||||
|
||||
_stopAllPermalinkCreators: function() {
|
||||
_stopAllPermalinkCreators() {
|
||||
if (!this._permalinkCreators) return;
|
||||
for (const roomId of Object.keys(this._permalinkCreators)) {
|
||||
this._permalinkCreators[roomId].stop();
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
_onWidgetEchoStoreUpdate: function() {
|
||||
_onWidgetEchoStoreUpdate = () => {
|
||||
this.setState({
|
||||
showApps: this._shouldShowApps(this.state.room),
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
_setupRoom: function(room, roomId, joining, shouldPeek) {
|
||||
_setupRoom(room, roomId, joining, shouldPeek) {
|
||||
// if this is an unknown room then we're in one of three states:
|
||||
// - This is a room we can peek into (search engine) (we can /peek)
|
||||
// - This is a room we can publicly join or were invited to. (we can /join)
|
||||
|
@ -404,9 +403,9 @@ export default createReactClass({
|
|||
this.setState({isPeeking: false});
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
_shouldShowApps: function(room) {
|
||||
_shouldShowApps(room) {
|
||||
if (!BROWSER_SUPPORTS_SANDBOX) return false;
|
||||
|
||||
// Check if user has previously chosen to hide the app drawer for this
|
||||
|
@ -417,9 +416,9 @@ export default createReactClass({
|
|||
// This is confusing, but it means to say that we default to the tray being
|
||||
// hidden unless the user clicked to open it.
|
||||
return hideWidgetDrawer === "false";
|
||||
},
|
||||
}
|
||||
|
||||
componentDidMount: function() {
|
||||
componentDidMount() {
|
||||
const call = this._getCallForRoom();
|
||||
const callState = call ? call.call_state : "ended";
|
||||
this.setState({
|
||||
|
@ -435,14 +434,14 @@ export default createReactClass({
|
|||
this.onResize();
|
||||
|
||||
document.addEventListener("keydown", this.onNativeKeyDown);
|
||||
},
|
||||
}
|
||||
|
||||
shouldComponentUpdate: function(nextProps, nextState) {
|
||||
shouldComponentUpdate(nextProps, nextState) {
|
||||
return (!ObjectUtils.shallowEqual(this.props, nextProps) ||
|
||||
!ObjectUtils.shallowEqual(this.state, nextState));
|
||||
},
|
||||
}
|
||||
|
||||
componentDidUpdate: function() {
|
||||
componentDidUpdate() {
|
||||
if (this._roomView.current) {
|
||||
const roomView = this._roomView.current;
|
||||
if (!roomView.ondrop) {
|
||||
|
@ -464,9 +463,9 @@ export default createReactClass({
|
|||
atEndOfLiveTimeline: this._messagePanel.isAtEndOfLiveTimeline(),
|
||||
});
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
componentWillUnmount: function() {
|
||||
componentWillUnmount() {
|
||||
// set a boolean to say we've been unmounted, which any pending
|
||||
// promises can use to throw away their results.
|
||||
//
|
||||
|
@ -543,21 +542,21 @@ export default createReactClass({
|
|||
// Tinter.tint(); // reset colourscheme
|
||||
|
||||
SettingsStore.unwatchSetting(this._layoutWatcherRef);
|
||||
},
|
||||
}
|
||||
|
||||
onLayoutChange: function() {
|
||||
onLayoutChange = () => {
|
||||
this.setState({
|
||||
useIRCLayout: SettingsStore.getValue("useIRCLayout"),
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
_onRightPanelStoreUpdate: function() {
|
||||
_onRightPanelStoreUpdate = () => {
|
||||
this.setState({
|
||||
showRightPanel: RightPanelStore.getSharedInstance().isOpenForRoom,
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
onPageUnload(event) {
|
||||
onPageUnload = event => {
|
||||
if (ContentMessages.sharedInstance().getCurrentUploads().length > 0) {
|
||||
return event.returnValue =
|
||||
_t("You seem to be uploading files, are you sure you want to quit?");
|
||||
|
@ -565,10 +564,10 @@ export default createReactClass({
|
|||
return event.returnValue =
|
||||
_t("You seem to be in a call, are you sure you want to quit?");
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
// we register global shortcuts here, they *must not conflict* with local shortcuts elsewhere or both will fire
|
||||
onNativeKeyDown: function(ev) {
|
||||
onNativeKeyDown = ev => {
|
||||
let handled = false;
|
||||
const ctrlCmdOnly = isOnlyCtrlOrCmdKeyEvent(ev);
|
||||
|
||||
|
@ -592,9 +591,9 @@ export default createReactClass({
|
|||
ev.stopPropagation();
|
||||
ev.preventDefault();
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
onReactKeyDown: function(ev) {
|
||||
onReactKeyDown = ev => {
|
||||
let handled = false;
|
||||
|
||||
switch (ev.key) {
|
||||
|
@ -613,7 +612,7 @@ export default createReactClass({
|
|||
break;
|
||||
case Key.U.toUpperCase():
|
||||
if (isOnlyCtrlOrCmdIgnoreShiftKeyEvent(ev) && ev.shiftKey) {
|
||||
dis.dispatch({ action: "upload_file" })
|
||||
dis.dispatch({ action: "upload_file" });
|
||||
handled = true;
|
||||
}
|
||||
break;
|
||||
|
@ -623,9 +622,9 @@ export default createReactClass({
|
|||
ev.stopPropagation();
|
||||
ev.preventDefault();
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
onAction: function(payload) {
|
||||
onAction = payload => {
|
||||
switch (payload.action) {
|
||||
case 'message_send_failed':
|
||||
case 'message_sent':
|
||||
|
@ -709,9 +708,9 @@ export default createReactClass({
|
|||
}
|
||||
break;
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
onRoomTimeline: function(ev, room, toStartOfTimeline, removed, data) {
|
||||
onRoomTimeline = (ev, room, toStartOfTimeline, removed, data) => {
|
||||
if (this.unmounted) return;
|
||||
|
||||
// ignore events for other rooms
|
||||
|
@ -747,51 +746,51 @@ export default createReactClass({
|
|||
});
|
||||
}
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
onRoomName: function(room) {
|
||||
onRoomName = room => {
|
||||
if (this.state.room && room.roomId == this.state.room.roomId) {
|
||||
this.forceUpdate();
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
onRoomRecoveryReminderDontAskAgain: function() {
|
||||
onRoomRecoveryReminderDontAskAgain = () => {
|
||||
// Called when the option to not ask again is set:
|
||||
// force an update to hide the recovery reminder
|
||||
this.forceUpdate();
|
||||
},
|
||||
};
|
||||
|
||||
onKeyBackupStatus() {
|
||||
onKeyBackupStatus = () => {
|
||||
// Key backup status changes affect whether the in-room recovery
|
||||
// reminder is displayed.
|
||||
this.forceUpdate();
|
||||
},
|
||||
};
|
||||
|
||||
canResetTimeline: function() {
|
||||
canResetTimeline = () => {
|
||||
if (!this._messagePanel) {
|
||||
return true;
|
||||
}
|
||||
return this._messagePanel.canResetTimeline();
|
||||
},
|
||||
};
|
||||
|
||||
// called when state.room is first initialised (either at initial load,
|
||||
// after a successful peek, or after we join the room).
|
||||
_onRoomLoaded: function(room) {
|
||||
_onRoomLoaded = room => {
|
||||
this._calculatePeekRules(room);
|
||||
this._updatePreviewUrlVisibility(room);
|
||||
this._loadMembersIfJoined(room);
|
||||
this._calculateRecommendedVersion(room);
|
||||
this._updateE2EStatus(room);
|
||||
this._updatePermissions(room);
|
||||
},
|
||||
};
|
||||
|
||||
_calculateRecommendedVersion: async function(room) {
|
||||
async _calculateRecommendedVersion(room) {
|
||||
this.setState({
|
||||
upgradeRecommendation: await room.getRecommendedVersion(),
|
||||
});
|
||||
},
|
||||
}
|
||||
|
||||
_loadMembersIfJoined: async function(room) {
|
||||
async _loadMembersIfJoined(room) {
|
||||
// lazy load members if enabled
|
||||
if (this.context.hasLazyLoadMembersEnabled()) {
|
||||
if (room && room.getMyMembership() === 'join') {
|
||||
|
@ -808,9 +807,9 @@ export default createReactClass({
|
|||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
_calculatePeekRules: function(room) {
|
||||
_calculatePeekRules(room) {
|
||||
const guestAccessEvent = room.currentState.getStateEvents("m.room.guest_access", "");
|
||||
if (guestAccessEvent && guestAccessEvent.getContent().guest_access === "can_join") {
|
||||
this.setState({
|
||||
|
@ -824,17 +823,17 @@ export default createReactClass({
|
|||
canPeek: true,
|
||||
});
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
_updatePreviewUrlVisibility: function({roomId}) {
|
||||
_updatePreviewUrlVisibility({roomId}) {
|
||||
// URL Previews in E2EE rooms can be a privacy leak so use a different setting which is per-room explicit
|
||||
const key = this.context.isRoomEncrypted(roomId) ? 'urlPreviewsEnabled_e2ee' : 'urlPreviewsEnabled';
|
||||
this.setState({
|
||||
showUrlPreview: SettingsStore.getValue(key, roomId),
|
||||
});
|
||||
},
|
||||
}
|
||||
|
||||
onRoom: function(room) {
|
||||
onRoom = room => {
|
||||
if (!room || room.roomId !== this.state.roomId) {
|
||||
return;
|
||||
}
|
||||
|
@ -843,32 +842,32 @@ export default createReactClass({
|
|||
}, () => {
|
||||
this._onRoomLoaded(room);
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
onDeviceVerificationChanged: function(userId, device) {
|
||||
onDeviceVerificationChanged = (userId, device) => {
|
||||
const room = this.state.room;
|
||||
if (!room.currentState.getMember(userId)) {
|
||||
return;
|
||||
}
|
||||
this._updateE2EStatus(room);
|
||||
},
|
||||
};
|
||||
|
||||
onUserVerificationChanged: function(userId, _trustStatus) {
|
||||
onUserVerificationChanged = (userId, _trustStatus) => {
|
||||
const room = this.state.room;
|
||||
if (!room || !room.currentState.getMember(userId)) {
|
||||
return;
|
||||
}
|
||||
this._updateE2EStatus(room);
|
||||
},
|
||||
};
|
||||
|
||||
onCrossSigningKeysChanged: function() {
|
||||
onCrossSigningKeysChanged = () => {
|
||||
const room = this.state.room;
|
||||
if (room) {
|
||||
this._updateE2EStatus(room);
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
_updateE2EStatus: async function(room) {
|
||||
async _updateE2EStatus(room) {
|
||||
if (!this.context.isRoomEncrypted(room.roomId)) {
|
||||
return;
|
||||
}
|
||||
|
@ -886,26 +885,26 @@ export default createReactClass({
|
|||
this.setState({
|
||||
e2eStatus: await shieldStatusForRoom(this.context, room),
|
||||
});
|
||||
},
|
||||
}
|
||||
|
||||
updateTint: function() {
|
||||
updateTint() {
|
||||
const room = this.state.room;
|
||||
if (!room) return;
|
||||
|
||||
console.log("Tinter.tint from updateTint");
|
||||
const colorScheme = SettingsStore.getValue("roomColor", room.roomId);
|
||||
Tinter.tint(colorScheme.primary_color, colorScheme.secondary_color);
|
||||
},
|
||||
}
|
||||
|
||||
onAccountData: function(event) {
|
||||
onAccountData = event => {
|
||||
const type = event.getType();
|
||||
if ((type === "org.matrix.preview_urls" || type === "im.vector.web.settings") && this.state.room) {
|
||||
// non-e2ee url previews are stored in legacy event type `org.matrix.room.preview_urls`
|
||||
this._updatePreviewUrlVisibility(this.state.room);
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
onRoomAccountData: function(event, room) {
|
||||
onRoomAccountData = (event, room) => {
|
||||
if (room.roomId == this.state.roomId) {
|
||||
const type = event.getType();
|
||||
if (type === "org.matrix.room.color_scheme") {
|
||||
|
@ -918,18 +917,18 @@ export default createReactClass({
|
|||
this._updatePreviewUrlVisibility(room);
|
||||
}
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
onRoomStateEvents: function(ev, state) {
|
||||
onRoomStateEvents = (ev, state) => {
|
||||
// ignore if we don't have a room yet
|
||||
if (!this.state.room || this.state.room.roomId !== state.roomId) {
|
||||
return;
|
||||
}
|
||||
|
||||
this._updatePermissions(this.state.room);
|
||||
},
|
||||
};
|
||||
|
||||
onRoomStateMember: function(ev, state, member) {
|
||||
onRoomStateMember = (ev, state, member) => {
|
||||
// ignore if we don't have a room yet
|
||||
if (!this.state.room) {
|
||||
return;
|
||||
|
@ -941,17 +940,17 @@ export default createReactClass({
|
|||
}
|
||||
|
||||
this._updateRoomMembers(member);
|
||||
},
|
||||
};
|
||||
|
||||
onMyMembership: function(room, membership, oldMembership) {
|
||||
onMyMembership = (room, membership, oldMembership) => {
|
||||
if (room.roomId === this.state.roomId) {
|
||||
this.forceUpdate();
|
||||
this._loadMembersIfJoined(room);
|
||||
this._updatePermissions(room);
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
_updatePermissions: function(room) {
|
||||
_updatePermissions(room) {
|
||||
if (room) {
|
||||
const me = this.context.getUserId();
|
||||
const canReact = room.getMyMembership() === "join" && room.currentState.maySendEvent("m.reaction", me);
|
||||
|
@ -959,11 +958,11 @@ export default createReactClass({
|
|||
|
||||
this.setState({canReact, canReply});
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
// rate limited because a power level change will emit an event for every
|
||||
// member in the room.
|
||||
_updateRoomMembers: rate_limited_func(function(dueToMember) {
|
||||
_updateRoomMembers = rate_limited_func((dueToMember) => {
|
||||
// a member state changed in this room
|
||||
// refresh the conf call notification state
|
||||
this._updateConfCallNotification();
|
||||
|
@ -978,9 +977,9 @@ export default createReactClass({
|
|||
this._checkIfAlone(this.state.room, memberCountInfluence);
|
||||
|
||||
this._updateE2EStatus(this.state.room);
|
||||
}, 500),
|
||||
}, 500);
|
||||
|
||||
_checkIfAlone: function(room, countInfluence) {
|
||||
_checkIfAlone(room, countInfluence) {
|
||||
let warnedAboutLonelyRoom = false;
|
||||
if (localStorage) {
|
||||
warnedAboutLonelyRoom = localStorage.getItem('mx_user_alone_warned_' + this.state.room.roomId);
|
||||
|
@ -993,9 +992,9 @@ export default createReactClass({
|
|||
let joinedOrInvitedMemberCount = room.getJoinedMemberCount() + room.getInvitedMemberCount();
|
||||
if (countInfluence) joinedOrInvitedMemberCount += countInfluence;
|
||||
this.setState({isAlone: joinedOrInvitedMemberCount === 1});
|
||||
},
|
||||
}
|
||||
|
||||
_updateConfCallNotification: function() {
|
||||
_updateConfCallNotification() {
|
||||
const room = this.state.room;
|
||||
if (!room || !this.props.ConferenceHandler) {
|
||||
return;
|
||||
|
@ -1017,7 +1016,7 @@ export default createReactClass({
|
|||
confMember.membership === "join"
|
||||
),
|
||||
});
|
||||
},
|
||||
}
|
||||
|
||||
_updateDMState() {
|
||||
const room = this.state.room;
|
||||
|
@ -1028,9 +1027,9 @@ export default createReactClass({
|
|||
if (dmInviter) {
|
||||
Rooms.setDMRoom(room.roomId, dmInviter);
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
onSearchResultsFillRequest: function(backwards) {
|
||||
onSearchResultsFillRequest = backwards => {
|
||||
if (!backwards) {
|
||||
return Promise.resolve(false);
|
||||
}
|
||||
|
@ -1043,25 +1042,25 @@ export default createReactClass({
|
|||
debuglog("no more search results");
|
||||
return Promise.resolve(false);
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
onInviteButtonClick: function() {
|
||||
onInviteButtonClick = () => {
|
||||
// call AddressPickerDialog
|
||||
dis.dispatch({
|
||||
action: 'view_invite',
|
||||
roomId: this.state.room.roomId,
|
||||
});
|
||||
this.setState({isAlone: false}); // there's a good chance they'll invite someone
|
||||
},
|
||||
};
|
||||
|
||||
onStopAloneWarningClick: function() {
|
||||
onStopAloneWarningClick = () => {
|
||||
if (localStorage) {
|
||||
localStorage.setItem('mx_user_alone_warned_' + this.state.room.roomId, true);
|
||||
}
|
||||
this.setState({isAlone: false});
|
||||
},
|
||||
};
|
||||
|
||||
onJoinButtonClicked: function(ev) {
|
||||
onJoinButtonClicked = ev => {
|
||||
// If the user is a ROU, allow them to transition to a PWLU
|
||||
if (this.context && this.context.isGuest()) {
|
||||
// Join this room once the user has registered and logged in
|
||||
|
@ -1120,10 +1119,9 @@ export default createReactClass({
|
|||
return Promise.resolve();
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
},
|
||||
|
||||
onMessageListScroll: function(ev) {
|
||||
onMessageListScroll = ev => {
|
||||
if (this._messagePanel.isAtEndOfLiveTimeline()) {
|
||||
this.setState({
|
||||
numUnreadMessages: 0,
|
||||
|
@ -1135,9 +1133,9 @@ export default createReactClass({
|
|||
});
|
||||
}
|
||||
this._updateTopUnreadMessagesBar();
|
||||
},
|
||||
};
|
||||
|
||||
onDragOver: function(ev) {
|
||||
onDragOver = ev => {
|
||||
ev.stopPropagation();
|
||||
ev.preventDefault();
|
||||
|
||||
|
@ -1154,9 +1152,9 @@ export default createReactClass({
|
|||
ev.dataTransfer.dropEffect = 'copy';
|
||||
}
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
onDrop: function(ev) {
|
||||
onDrop = ev => {
|
||||
ev.stopPropagation();
|
||||
ev.preventDefault();
|
||||
ContentMessages.sharedInstance().sendContentListToRoom(
|
||||
|
@ -1164,15 +1162,15 @@ export default createReactClass({
|
|||
);
|
||||
this.setState({ draggingFile: false });
|
||||
dis.fire(Action.FocusComposer);
|
||||
},
|
||||
};
|
||||
|
||||
onDragLeaveOrEnd: function(ev) {
|
||||
onDragLeaveOrEnd = ev => {
|
||||
ev.stopPropagation();
|
||||
ev.preventDefault();
|
||||
this.setState({ draggingFile: false });
|
||||
},
|
||||
};
|
||||
|
||||
injectSticker: function(url, info, text) {
|
||||
injectSticker(url, info, text) {
|
||||
if (this.context.isGuest()) {
|
||||
dis.dispatch({action: 'require_registration'});
|
||||
return;
|
||||
|
@ -1185,9 +1183,9 @@ export default createReactClass({
|
|||
return;
|
||||
}
|
||||
});
|
||||
},
|
||||
}
|
||||
|
||||
onSearch: function(term, scope) {
|
||||
onSearch = (term, scope) => {
|
||||
this.setState({
|
||||
searchTerm: term,
|
||||
searchScope: scope,
|
||||
|
@ -1213,9 +1211,9 @@ export default createReactClass({
|
|||
debuglog("sending search request");
|
||||
const searchPromise = eventSearch(term, roomId);
|
||||
this._handleSearchResult(searchPromise);
|
||||
},
|
||||
};
|
||||
|
||||
_handleSearchResult: function(searchPromise) {
|
||||
_handleSearchResult(searchPromise) {
|
||||
const self = this;
|
||||
|
||||
// keep a record of the current search id, so that if the search terms
|
||||
|
@ -1266,9 +1264,9 @@ export default createReactClass({
|
|||
searchInProgress: false,
|
||||
});
|
||||
});
|
||||
},
|
||||
}
|
||||
|
||||
getSearchResultTiles: function() {
|
||||
getSearchResultTiles() {
|
||||
const SearchResultTile = sdk.getComponent('rooms.SearchResultTile');
|
||||
const Spinner = sdk.getComponent("elements.Spinner");
|
||||
|
||||
|
@ -1348,20 +1346,20 @@ export default createReactClass({
|
|||
onHeightChanged={onHeightChanged} />);
|
||||
}
|
||||
return ret;
|
||||
},
|
||||
}
|
||||
|
||||
onPinnedClick: function() {
|
||||
onPinnedClick = () => {
|
||||
const nowShowingPinned = !this.state.showingPinned;
|
||||
const roomId = this.state.room.roomId;
|
||||
this.setState({showingPinned: nowShowingPinned, searching: false});
|
||||
SettingsStore.setValue("PinnedEvents.isOpen", roomId, SettingLevel.ROOM_DEVICE, nowShowingPinned);
|
||||
},
|
||||
};
|
||||
|
||||
onSettingsClick: function() {
|
||||
onSettingsClick = () => {
|
||||
dis.dispatch({ action: 'open_room_settings' });
|
||||
},
|
||||
};
|
||||
|
||||
onCancelClick: function() {
|
||||
onCancelClick = () => {
|
||||
console.log("updateTint from onCancelClick");
|
||||
this.updateTint();
|
||||
if (this.state.forwardingEvent) {
|
||||
|
@ -1371,23 +1369,23 @@ export default createReactClass({
|
|||
});
|
||||
}
|
||||
dis.fire(Action.FocusComposer);
|
||||
},
|
||||
};
|
||||
|
||||
onLeaveClick: function() {
|
||||
onLeaveClick = () => {
|
||||
dis.dispatch({
|
||||
action: 'leave_room',
|
||||
room_id: this.state.room.roomId,
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
onForgetClick: function() {
|
||||
onForgetClick = () => {
|
||||
dis.dispatch({
|
||||
action: 'forget_room',
|
||||
room_id: this.state.room.roomId,
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
onRejectButtonClicked: function(ev) {
|
||||
onRejectButtonClicked = ev => {
|
||||
const self = this;
|
||||
this.setState({
|
||||
rejecting: true,
|
||||
|
@ -1412,9 +1410,9 @@ export default createReactClass({
|
|||
rejectError: error,
|
||||
});
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
onRejectAndIgnoreClick: async function() {
|
||||
onRejectAndIgnoreClick = async () => {
|
||||
this.setState({
|
||||
rejecting: true,
|
||||
});
|
||||
|
@ -1446,49 +1444,49 @@ export default createReactClass({
|
|||
rejectError: error,
|
||||
});
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
onRejectThreepidInviteButtonClicked: function(ev) {
|
||||
onRejectThreepidInviteButtonClicked = ev => {
|
||||
// We can reject 3pid invites in the same way that we accept them,
|
||||
// using /leave rather than /join. In the short term though, we
|
||||
// just ignore them.
|
||||
// https://github.com/vector-im/vector-web/issues/1134
|
||||
dis.fire(Action.ViewRoomDirectory);
|
||||
},
|
||||
};
|
||||
|
||||
onSearchClick: function() {
|
||||
onSearchClick = () => {
|
||||
this.setState({
|
||||
searching: !this.state.searching,
|
||||
showingPinned: false,
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
onCancelSearchClick: function() {
|
||||
onCancelSearchClick = () => {
|
||||
this.setState({
|
||||
searching: false,
|
||||
searchResults: null,
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
// jump down to the bottom of this room, where new events are arriving
|
||||
jumpToLiveTimeline: function() {
|
||||
jumpToLiveTimeline = () => {
|
||||
this._messagePanel.jumpToLiveTimeline();
|
||||
dis.fire(Action.FocusComposer);
|
||||
},
|
||||
};
|
||||
|
||||
// jump up to wherever our read marker is
|
||||
jumpToReadMarker: function() {
|
||||
jumpToReadMarker = () => {
|
||||
this._messagePanel.jumpToReadMarker();
|
||||
},
|
||||
};
|
||||
|
||||
// update the read marker to match the read-receipt
|
||||
forgetReadMarker: function(ev) {
|
||||
forgetReadMarker = ev => {
|
||||
ev.stopPropagation();
|
||||
this._messagePanel.forgetReadMarker();
|
||||
},
|
||||
};
|
||||
|
||||
// decide whether or not the top 'unread messages' bar should be shown
|
||||
_updateTopUnreadMessagesBar: function() {
|
||||
_updateTopUnreadMessagesBar = () => {
|
||||
if (!this._messagePanel) {
|
||||
return;
|
||||
}
|
||||
|
@ -1497,12 +1495,12 @@ export default createReactClass({
|
|||
if (this.state.showTopUnreadMessagesBar != showBar) {
|
||||
this.setState({showTopUnreadMessagesBar: showBar});
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
// get the current scroll position of the room, so that it can be
|
||||
// restored when we switch back to it.
|
||||
//
|
||||
_getScrollState: function() {
|
||||
_getScrollState() {
|
||||
const messagePanel = this._messagePanel;
|
||||
if (!messagePanel) return null;
|
||||
|
||||
|
@ -1537,9 +1535,9 @@ export default createReactClass({
|
|||
focussedEvent: scrollState.trackedScrollToken,
|
||||
pixelOffset: scrollState.pixelOffset,
|
||||
};
|
||||
},
|
||||
}
|
||||
|
||||
onResize: function() {
|
||||
onResize = () => {
|
||||
// It seems flexbox doesn't give us a way to constrain the auxPanel height to have
|
||||
// a minimum of the height of the video element, whilst also capping it from pushing out the page
|
||||
// so we have to do it via JS instead. In this implementation we cap the height by putting
|
||||
|
@ -1557,16 +1555,16 @@ export default createReactClass({
|
|||
if (auxPanelMaxHeight < 50) auxPanelMaxHeight = 50;
|
||||
|
||||
this.setState({auxPanelMaxHeight: auxPanelMaxHeight});
|
||||
},
|
||||
};
|
||||
|
||||
onFullscreenClick: function() {
|
||||
onFullscreenClick = () => {
|
||||
dis.dispatch({
|
||||
action: 'video_fullscreen',
|
||||
fullscreen: true,
|
||||
}, true);
|
||||
},
|
||||
};
|
||||
|
||||
onMuteAudioClick: function() {
|
||||
onMuteAudioClick = () => {
|
||||
const call = this._getCallForRoom();
|
||||
if (!call) {
|
||||
return;
|
||||
|
@ -1574,9 +1572,9 @@ export default createReactClass({
|
|||
const newState = !call.isMicrophoneMuted();
|
||||
call.setMicrophoneMuted(newState);
|
||||
this.forceUpdate(); // TODO: just update the voip buttons
|
||||
},
|
||||
};
|
||||
|
||||
onMuteVideoClick: function() {
|
||||
onMuteVideoClick = () => {
|
||||
const call = this._getCallForRoom();
|
||||
if (!call) {
|
||||
return;
|
||||
|
@ -1584,29 +1582,29 @@ export default createReactClass({
|
|||
const newState = !call.isLocalVideoMuted();
|
||||
call.setLocalVideoMuted(newState);
|
||||
this.forceUpdate(); // TODO: just update the voip buttons
|
||||
},
|
||||
};
|
||||
|
||||
onStatusBarVisible: function() {
|
||||
onStatusBarVisible = () => {
|
||||
if (this.unmounted) return;
|
||||
this.setState({
|
||||
statusBarVisible: true,
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
onStatusBarHidden: function() {
|
||||
onStatusBarHidden = () => {
|
||||
// This is currently not desired as it is annoying if it keeps expanding and collapsing
|
||||
if (this.unmounted) return;
|
||||
this.setState({
|
||||
statusBarVisible: false,
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
* called by the parent component when PageUp/Down/etc is pressed.
|
||||
*
|
||||
* We pass it down to the scroll panel.
|
||||
*/
|
||||
handleScrollKey: function(ev) {
|
||||
handleScrollKey = ev => {
|
||||
let panel;
|
||||
if (this._searchResultsPanel.current) {
|
||||
panel = this._searchResultsPanel.current;
|
||||
|
@ -1617,48 +1615,48 @@ export default createReactClass({
|
|||
if (panel) {
|
||||
panel.handleScrollKey(ev);
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
* get any current call for this room
|
||||
*/
|
||||
_getCallForRoom: function() {
|
||||
_getCallForRoom() {
|
||||
if (!this.state.room) {
|
||||
return null;
|
||||
}
|
||||
return CallHandler.getCallForRoom(this.state.room.roomId);
|
||||
},
|
||||
}
|
||||
|
||||
// this has to be a proper method rather than an unnamed function,
|
||||
// otherwise react calls it with null on each update.
|
||||
_gatherTimelinePanelRef: function(r) {
|
||||
_gatherTimelinePanelRef = r => {
|
||||
this._messagePanel = r;
|
||||
if (r) {
|
||||
console.log("updateTint from RoomView._gatherTimelinePanelRef");
|
||||
this.updateTint();
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
_getOldRoom: function() {
|
||||
_getOldRoom() {
|
||||
const createEvent = this.state.room.currentState.getStateEvents("m.room.create", "");
|
||||
if (!createEvent || !createEvent.getContent()['predecessor']) return null;
|
||||
|
||||
return this.context.getRoom(createEvent.getContent()['predecessor']['room_id']);
|
||||
},
|
||||
}
|
||||
|
||||
_getHiddenHighlightCount: function() {
|
||||
_getHiddenHighlightCount() {
|
||||
const oldRoom = this._getOldRoom();
|
||||
if (!oldRoom) return 0;
|
||||
return oldRoom.getUnreadNotificationCount('highlight');
|
||||
},
|
||||
}
|
||||
|
||||
_onHiddenHighlightsClick: function() {
|
||||
_onHiddenHighlightsClick = () => {
|
||||
const oldRoom = this._getOldRoom();
|
||||
if (!oldRoom) return;
|
||||
dis.dispatch({action: "view_room", room_id: oldRoom.roomId});
|
||||
},
|
||||
};
|
||||
|
||||
render: function() {
|
||||
render() {
|
||||
const RoomHeader = sdk.getComponent('rooms.RoomHeader');
|
||||
const ForwardMessage = sdk.getComponent("rooms.ForwardMessage");
|
||||
const AuxPanel = sdk.getComponent("rooms.AuxPanel");
|
||||
|
@ -2118,5 +2116,5 @@ export default createReactClass({
|
|||
</main>
|
||||
</RoomContext.Provider>
|
||||
);
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,7 +15,6 @@ limitations under the License.
|
|||
*/
|
||||
|
||||
import React, {createRef} from "react";
|
||||
import createReactClass from 'create-react-class';
|
||||
import PropTypes from 'prop-types';
|
||||
import { Key } from '../../Keyboard';
|
||||
import Timer from '../../utils/Timer';
|
||||
|
@ -84,10 +83,8 @@ if (DEBUG_SCROLL) {
|
|||
* offset as normal.
|
||||
*/
|
||||
|
||||
export default createReactClass({
|
||||
displayName: 'ScrollPanel',
|
||||
|
||||
propTypes: {
|
||||
export default class ScrollPanel extends React.Component {
|
||||
static propTypes = {
|
||||
/* stickyBottom: if set to true, then once the user hits the bottom of
|
||||
* the list, any new children added to the list will cause the list to
|
||||
* scroll down to show the new element, rather than preserving the
|
||||
|
@ -149,20 +146,19 @@ export default createReactClass({
|
|||
* of the wrapper
|
||||
*/
|
||||
fixedChildren: PropTypes.node,
|
||||
},
|
||||
};
|
||||
|
||||
getDefaultProps: function() {
|
||||
return {
|
||||
stickyBottom: true,
|
||||
startAtBottom: true,
|
||||
onFillRequest: function(backwards) { return Promise.resolve(false); },
|
||||
onUnfillRequest: function(backwards, scrollToken) {},
|
||||
onScroll: function() {},
|
||||
};
|
||||
},
|
||||
static defaultProps = {
|
||||
stickyBottom: true,
|
||||
startAtBottom: true,
|
||||
onFillRequest: function(backwards) { return Promise.resolve(false); },
|
||||
onUnfillRequest: function(backwards, scrollToken) {},
|
||||
onScroll: function() {},
|
||||
};
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
// TODO: [REACT-WARNING] Replace component with real class, use constructor for refs
|
||||
UNSAFE_componentWillMount: function() {
|
||||
this._pendingFillRequests = {b: null, f: null};
|
||||
|
||||
if (this.props.resizeNotifier) {
|
||||
|
@ -172,13 +168,13 @@ export default createReactClass({
|
|||
this.resetScrollState();
|
||||
|
||||
this._itemlist = createRef();
|
||||
},
|
||||
}
|
||||
|
||||
componentDidMount: function() {
|
||||
componentDidMount() {
|
||||
this.checkScroll();
|
||||
},
|
||||
}
|
||||
|
||||
componentDidUpdate: function() {
|
||||
componentDidUpdate() {
|
||||
// after adding event tiles, we may need to tweak the scroll (either to
|
||||
// keep at the bottom of the timeline, or to maintain the view after
|
||||
// adding events to the top).
|
||||
|
@ -186,9 +182,9 @@ export default createReactClass({
|
|||
// This will also re-check the fill state, in case the paginate was inadequate
|
||||
this.checkScroll();
|
||||
this.updatePreventShrinking();
|
||||
},
|
||||
}
|
||||
|
||||
componentWillUnmount: function() {
|
||||
componentWillUnmount() {
|
||||
// set a boolean to say we've been unmounted, which any pending
|
||||
// promises can use to throw away their results.
|
||||
//
|
||||
|
@ -198,41 +194,41 @@ export default createReactClass({
|
|||
if (this.props.resizeNotifier) {
|
||||
this.props.resizeNotifier.removeListener("middlePanelResized", this.onResize);
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
onScroll: function(ev) {
|
||||
onScroll = ev => {
|
||||
debuglog("onScroll", this._getScrollNode().scrollTop);
|
||||
this._scrollTimeout.restart();
|
||||
this._saveScrollState();
|
||||
this.updatePreventShrinking();
|
||||
this.props.onScroll(ev);
|
||||
this.checkFillState();
|
||||
},
|
||||
};
|
||||
|
||||
onResize: function() {
|
||||
onResize = () => {
|
||||
this.checkScroll();
|
||||
// update preventShrinkingState if present
|
||||
if (this.preventShrinkingState) {
|
||||
this.preventShrinking();
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
// after an update to the contents of the panel, check that the scroll is
|
||||
// where it ought to be, and set off pagination requests if necessary.
|
||||
checkScroll: function() {
|
||||
checkScroll = () => {
|
||||
if (this.unmounted) {
|
||||
return;
|
||||
}
|
||||
this._restoreSavedScrollState();
|
||||
this.checkFillState();
|
||||
},
|
||||
};
|
||||
|
||||
// return true if the content is fully scrolled down right now; else false.
|
||||
//
|
||||
// note that this is independent of the 'stuckAtBottom' state - it is simply
|
||||
// about whether the content is scrolled down right now, irrespective of
|
||||
// whether it will stay that way when the children update.
|
||||
isAtBottom: function() {
|
||||
isAtBottom = () => {
|
||||
const sn = this._getScrollNode();
|
||||
// fractional values (both too big and too small)
|
||||
// for scrollTop happen on certain browsers/platforms
|
||||
|
@ -240,7 +236,7 @@ export default createReactClass({
|
|||
// so check difference <= 1;
|
||||
return Math.abs(sn.scrollHeight - (sn.scrollTop + sn.clientHeight)) <= 1;
|
||||
|
||||
},
|
||||
};
|
||||
|
||||
// returns the vertical height in the given direction that can be removed from
|
||||
// the content box (which has a height of scrollHeight, see checkFillState) without
|
||||
|
@ -273,7 +269,7 @@ export default createReactClass({
|
|||
// |#########| - |
|
||||
// |#########| |
|
||||
// `---------' -
|
||||
_getExcessHeight: function(backwards) {
|
||||
_getExcessHeight(backwards) {
|
||||
const sn = this._getScrollNode();
|
||||
const contentHeight = this._getMessagesHeight();
|
||||
const listHeight = this._getListHeight();
|
||||
|
@ -285,10 +281,10 @@ export default createReactClass({
|
|||
} else {
|
||||
return contentHeight - (unclippedScrollTop + 2*sn.clientHeight) - UNPAGINATION_PADDING;
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
// check the scroll state and send out backfill requests if necessary.
|
||||
checkFillState: async function(depth=0) {
|
||||
checkFillState = async (depth=0) => {
|
||||
if (this.unmounted) {
|
||||
return;
|
||||
}
|
||||
|
@ -368,10 +364,10 @@ export default createReactClass({
|
|||
this._fillRequestWhileRunning = false;
|
||||
this.checkFillState();
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
// check if unfilling is possible and send an unfill request if necessary
|
||||
_checkUnfillState: function(backwards) {
|
||||
_checkUnfillState(backwards) {
|
||||
let excessHeight = this._getExcessHeight(backwards);
|
||||
if (excessHeight <= 0) {
|
||||
return;
|
||||
|
@ -417,10 +413,10 @@ export default createReactClass({
|
|||
this.props.onUnfillRequest(backwards, markerScrollToken);
|
||||
}, UNFILL_REQUEST_DEBOUNCE_MS);
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
// check if there is already a pending fill request. If not, set one off.
|
||||
_maybeFill: function(depth, backwards) {
|
||||
_maybeFill(depth, backwards) {
|
||||
const dir = backwards ? 'b' : 'f';
|
||||
if (this._pendingFillRequests[dir]) {
|
||||
debuglog("Already a "+dir+" fill in progress - not starting another");
|
||||
|
@ -456,7 +452,7 @@ export default createReactClass({
|
|||
return this.checkFillState(depth + 1);
|
||||
}
|
||||
});
|
||||
},
|
||||
}
|
||||
|
||||
/* get the current scroll state. This returns an object with the following
|
||||
* properties:
|
||||
|
@ -472,9 +468,7 @@ export default createReactClass({
|
|||
* the number of pixels the bottom of the tracked child is above the
|
||||
* bottom of the scroll panel.
|
||||
*/
|
||||
getScrollState: function() {
|
||||
return this.scrollState;
|
||||
},
|
||||
getScrollState = () => this.scrollState;
|
||||
|
||||
/* reset the saved scroll state.
|
||||
*
|
||||
|
@ -488,7 +482,7 @@ export default createReactClass({
|
|||
* no use if no children exist yet, or if you are about to replace the
|
||||
* child list.)
|
||||
*/
|
||||
resetScrollState: function() {
|
||||
resetScrollState = () => {
|
||||
this.scrollState = {
|
||||
stuckAtBottom: this.props.startAtBottom,
|
||||
};
|
||||
|
@ -496,20 +490,20 @@ export default createReactClass({
|
|||
this._pages = 0;
|
||||
this._scrollTimeout = new Timer(100);
|
||||
this._heightUpdateInProgress = false;
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
* jump to the top of the content.
|
||||
*/
|
||||
scrollToTop: function() {
|
||||
scrollToTop = () => {
|
||||
this._getScrollNode().scrollTop = 0;
|
||||
this._saveScrollState();
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
* jump to the bottom of the content.
|
||||
*/
|
||||
scrollToBottom: function() {
|
||||
scrollToBottom = () => {
|
||||
// the easiest way to make sure that the scroll state is correctly
|
||||
// saved is to do the scroll, then save the updated state. (Calculating
|
||||
// it ourselves is hard, and we can't rely on an onScroll callback
|
||||
|
@ -517,25 +511,25 @@ export default createReactClass({
|
|||
const sn = this._getScrollNode();
|
||||
sn.scrollTop = sn.scrollHeight;
|
||||
this._saveScrollState();
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
* Page up/down.
|
||||
*
|
||||
* @param {number} mult: -1 to page up, +1 to page down
|
||||
*/
|
||||
scrollRelative: function(mult) {
|
||||
scrollRelative = mult => {
|
||||
const scrollNode = this._getScrollNode();
|
||||
const delta = mult * scrollNode.clientHeight * 0.5;
|
||||
scrollNode.scrollBy(0, delta);
|
||||
this._saveScrollState();
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
* Scroll up/down in response to a scroll key
|
||||
* @param {object} ev the keyboard event
|
||||
*/
|
||||
handleScrollKey: function(ev) {
|
||||
handleScrollKey = ev => {
|
||||
switch (ev.key) {
|
||||
case Key.PAGE_UP:
|
||||
if (!ev.ctrlKey && !ev.shiftKey && !ev.altKey && !ev.metaKey) {
|
||||
|
@ -561,7 +555,7 @@ export default createReactClass({
|
|||
}
|
||||
break;
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
/* Scroll the panel to bring the DOM node with the scroll token
|
||||
* `scrollToken` into view.
|
||||
|
@ -574,7 +568,7 @@ export default createReactClass({
|
|||
* node (specifically, the bottom of it) will be positioned. If omitted, it
|
||||
* defaults to 0.
|
||||
*/
|
||||
scrollToToken: function(scrollToken, pixelOffset, offsetBase) {
|
||||
scrollToToken = (scrollToken, pixelOffset, offsetBase) => {
|
||||
pixelOffset = pixelOffset || 0;
|
||||
offsetBase = offsetBase || 0;
|
||||
|
||||
|
@ -596,9 +590,9 @@ export default createReactClass({
|
|||
scrollNode.scrollTop = (trackedNode.offsetTop - (scrollNode.clientHeight * offsetBase)) + pixelOffset;
|
||||
this._saveScrollState();
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
_saveScrollState: function() {
|
||||
_saveScrollState() {
|
||||
if (this.props.stickyBottom && this.isAtBottom()) {
|
||||
this.scrollState = { stuckAtBottom: true };
|
||||
debuglog("saved stuckAtBottom state");
|
||||
|
@ -641,9 +635,9 @@ export default createReactClass({
|
|||
bottomOffset: bottomOffset,
|
||||
pixelOffset: bottomOffset - viewportBottom, //needed for restoring the scroll position when coming back to the room
|
||||
};
|
||||
},
|
||||
}
|
||||
|
||||
_restoreSavedScrollState: async function() {
|
||||
async _restoreSavedScrollState() {
|
||||
const scrollState = this.scrollState;
|
||||
|
||||
if (scrollState.stuckAtBottom) {
|
||||
|
@ -676,7 +670,8 @@ export default createReactClass({
|
|||
} else {
|
||||
debuglog("not updating height because request already in progress");
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
// need a better name that also indicates this will change scrollTop? Rebalance height? Reveal content?
|
||||
async _updateHeight() {
|
||||
// wait until user has stopped scrolling
|
||||
|
@ -731,7 +726,7 @@ export default createReactClass({
|
|||
debuglog("updateHeight to", {newHeight, topDiff});
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
_getTrackedNode() {
|
||||
const scrollState = this.scrollState;
|
||||
|
@ -764,11 +759,11 @@ export default createReactClass({
|
|||
}
|
||||
|
||||
return scrollState.trackedNode;
|
||||
},
|
||||
}
|
||||
|
||||
_getListHeight() {
|
||||
return this._bottomGrowth + (this._pages * PAGE_SIZE);
|
||||
},
|
||||
}
|
||||
|
||||
_getMessagesHeight() {
|
||||
const itemlist = this._itemlist.current;
|
||||
|
@ -777,17 +772,17 @@ export default createReactClass({
|
|||
const firstNodeTop = itemlist.firstElementChild ? itemlist.firstElementChild.offsetTop : 0;
|
||||
// 18 is itemlist padding
|
||||
return lastNodeBottom - firstNodeTop + (18 * 2);
|
||||
},
|
||||
}
|
||||
|
||||
_topFromBottom(node) {
|
||||
// current capped height - distance from top = distance from bottom of container to top of tracked element
|
||||
return this._itemlist.current.clientHeight - node.offsetTop;
|
||||
},
|
||||
}
|
||||
|
||||
/* get the DOM node which has the scrollTop property we care about for our
|
||||
* message panel.
|
||||
*/
|
||||
_getScrollNode: function() {
|
||||
_getScrollNode() {
|
||||
if (this.unmounted) {
|
||||
// this shouldn't happen, but when it does, turn the NPE into
|
||||
// something more meaningful.
|
||||
|
@ -801,18 +796,18 @@ export default createReactClass({
|
|||
}
|
||||
|
||||
return this._divScroll;
|
||||
},
|
||||
}
|
||||
|
||||
_collectScroll: function(divScroll) {
|
||||
_collectScroll = divScroll => {
|
||||
this._divScroll = divScroll;
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
Mark the bottom offset of the last tile so we can balance it out when
|
||||
anything below it changes, by calling updatePreventShrinking, to keep
|
||||
the same minimum bottom offset, effectively preventing the timeline to shrink.
|
||||
*/
|
||||
preventShrinking: function() {
|
||||
preventShrinking = () => {
|
||||
const messageList = this._itemlist.current;
|
||||
const tiles = messageList && messageList.children;
|
||||
if (!messageList) {
|
||||
|
@ -836,16 +831,16 @@ export default createReactClass({
|
|||
offsetNode: lastTileNode,
|
||||
};
|
||||
debuglog("prevent shrinking, last tile ", offsetFromBottom, "px from bottom");
|
||||
},
|
||||
};
|
||||
|
||||
/** Clear shrinking prevention. Used internally, and when the timeline is reloaded. */
|
||||
clearPreventShrinking: function() {
|
||||
clearPreventShrinking = () => {
|
||||
const messageList = this._itemlist.current;
|
||||
const balanceElement = messageList && messageList.parentElement;
|
||||
if (balanceElement) balanceElement.style.paddingBottom = null;
|
||||
this.preventShrinkingState = null;
|
||||
debuglog("prevent shrinking cleared");
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
update the container padding to balance
|
||||
|
@ -855,7 +850,7 @@ export default createReactClass({
|
|||
from the bottom of the marked tile grows larger than
|
||||
what it was when marking.
|
||||
*/
|
||||
updatePreventShrinking: function() {
|
||||
updatePreventShrinking = () => {
|
||||
if (this.preventShrinkingState) {
|
||||
const sn = this._getScrollNode();
|
||||
const scrollState = this.scrollState;
|
||||
|
@ -885,9 +880,9 @@ export default createReactClass({
|
|||
this.clearPreventShrinking();
|
||||
}
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
render: function() {
|
||||
render() {
|
||||
// TODO: the classnames on the div and ol could do with being updated to
|
||||
// reflect the fact that we don't necessarily contain a list of messages.
|
||||
// it's not obvious why we have a separate div and ol anyway.
|
||||
|
@ -905,5 +900,5 @@ export default createReactClass({
|
|||
</div>
|
||||
</AutoHideScrollbar>
|
||||
);
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,7 +16,6 @@ limitations under the License.
|
|||
*/
|
||||
|
||||
import React, {createRef} from 'react';
|
||||
import createReactClass from 'create-react-class';
|
||||
import PropTypes from 'prop-types';
|
||||
import { Key } from '../../Keyboard';
|
||||
import dis from '../../dispatcher/dispatcher';
|
||||
|
@ -24,10 +23,8 @@ import {throttle} from 'lodash';
|
|||
import AccessibleButton from '../../components/views/elements/AccessibleButton';
|
||||
import classNames from 'classnames';
|
||||
|
||||
export default createReactClass({
|
||||
displayName: 'SearchBox',
|
||||
|
||||
propTypes: {
|
||||
export default class SearchBox extends React.Component {
|
||||
static propTypes = {
|
||||
onSearch: PropTypes.func,
|
||||
onCleared: PropTypes.func,
|
||||
onKeyDown: PropTypes.func,
|
||||
|
@ -38,35 +35,32 @@ export default createReactClass({
|
|||
// on room search focus action (it would be nicer to take
|
||||
// this functionality out, but not obvious how that would work)
|
||||
enableRoomSearchFocus: PropTypes.bool,
|
||||
},
|
||||
};
|
||||
|
||||
getDefaultProps: function() {
|
||||
return {
|
||||
enableRoomSearchFocus: false,
|
||||
};
|
||||
},
|
||||
static defaultProps = {
|
||||
enableRoomSearchFocus: false,
|
||||
};
|
||||
|
||||
getInitialState: function() {
|
||||
return {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
this._search = createRef();
|
||||
|
||||
this.state = {
|
||||
searchTerm: "",
|
||||
blurred: true,
|
||||
};
|
||||
},
|
||||
}
|
||||
|
||||
// TODO: [REACT-WARNING] Replace component with real class, use constructor for refs
|
||||
UNSAFE_componentWillMount: function() {
|
||||
this._search = createRef();
|
||||
},
|
||||
|
||||
componentDidMount: function() {
|
||||
componentDidMount() {
|
||||
this.dispatcherRef = dis.register(this.onAction);
|
||||
},
|
||||
}
|
||||
|
||||
componentWillUnmount: function() {
|
||||
componentWillUnmount() {
|
||||
dis.unregister(this.dispatcherRef);
|
||||
},
|
||||
}
|
||||
|
||||
onAction: function(payload) {
|
||||
onAction = payload => {
|
||||
if (!this.props.enableRoomSearchFocus) return;
|
||||
|
||||
switch (payload.action) {
|
||||
|
@ -81,51 +75,51 @@ export default createReactClass({
|
|||
}
|
||||
break;
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
onChange: function() {
|
||||
onChange = () => {
|
||||
if (!this._search.current) return;
|
||||
this.setState({ searchTerm: this._search.current.value });
|
||||
this.onSearch();
|
||||
},
|
||||
};
|
||||
|
||||
onSearch: throttle(function() {
|
||||
onSearch = throttle(() => {
|
||||
this.props.onSearch(this._search.current.value);
|
||||
}, 200, {trailing: true, leading: true}),
|
||||
}, 200, {trailing: true, leading: true});
|
||||
|
||||
_onKeyDown: function(ev) {
|
||||
_onKeyDown = ev => {
|
||||
switch (ev.key) {
|
||||
case Key.ESCAPE:
|
||||
this._clearSearch("keyboard");
|
||||
break;
|
||||
}
|
||||
if (this.props.onKeyDown) this.props.onKeyDown(ev);
|
||||
},
|
||||
};
|
||||
|
||||
_onFocus: function(ev) {
|
||||
_onFocus = ev => {
|
||||
this.setState({blurred: false});
|
||||
ev.target.select();
|
||||
if (this.props.onFocus) {
|
||||
this.props.onFocus(ev);
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
_onBlur: function(ev) {
|
||||
_onBlur = ev => {
|
||||
this.setState({blurred: true});
|
||||
if (this.props.onBlur) {
|
||||
this.props.onBlur(ev);
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
_clearSearch: function(source) {
|
||||
_clearSearch(source) {
|
||||
this._search.current.value = "";
|
||||
this.onChange();
|
||||
if (this.props.onCleared) {
|
||||
this.props.onCleared(source);
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
render: function() {
|
||||
render() {
|
||||
// check for collapsed here and
|
||||
// not at parent so we keep
|
||||
// searchTerm in our state
|
||||
|
@ -166,5 +160,5 @@ export default createReactClass({
|
|||
{ clearButton }
|
||||
</div>
|
||||
);
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,7 +16,6 @@ limitations under the License.
|
|||
*/
|
||||
|
||||
import React from 'react';
|
||||
import createReactClass from 'create-react-class';
|
||||
import TagOrderStore from '../../stores/TagOrderStore';
|
||||
|
||||
import GroupActions from '../../actions/GroupActions';
|
||||
|
@ -32,21 +31,15 @@ import AutoHideScrollbar from "./AutoHideScrollbar";
|
|||
import SettingsStore from "../../settings/SettingsStore";
|
||||
import UserTagTile from "../views/elements/UserTagTile";
|
||||
|
||||
const TagPanel = createReactClass({
|
||||
displayName: 'TagPanel',
|
||||
class TagPanel extends React.Component {
|
||||
static contextType = MatrixClientContext;
|
||||
|
||||
statics: {
|
||||
contextType: MatrixClientContext,
|
||||
},
|
||||
state = {
|
||||
orderedTags: [],
|
||||
selectedTags: [],
|
||||
};
|
||||
|
||||
getInitialState() {
|
||||
return {
|
||||
orderedTags: [],
|
||||
selectedTags: [],
|
||||
};
|
||||
},
|
||||
|
||||
componentDidMount: function() {
|
||||
componentDidMount() {
|
||||
this.unmounted = false;
|
||||
this.context.on("Group.myMembership", this._onGroupMyMembership);
|
||||
this.context.on("sync", this._onClientSync);
|
||||
|
@ -62,7 +55,7 @@ const TagPanel = createReactClass({
|
|||
});
|
||||
// This could be done by anything with a matrix client
|
||||
dis.dispatch(GroupActions.fetchJoinedGroups(this.context));
|
||||
},
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
this.unmounted = true;
|
||||
|
@ -71,14 +64,14 @@ const TagPanel = createReactClass({
|
|||
if (this._tagOrderStoreToken) {
|
||||
this._tagOrderStoreToken.remove();
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
_onGroupMyMembership() {
|
||||
_onGroupMyMembership = () => {
|
||||
if (this.unmounted) return;
|
||||
dis.dispatch(GroupActions.fetchJoinedGroups(this.context));
|
||||
},
|
||||
};
|
||||
|
||||
_onClientSync(syncState, prevState) {
|
||||
_onClientSync = (syncState, prevState) => {
|
||||
// Consider the client reconnected if there is no error with syncing.
|
||||
// This means the state could be RECONNECTING, SYNCING, PREPARED or CATCHUP.
|
||||
const reconnected = syncState !== "ERROR" && prevState !== syncState;
|
||||
|
@ -86,18 +79,18 @@ const TagPanel = createReactClass({
|
|||
// Load joined groups
|
||||
dis.dispatch(GroupActions.fetchJoinedGroups(this.context));
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
onMouseDown(e) {
|
||||
onMouseDown = e => {
|
||||
// only dispatch if its not a no-op
|
||||
if (this.state.selectedTags.length > 0) {
|
||||
dis.dispatch({action: 'deselect_tags'});
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
onClearFilterClick(ev) {
|
||||
onClearFilterClick = ev => {
|
||||
dis.dispatch({action: 'deselect_tags'});
|
||||
},
|
||||
};
|
||||
|
||||
renderGlobalIcon() {
|
||||
if (!SettingsStore.getValue("feature_communities_v2_prototypes")) return null;
|
||||
|
@ -108,7 +101,7 @@ const TagPanel = createReactClass({
|
|||
<hr className="mx_TagPanel_divider" />
|
||||
</div>
|
||||
);
|
||||
},
|
||||
}
|
||||
|
||||
render() {
|
||||
const DNDTagTile = sdk.getComponent('elements.DNDTagTile');
|
||||
|
@ -173,6 +166,6 @@ const TagPanel = createReactClass({
|
|||
</Droppable>
|
||||
</AutoHideScrollbar>
|
||||
</div>;
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
export default TagPanel;
|
||||
|
|
|
@ -19,7 +19,6 @@ limitations under the License.
|
|||
|
||||
import SettingsStore from "../../settings/SettingsStore";
|
||||
import React, {createRef} from 'react';
|
||||
import createReactClass from 'create-react-class';
|
||||
import ReactDOM from "react-dom";
|
||||
import PropTypes from 'prop-types';
|
||||
import {EventTimeline} from "matrix-js-sdk";
|
||||
|
@ -54,10 +53,8 @@ if (DEBUG) {
|
|||
*
|
||||
* Also responsible for handling and sending read receipts.
|
||||
*/
|
||||
const TimelinePanel = createReactClass({
|
||||
displayName: 'TimelinePanel',
|
||||
|
||||
propTypes: {
|
||||
class TimelinePanel extends React.Component {
|
||||
static propTypes = {
|
||||
// The js-sdk EventTimelineSet object for the timeline sequence we are
|
||||
// representing. This may or may not have a room, depending on what it's
|
||||
// a timeline representing. If it has a room, we maintain RRs etc for
|
||||
|
@ -115,23 +112,28 @@ const TimelinePanel = createReactClass({
|
|||
|
||||
// whether to use the irc layout
|
||||
useIRCLayout: PropTypes.bool,
|
||||
},
|
||||
}
|
||||
|
||||
statics: {
|
||||
// a map from room id to read marker event timestamp
|
||||
roomReadMarkerTsMap: {},
|
||||
},
|
||||
// a map from room id to read marker event timestamp
|
||||
static roomReadMarkerTsMap = {};
|
||||
|
||||
getDefaultProps: function() {
|
||||
return {
|
||||
// By default, disable the timelineCap in favour of unpaginating based on
|
||||
// event tile heights. (See _unpaginateEvents)
|
||||
timelineCap: Number.MAX_VALUE,
|
||||
className: 'mx_RoomView_messagePanel',
|
||||
};
|
||||
},
|
||||
static defaultProps = {
|
||||
// By default, disable the timelineCap in favour of unpaginating based on
|
||||
// event tile heights. (See _unpaginateEvents)
|
||||
timelineCap: Number.MAX_VALUE,
|
||||
className: 'mx_RoomView_messagePanel',
|
||||
};
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
debuglog("TimelinePanel: mounting");
|
||||
|
||||
this.lastRRSentEventId = undefined;
|
||||
this.lastRMSentEventId = undefined;
|
||||
|
||||
this._messagePanel = createRef();
|
||||
|
||||
getInitialState: function() {
|
||||
// XXX: we could track RM per TimelineSet rather than per Room.
|
||||
// but for now we just do it per room for simplicity.
|
||||
let initialReadMarker = null;
|
||||
|
@ -144,7 +146,7 @@ const TimelinePanel = createReactClass({
|
|||
}
|
||||
}
|
||||
|
||||
return {
|
||||
this.state = {
|
||||
events: [],
|
||||
liveEvents: [],
|
||||
timelineLoading: true, // track whether our room timeline is loading
|
||||
|
@ -203,24 +205,6 @@ const TimelinePanel = createReactClass({
|
|||
// how long to show the RM for when it's scrolled off-screen
|
||||
readMarkerOutOfViewThresholdMs: SettingsStore.getValue("readMarkerOutOfViewThresholdMs"),
|
||||
};
|
||||
},
|
||||
|
||||
// TODO: [REACT-WARNING] Replace component with real class, use constructor for refs
|
||||
UNSAFE_componentWillMount: function() {
|
||||
debuglog("TimelinePanel: mounting");
|
||||
|
||||
this.lastRRSentEventId = undefined;
|
||||
this.lastRMSentEventId = undefined;
|
||||
|
||||
this._messagePanel = createRef();
|
||||
|
||||
if (this.props.manageReadReceipts) {
|
||||
this.updateReadReceiptOnUserActivity();
|
||||
}
|
||||
if (this.props.manageReadMarkers) {
|
||||
this.updateReadMarkerOnUserActivity();
|
||||
}
|
||||
|
||||
|
||||
this.dispatcherRef = dis.register(this.onAction);
|
||||
MatrixClientPeg.get().on("Room.timeline", this.onRoomTimeline);
|
||||
|
@ -234,12 +218,24 @@ const TimelinePanel = createReactClass({
|
|||
MatrixClientPeg.get().on("Event.decrypted", this.onEventDecrypted);
|
||||
MatrixClientPeg.get().on("Event.replaced", this.onEventReplaced);
|
||||
MatrixClientPeg.get().on("sync", this.onSync);
|
||||
}
|
||||
|
||||
// TODO: [REACT-WARNING] Move into constructor
|
||||
// eslint-disable-next-line camelcase
|
||||
UNSAFE_componentWillMount() {
|
||||
if (this.props.manageReadReceipts) {
|
||||
this.updateReadReceiptOnUserActivity();
|
||||
}
|
||||
if (this.props.manageReadMarkers) {
|
||||
this.updateReadMarkerOnUserActivity();
|
||||
}
|
||||
|
||||
this._initTimeline(this.props);
|
||||
},
|
||||
}
|
||||
|
||||
// TODO: [REACT-WARNING] Replace with appropriate lifecycle event
|
||||
UNSAFE_componentWillReceiveProps: function(newProps) {
|
||||
// eslint-disable-next-line camelcase
|
||||
UNSAFE_componentWillReceiveProps(newProps) {
|
||||
if (newProps.timelineSet !== this.props.timelineSet) {
|
||||
// throw new Error("changing timelineSet on a TimelinePanel is not supported");
|
||||
|
||||
|
@ -260,9 +256,9 @@ const TimelinePanel = createReactClass({
|
|||
" (was " + this.props.eventId + ")");
|
||||
return this._initTimeline(newProps);
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
shouldComponentUpdate: function(nextProps, nextState) {
|
||||
shouldComponentUpdate(nextProps, nextState) {
|
||||
if (!ObjectUtils.shallowEqual(this.props, nextProps)) {
|
||||
if (DEBUG) {
|
||||
console.group("Timeline.shouldComponentUpdate: props change");
|
||||
|
@ -284,9 +280,9 @@ const TimelinePanel = createReactClass({
|
|||
}
|
||||
|
||||
return false;
|
||||
},
|
||||
}
|
||||
|
||||
componentWillUnmount: function() {
|
||||
componentWillUnmount() {
|
||||
// set a boolean to say we've been unmounted, which any pending
|
||||
// promises can use to throw away their results.
|
||||
//
|
||||
|
@ -316,9 +312,9 @@ const TimelinePanel = createReactClass({
|
|||
client.removeListener("Event.replaced", this.onEventReplaced);
|
||||
client.removeListener("sync", this.onSync);
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
onMessageListUnfillRequest: function(backwards, scrollToken) {
|
||||
onMessageListUnfillRequest = (backwards, scrollToken) => {
|
||||
// If backwards, unpaginate from the back (i.e. the start of the timeline)
|
||||
const dir = backwards ? EventTimeline.BACKWARDS : EventTimeline.FORWARDS;
|
||||
debuglog("TimelinePanel: unpaginating events in direction", dir);
|
||||
|
@ -349,18 +345,18 @@ const TimelinePanel = createReactClass({
|
|||
firstVisibleEventIndex,
|
||||
});
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
onPaginationRequest(timelineWindow, direction, size) {
|
||||
onPaginationRequest = (timelineWindow, direction, size) => {
|
||||
if (this.props.onPaginationRequest) {
|
||||
return this.props.onPaginationRequest(timelineWindow, direction, size);
|
||||
} else {
|
||||
return timelineWindow.paginate(direction, size);
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
// set off a pagination request.
|
||||
onMessageListFillRequest: function(backwards) {
|
||||
onMessageListFillRequest = backwards => {
|
||||
if (!this._shouldPaginate()) return Promise.resolve(false);
|
||||
|
||||
const dir = backwards ? EventTimeline.BACKWARDS : EventTimeline.FORWARDS;
|
||||
|
@ -425,9 +421,9 @@ const TimelinePanel = createReactClass({
|
|||
});
|
||||
});
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
onMessageListScroll: function(e) {
|
||||
onMessageListScroll = e => {
|
||||
if (this.props.onScroll) {
|
||||
this.props.onScroll(e);
|
||||
}
|
||||
|
@ -447,9 +443,9 @@ const TimelinePanel = createReactClass({
|
|||
// NO-OP when timeout already has set to the given value
|
||||
this._readMarkerActivityTimer.changeTimeout(timeout);
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
onAction: function(payload) {
|
||||
onAction = payload => {
|
||||
if (payload.action === 'ignore_state_changed') {
|
||||
this.forceUpdate();
|
||||
}
|
||||
|
@ -463,9 +459,9 @@ const TimelinePanel = createReactClass({
|
|||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
onRoomTimeline: function(ev, room, toStartOfTimeline, removed, data) {
|
||||
onRoomTimeline = (ev, room, toStartOfTimeline, removed, data) => {
|
||||
// ignore events for other timeline sets
|
||||
if (data.timeline.getTimelineSet() !== this.props.timelineSet) return;
|
||||
|
||||
|
@ -537,21 +533,19 @@ const TimelinePanel = createReactClass({
|
|||
}
|
||||
});
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
onRoomTimelineReset: function(room, timelineSet) {
|
||||
onRoomTimelineReset = (room, timelineSet) => {
|
||||
if (timelineSet !== this.props.timelineSet) return;
|
||||
|
||||
if (this._messagePanel.current && this._messagePanel.current.isAtBottom()) {
|
||||
this._loadTimeline();
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
canResetTimeline: function() {
|
||||
return this._messagePanel.current && this._messagePanel.current.isAtBottom();
|
||||
},
|
||||
canResetTimeline = () => this._messagePanel.current && this._messagePanel.current.isAtBottom();
|
||||
|
||||
onRoomRedaction: function(ev, room) {
|
||||
onRoomRedaction = (ev, room) => {
|
||||
if (this.unmounted) return;
|
||||
|
||||
// ignore events for other rooms
|
||||
|
@ -560,9 +554,9 @@ const TimelinePanel = createReactClass({
|
|||
// we could skip an update if the event isn't in our timeline,
|
||||
// but that's probably an early optimisation.
|
||||
this.forceUpdate();
|
||||
},
|
||||
};
|
||||
|
||||
onEventReplaced: function(replacedEvent, room) {
|
||||
onEventReplaced = (replacedEvent, room) => {
|
||||
if (this.unmounted) return;
|
||||
|
||||
// ignore events for other rooms
|
||||
|
@ -571,27 +565,27 @@ const TimelinePanel = createReactClass({
|
|||
// we could skip an update if the event isn't in our timeline,
|
||||
// but that's probably an early optimisation.
|
||||
this.forceUpdate();
|
||||
},
|
||||
};
|
||||
|
||||
onRoomReceipt: function(ev, room) {
|
||||
onRoomReceipt = (ev, room) => {
|
||||
if (this.unmounted) return;
|
||||
|
||||
// ignore events for other rooms
|
||||
if (room !== this.props.timelineSet.room) return;
|
||||
|
||||
this.forceUpdate();
|
||||
},
|
||||
};
|
||||
|
||||
onLocalEchoUpdated: function(ev, room, oldEventId) {
|
||||
onLocalEchoUpdated = (ev, room, oldEventId) => {
|
||||
if (this.unmounted) return;
|
||||
|
||||
// ignore events for other rooms
|
||||
if (room !== this.props.timelineSet.room) return;
|
||||
|
||||
this._reloadEvents();
|
||||
},
|
||||
};
|
||||
|
||||
onAccountData: function(ev, room) {
|
||||
onAccountData = (ev, room) => {
|
||||
if (this.unmounted) return;
|
||||
|
||||
// ignore events for other rooms
|
||||
|
@ -605,9 +599,9 @@ const TimelinePanel = createReactClass({
|
|||
this.setState({
|
||||
readMarkerEventId: ev.getContent().event_id,
|
||||
}, this.props.onReadMarkerUpdated);
|
||||
},
|
||||
};
|
||||
|
||||
onEventDecrypted: function(ev) {
|
||||
onEventDecrypted = ev => {
|
||||
// Can be null for the notification timeline, etc.
|
||||
if (!this.props.timelineSet.room) return;
|
||||
|
||||
|
@ -620,19 +614,19 @@ const TimelinePanel = createReactClass({
|
|||
if (ev.getRoomId() === this.props.timelineSet.room.roomId) {
|
||||
this.forceUpdate();
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
onSync: function(state, prevState, data) {
|
||||
onSync = (state, prevState, data) => {
|
||||
this.setState({clientSyncState: state});
|
||||
},
|
||||
};
|
||||
|
||||
_readMarkerTimeout(readMarkerPosition) {
|
||||
return readMarkerPosition === 0 ?
|
||||
this.state.readMarkerInViewThresholdMs :
|
||||
this.state.readMarkerOutOfViewThresholdMs;
|
||||
},
|
||||
}
|
||||
|
||||
updateReadMarkerOnUserActivity: async function() {
|
||||
async updateReadMarkerOnUserActivity() {
|
||||
const initialTimeout = this._readMarkerTimeout(this.getReadMarkerPosition());
|
||||
this._readMarkerActivityTimer = new Timer(initialTimeout);
|
||||
|
||||
|
@ -644,9 +638,9 @@ const TimelinePanel = createReactClass({
|
|||
// outside of try/catch to not swallow errors
|
||||
this.updateReadMarker();
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
updateReadReceiptOnUserActivity: async function() {
|
||||
async updateReadReceiptOnUserActivity() {
|
||||
this._readReceiptActivityTimer = new Timer(READ_RECEIPT_INTERVAL_MS);
|
||||
while (this._readReceiptActivityTimer) { //unset on unmount
|
||||
UserActivity.sharedInstance().timeWhileActiveNow(this._readReceiptActivityTimer);
|
||||
|
@ -656,9 +650,9 @@ const TimelinePanel = createReactClass({
|
|||
// outside of try/catch to not swallow errors
|
||||
this.sendReadReceipt();
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
sendReadReceipt: function() {
|
||||
sendReadReceipt = () => {
|
||||
if (SettingsStore.getValue("lowBandwidth")) return;
|
||||
|
||||
if (!this._messagePanel.current) return;
|
||||
|
@ -766,11 +760,11 @@ const TimelinePanel = createReactClass({
|
|||
});
|
||||
}
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
// if the read marker is on the screen, we can now assume we've caught up to the end
|
||||
// of the screen, so move the marker down to the bottom of the screen.
|
||||
updateReadMarker: function() {
|
||||
updateReadMarker = () => {
|
||||
if (!this.props.manageReadMarkers) return;
|
||||
if (this.getReadMarkerPosition() === 1) {
|
||||
// the read marker is at an event below the viewport,
|
||||
|
@ -801,11 +795,11 @@ const TimelinePanel = createReactClass({
|
|||
|
||||
// Send the updated read marker (along with read receipt) to the server
|
||||
this.sendReadReceipt();
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
// advance the read marker past any events we sent ourselves.
|
||||
_advanceReadMarkerPastMyEvents: function() {
|
||||
_advanceReadMarkerPastMyEvents() {
|
||||
if (!this.props.manageReadMarkers) return;
|
||||
|
||||
// we call `_timelineWindow.getEvents()` rather than using
|
||||
|
@ -837,11 +831,11 @@ const TimelinePanel = createReactClass({
|
|||
|
||||
const ev = events[i];
|
||||
this._setReadMarker(ev.getId(), ev.getTs());
|
||||
},
|
||||
}
|
||||
|
||||
/* jump down to the bottom of this room, where new events are arriving
|
||||
*/
|
||||
jumpToLiveTimeline: function() {
|
||||
jumpToLiveTimeline = () => {
|
||||
// if we can't forward-paginate the existing timeline, then there
|
||||
// is no point reloading it - just jump straight to the bottom.
|
||||
//
|
||||
|
@ -854,12 +848,12 @@ const TimelinePanel = createReactClass({
|
|||
this._messagePanel.current.scrollToBottom();
|
||||
}
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
/* scroll to show the read-up-to marker. We put it 1/3 of the way down
|
||||
* the container.
|
||||
*/
|
||||
jumpToReadMarker: function() {
|
||||
jumpToReadMarker = () => {
|
||||
if (!this.props.manageReadMarkers) return;
|
||||
if (!this._messagePanel.current) return;
|
||||
if (!this.state.readMarkerEventId) return;
|
||||
|
@ -883,11 +877,11 @@ const TimelinePanel = createReactClass({
|
|||
// As with jumpToLiveTimeline, we want to reload the timeline around the
|
||||
// read-marker.
|
||||
this._loadTimeline(this.state.readMarkerEventId, 0, 1/3);
|
||||
},
|
||||
};
|
||||
|
||||
/* update the read-up-to marker to match the read receipt
|
||||
*/
|
||||
forgetReadMarker: function() {
|
||||
forgetReadMarker = () => {
|
||||
if (!this.props.manageReadMarkers) return;
|
||||
|
||||
const rmId = this._getCurrentReadReceipt();
|
||||
|
@ -903,17 +897,17 @@ const TimelinePanel = createReactClass({
|
|||
}
|
||||
|
||||
this._setReadMarker(rmId, rmTs);
|
||||
},
|
||||
};
|
||||
|
||||
/* return true if the content is fully scrolled down and we are
|
||||
* at the end of the live timeline.
|
||||
*/
|
||||
isAtEndOfLiveTimeline: function() {
|
||||
isAtEndOfLiveTimeline = () => {
|
||||
return this._messagePanel.current
|
||||
&& this._messagePanel.current.isAtBottom()
|
||||
&& this._timelineWindow
|
||||
&& !this._timelineWindow.canPaginate(EventTimeline.FORWARDS);
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
/* get the current scroll state. See ScrollPanel.getScrollState for
|
||||
|
@ -921,10 +915,10 @@ const TimelinePanel = createReactClass({
|
|||
*
|
||||
* returns null if we are not mounted.
|
||||
*/
|
||||
getScrollState: function() {
|
||||
getScrollState = () => {
|
||||
if (!this._messagePanel.current) { return null; }
|
||||
return this._messagePanel.current.getScrollState();
|
||||
},
|
||||
};
|
||||
|
||||
// returns one of:
|
||||
//
|
||||
|
@ -932,7 +926,7 @@ const TimelinePanel = createReactClass({
|
|||
// -1: read marker is above the window
|
||||
// 0: read marker is visible
|
||||
// +1: read marker is below the window
|
||||
getReadMarkerPosition: function() {
|
||||
getReadMarkerPosition = () => {
|
||||
if (!this.props.manageReadMarkers) return null;
|
||||
if (!this._messagePanel.current) return null;
|
||||
|
||||
|
@ -953,9 +947,9 @@ const TimelinePanel = createReactClass({
|
|||
}
|
||||
|
||||
return null;
|
||||
},
|
||||
};
|
||||
|
||||
canJumpToReadMarker: function() {
|
||||
canJumpToReadMarker = () => {
|
||||
// 1. Do not show jump bar if neither the RM nor the RR are set.
|
||||
// 3. We want to show the bar if the read-marker is off the top of the screen.
|
||||
// 4. Also, if pos === null, the event might not be paginated - show the unread bar
|
||||
|
@ -963,14 +957,14 @@ const TimelinePanel = createReactClass({
|
|||
const ret = this.state.readMarkerEventId !== null && // 1.
|
||||
(pos < 0 || pos === null); // 3., 4.
|
||||
return ret;
|
||||
},
|
||||
};
|
||||
|
||||
/*
|
||||
* called by the parent component when PageUp/Down/etc is pressed.
|
||||
*
|
||||
* We pass it down to the scroll panel.
|
||||
*/
|
||||
handleScrollKey: function(ev) {
|
||||
handleScrollKey = ev => {
|
||||
if (!this._messagePanel.current) { return; }
|
||||
|
||||
// jump to the live timeline on ctrl-end, rather than the end of the
|
||||
|
@ -980,9 +974,9 @@ const TimelinePanel = createReactClass({
|
|||
} else {
|
||||
this._messagePanel.current.handleScrollKey(ev);
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
_initTimeline: function(props) {
|
||||
_initTimeline(props) {
|
||||
const initialEvent = props.eventId;
|
||||
const pixelOffset = props.eventPixelOffset;
|
||||
|
||||
|
@ -994,7 +988,7 @@ const TimelinePanel = createReactClass({
|
|||
}
|
||||
|
||||
return this._loadTimeline(initialEvent, pixelOffset, offsetBase);
|
||||
},
|
||||
}
|
||||
|
||||
/**
|
||||
* (re)-load the event timeline, and initialise the scroll state, centered
|
||||
|
@ -1012,7 +1006,7 @@ const TimelinePanel = createReactClass({
|
|||
*
|
||||
* returns a promise which will resolve when the load completes.
|
||||
*/
|
||||
_loadTimeline: function(eventId, pixelOffset, offsetBase) {
|
||||
_loadTimeline(eventId, pixelOffset, offsetBase) {
|
||||
this._timelineWindow = new Matrix.TimelineWindow(
|
||||
MatrixClientPeg.get(), this.props.timelineSet,
|
||||
{windowLimit: this.props.timelineCap});
|
||||
|
@ -1122,21 +1116,21 @@ const TimelinePanel = createReactClass({
|
|||
});
|
||||
prom.then(onLoaded, onError);
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
// handle the completion of a timeline load or localEchoUpdate, by
|
||||
// reloading the events from the timelinewindow and pending event list into
|
||||
// the state.
|
||||
_reloadEvents: function() {
|
||||
_reloadEvents() {
|
||||
// we might have switched rooms since the load started - just bin
|
||||
// the results if so.
|
||||
if (this.unmounted) return;
|
||||
|
||||
this.setState(this._getEvents());
|
||||
},
|
||||
}
|
||||
|
||||
// get the list of events from the timeline window and the pending event list
|
||||
_getEvents: function() {
|
||||
_getEvents() {
|
||||
const events = this._timelineWindow.getEvents();
|
||||
const firstVisibleEventIndex = this._checkForPreJoinUISI(events);
|
||||
|
||||
|
@ -1154,7 +1148,7 @@ const TimelinePanel = createReactClass({
|
|||
liveEvents,
|
||||
firstVisibleEventIndex,
|
||||
};
|
||||
},
|
||||
}
|
||||
|
||||
/**
|
||||
* Check for undecryptable messages that were sent while the user was not in
|
||||
|
@ -1166,7 +1160,7 @@ const TimelinePanel = createReactClass({
|
|||
* undecryptable event that was sent while the user was not in the room. If no
|
||||
* such events were found, then it returns 0.
|
||||
*/
|
||||
_checkForPreJoinUISI: function(events) {
|
||||
_checkForPreJoinUISI(events) {
|
||||
const room = this.props.timelineSet.room;
|
||||
|
||||
if (events.length === 0 || !room ||
|
||||
|
@ -1228,18 +1222,18 @@ const TimelinePanel = createReactClass({
|
|||
}
|
||||
}
|
||||
return 0;
|
||||
},
|
||||
}
|
||||
|
||||
_indexForEventId: function(evId) {
|
||||
_indexForEventId(evId) {
|
||||
for (let i = 0; i < this.state.events.length; ++i) {
|
||||
if (evId == this.state.events[i].getId()) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
},
|
||||
}
|
||||
|
||||
_getLastDisplayedEventIndex: function(opts) {
|
||||
_getLastDisplayedEventIndex(opts) {
|
||||
opts = opts || {};
|
||||
const ignoreOwn = opts.ignoreOwn || false;
|
||||
const allowPartial = opts.allowPartial || false;
|
||||
|
@ -1313,7 +1307,7 @@ const TimelinePanel = createReactClass({
|
|||
}
|
||||
|
||||
return null;
|
||||
},
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the id of the event corresponding to our user's latest read-receipt.
|
||||
|
@ -1324,7 +1318,7 @@ const TimelinePanel = createReactClass({
|
|||
* SDK.
|
||||
* @return {String} the event ID
|
||||
*/
|
||||
_getCurrentReadReceipt: function(ignoreSynthesized) {
|
||||
_getCurrentReadReceipt(ignoreSynthesized) {
|
||||
const client = MatrixClientPeg.get();
|
||||
// the client can be null on logout
|
||||
if (client == null) {
|
||||
|
@ -1333,9 +1327,9 @@ const TimelinePanel = createReactClass({
|
|||
|
||||
const myUserId = client.credentials.userId;
|
||||
return this.props.timelineSet.room.getEventReadUpTo(myUserId, ignoreSynthesized);
|
||||
},
|
||||
}
|
||||
|
||||
_setReadMarker: function(eventId, eventTs, inhibitSetState) {
|
||||
_setReadMarker(eventId, eventTs, inhibitSetState) {
|
||||
const roomId = this.props.timelineSet.room.roomId;
|
||||
|
||||
// don't update the state (and cause a re-render) if there is
|
||||
|
@ -1358,9 +1352,9 @@ const TimelinePanel = createReactClass({
|
|||
this.setState({
|
||||
readMarkerEventId: eventId,
|
||||
}, this.props.onReadMarkerUpdated);
|
||||
},
|
||||
}
|
||||
|
||||
_shouldPaginate: function() {
|
||||
_shouldPaginate() {
|
||||
// don't try to paginate while events in the timeline are
|
||||
// still being decrypted. We don't render events while they're
|
||||
// being decrypted, so they don't take up space in the timeline.
|
||||
|
@ -1369,13 +1363,11 @@ const TimelinePanel = createReactClass({
|
|||
return !this.state.events.some((e) => {
|
||||
return e.isBeingDecrypted();
|
||||
});
|
||||
},
|
||||
}
|
||||
|
||||
getRelationsForEvent(...args) {
|
||||
return this.props.timelineSet.getRelationsForEvent(...args);
|
||||
},
|
||||
getRelationsForEvent = (...args) => this.props.timelineSet.getRelationsForEvent(...args);
|
||||
|
||||
render: function() {
|
||||
render() {
|
||||
const MessagePanel = sdk.getComponent("structures.MessagePanel");
|
||||
const Loader = sdk.getComponent("elements.Spinner");
|
||||
|
||||
|
@ -1456,7 +1448,7 @@ const TimelinePanel = createReactClass({
|
|||
useIRCLayout={this.props.useIRCLayout}
|
||||
/>
|
||||
);
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export default TimelinePanel;
|
||||
|
|
|
@ -16,30 +16,28 @@ limitations under the License.
|
|||
*/
|
||||
|
||||
import React from 'react';
|
||||
import createReactClass from 'create-react-class';
|
||||
import PropTypes from 'prop-types';
|
||||
import ContentMessages from '../../ContentMessages';
|
||||
import dis from "../../dispatcher/dispatcher";
|
||||
import filesize from "filesize";
|
||||
import { _t } from '../../languageHandler';
|
||||
|
||||
export default createReactClass({
|
||||
displayName: 'UploadBar',
|
||||
propTypes: {
|
||||
export default class UploadBar extends React.Component {
|
||||
static propTypes = {
|
||||
room: PropTypes.object,
|
||||
},
|
||||
};
|
||||
|
||||
componentDidMount: function() {
|
||||
componentDidMount() {
|
||||
this.dispatcherRef = dis.register(this.onAction);
|
||||
this.mounted = true;
|
||||
},
|
||||
}
|
||||
|
||||
componentWillUnmount: function() {
|
||||
componentWillUnmount() {
|
||||
this.mounted = false;
|
||||
dis.unregister(this.dispatcherRef);
|
||||
},
|
||||
}
|
||||
|
||||
onAction: function(payload) {
|
||||
onAction = payload => {
|
||||
switch (payload.action) {
|
||||
case 'upload_progress':
|
||||
case 'upload_finished':
|
||||
|
@ -48,9 +46,9 @@ export default createReactClass({
|
|||
if (this.mounted) this.forceUpdate();
|
||||
break;
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
render: function() {
|
||||
render() {
|
||||
const uploads = ContentMessages.sharedInstance().getCurrentUploads();
|
||||
|
||||
// for testing UI... - also fix up the ContentMessages.getCurrentUploads().length
|
||||
|
@ -105,5 +103,5 @@ export default createReactClass({
|
|||
<div className="mx_UploadBar_uploadFilename">{ uploadText }</div>
|
||||
</div>
|
||||
);
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,24 +17,21 @@ limitations under the License.
|
|||
*/
|
||||
|
||||
import React from 'react';
|
||||
import createReactClass from 'create-react-class';
|
||||
import PropTypes from 'prop-types';
|
||||
import SyntaxHighlight from '../views/elements/SyntaxHighlight';
|
||||
import {_t} from "../../languageHandler";
|
||||
import * as sdk from "../../index";
|
||||
|
||||
|
||||
export default createReactClass({
|
||||
displayName: 'ViewSource',
|
||||
|
||||
propTypes: {
|
||||
export default class ViewSource extends React.Component {
|
||||
static propTypes = {
|
||||
content: PropTypes.object.isRequired,
|
||||
onFinished: PropTypes.func.isRequired,
|
||||
roomId: PropTypes.string.isRequired,
|
||||
eventId: PropTypes.string.isRequired,
|
||||
},
|
||||
};
|
||||
|
||||
render: function() {
|
||||
render() {
|
||||
const BaseDialog = sdk.getComponent('views.dialogs.BaseDialog');
|
||||
return (
|
||||
<BaseDialog className="mx_ViewSource" onFinished={this.props.onFinished} title={_t('View Source')}>
|
||||
|
@ -49,5 +46,5 @@ export default createReactClass({
|
|||
</div>
|
||||
</BaseDialog>
|
||||
);
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,7 +17,6 @@ limitations under the License.
|
|||
*/
|
||||
|
||||
import React from 'react';
|
||||
import createReactClass from 'create-react-class';
|
||||
import PropTypes from 'prop-types';
|
||||
import { _t } from '../../../languageHandler';
|
||||
import * as sdk from '../../../index';
|
||||
|
@ -40,50 +39,47 @@ const PHASE_EMAIL_SENT = 3;
|
|||
// User has clicked the link in email and completed reset
|
||||
const PHASE_DONE = 4;
|
||||
|
||||
export default createReactClass({
|
||||
displayName: 'ForgotPassword',
|
||||
|
||||
propTypes: {
|
||||
export default class ForgotPassword extends React.Component {
|
||||
static propTypes = {
|
||||
serverConfig: PropTypes.instanceOf(ValidatedServerConfig).isRequired,
|
||||
onServerConfigChange: PropTypes.func.isRequired,
|
||||
onLoginClick: PropTypes.func,
|
||||
onComplete: PropTypes.func.isRequired,
|
||||
},
|
||||
};
|
||||
|
||||
getInitialState: function() {
|
||||
return {
|
||||
phase: PHASE_FORGOT,
|
||||
email: "",
|
||||
password: "",
|
||||
password2: "",
|
||||
errorText: null,
|
||||
state = {
|
||||
phase: PHASE_FORGOT,
|
||||
email: "",
|
||||
password: "",
|
||||
password2: "",
|
||||
errorText: null,
|
||||
|
||||
// We perform liveliness checks later, but for now suppress the errors.
|
||||
// We also track the server dead errors independently of the regular errors so
|
||||
// that we can render it differently, and override any other error the user may
|
||||
// be seeing.
|
||||
serverIsAlive: true,
|
||||
serverErrorIsFatal: false,
|
||||
serverDeadError: "",
|
||||
serverRequiresIdServer: null,
|
||||
};
|
||||
},
|
||||
// We perform liveliness checks later, but for now suppress the errors.
|
||||
// We also track the server dead errors independently of the regular errors so
|
||||
// that we can render it differently, and override any other error the user may
|
||||
// be seeing.
|
||||
serverIsAlive: true,
|
||||
serverErrorIsFatal: false,
|
||||
serverDeadError: "",
|
||||
serverRequiresIdServer: null,
|
||||
};
|
||||
|
||||
componentDidMount: function() {
|
||||
componentDidMount() {
|
||||
this.reset = null;
|
||||
this._checkServerLiveliness(this.props.serverConfig);
|
||||
},
|
||||
}
|
||||
|
||||
// TODO: [REACT-WARNING] Replace with appropriate lifecycle event
|
||||
UNSAFE_componentWillReceiveProps: function(newProps) {
|
||||
// eslint-disable-next-line camelcase
|
||||
UNSAFE_componentWillReceiveProps(newProps) {
|
||||
if (newProps.serverConfig.hsUrl === this.props.serverConfig.hsUrl &&
|
||||
newProps.serverConfig.isUrl === this.props.serverConfig.isUrl) return;
|
||||
|
||||
// Do a liveliness check on the new URLs
|
||||
this._checkServerLiveliness(newProps.serverConfig);
|
||||
},
|
||||
}
|
||||
|
||||
_checkServerLiveliness: async function(serverConfig) {
|
||||
async _checkServerLiveliness(serverConfig) {
|
||||
try {
|
||||
await AutoDiscoveryUtils.validateServerConfigWithStaticUrls(
|
||||
serverConfig.hsUrl,
|
||||
|
@ -100,9 +96,9 @@ export default createReactClass({
|
|||
} catch (e) {
|
||||
this.setState(AutoDiscoveryUtils.authComponentStateForError(e, "forgot_password"));
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
submitPasswordReset: function(email, password) {
|
||||
submitPasswordReset(email, password) {
|
||||
this.setState({
|
||||
phase: PHASE_SENDING_EMAIL,
|
||||
});
|
||||
|
@ -117,9 +113,9 @@ export default createReactClass({
|
|||
phase: PHASE_FORGOT,
|
||||
});
|
||||
});
|
||||
},
|
||||
}
|
||||
|
||||
onVerify: async function(ev) {
|
||||
onVerify = async ev => {
|
||||
ev.preventDefault();
|
||||
if (!this.reset) {
|
||||
console.error("onVerify called before submitPasswordReset!");
|
||||
|
@ -131,9 +127,9 @@ export default createReactClass({
|
|||
} catch (err) {
|
||||
this.showErrorDialog(err.message);
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
onSubmitForm: async function(ev) {
|
||||
onSubmitForm = async ev => {
|
||||
ev.preventDefault();
|
||||
|
||||
// refresh the server errors, just in case the server came back online
|
||||
|
@ -166,41 +162,41 @@ export default createReactClass({
|
|||
},
|
||||
});
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
onInputChanged: function(stateKey, ev) {
|
||||
onInputChanged = (stateKey, ev) => {
|
||||
this.setState({
|
||||
[stateKey]: ev.target.value,
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
async onServerDetailsNextPhaseClick() {
|
||||
onServerDetailsNextPhaseClick = async () => {
|
||||
this.setState({
|
||||
phase: PHASE_FORGOT,
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
onEditServerDetailsClick(ev) {
|
||||
onEditServerDetailsClick = ev => {
|
||||
ev.preventDefault();
|
||||
ev.stopPropagation();
|
||||
this.setState({
|
||||
phase: PHASE_SERVER_DETAILS,
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
onLoginClick: function(ev) {
|
||||
onLoginClick = ev => {
|
||||
ev.preventDefault();
|
||||
ev.stopPropagation();
|
||||
this.props.onLoginClick();
|
||||
},
|
||||
};
|
||||
|
||||
showErrorDialog: function(body, title) {
|
||||
showErrorDialog(body, title) {
|
||||
const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
|
||||
Modal.createTrackedDialog('Forgot Password Error', '', ErrorDialog, {
|
||||
title: title,
|
||||
description: body,
|
||||
});
|
||||
},
|
||||
}
|
||||
|
||||
renderServerDetails() {
|
||||
const ServerConfig = sdk.getComponent("auth.ServerConfig");
|
||||
|
@ -218,7 +214,7 @@ export default createReactClass({
|
|||
submitText={_t("Next")}
|
||||
submitClass="mx_Login_submit"
|
||||
/>;
|
||||
},
|
||||
}
|
||||
|
||||
renderForgot() {
|
||||
const Field = sdk.getComponent('elements.Field');
|
||||
|
@ -335,12 +331,12 @@ export default createReactClass({
|
|||
{_t('Sign in instead')}
|
||||
</a>
|
||||
</div>;
|
||||
},
|
||||
}
|
||||
|
||||
renderSendingEmail() {
|
||||
const Spinner = sdk.getComponent("elements.Spinner");
|
||||
return <Spinner />;
|
||||
},
|
||||
}
|
||||
|
||||
renderEmailSent() {
|
||||
return <div>
|
||||
|
@ -350,7 +346,7 @@ export default createReactClass({
|
|||
<input className="mx_Login_submit" type="button" onClick={this.onVerify}
|
||||
value={_t('I have verified my email address')} />
|
||||
</div>;
|
||||
},
|
||||
}
|
||||
|
||||
renderDone() {
|
||||
return <div>
|
||||
|
@ -363,9 +359,9 @@ export default createReactClass({
|
|||
<input className="mx_Login_submit" type="button" onClick={this.props.onComplete}
|
||||
value={_t('Return to login screen')} />
|
||||
</div>;
|
||||
},
|
||||
}
|
||||
|
||||
render: function() {
|
||||
render() {
|
||||
const AuthHeader = sdk.getComponent("auth.AuthHeader");
|
||||
const AuthBody = sdk.getComponent("auth.AuthBody");
|
||||
|
||||
|
@ -397,5 +393,5 @@ export default createReactClass({
|
|||
</AuthBody>
|
||||
</AuthPage>
|
||||
);
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,7 +17,6 @@ limitations under the License.
|
|||
*/
|
||||
|
||||
import React from 'react';
|
||||
import createReactClass from 'create-react-class';
|
||||
import PropTypes from 'prop-types';
|
||||
import {_t, _td} from '../../../languageHandler';
|
||||
import * as sdk from '../../../index';
|
||||
|
@ -53,13 +52,11 @@ _td("Invalid base_url for m.identity_server");
|
|||
_td("Identity server URL does not appear to be a valid identity server");
|
||||
_td("General failure");
|
||||
|
||||
/**
|
||||
/*
|
||||
* A wire component which glues together login UI components and Login logic
|
||||
*/
|
||||
export default createReactClass({
|
||||
displayName: 'Login',
|
||||
|
||||
propTypes: {
|
||||
export default class LoginComponent extends React.Component {
|
||||
static propTypes = {
|
||||
// Called when the user has logged in. Params:
|
||||
// - The object returned by the login API
|
||||
// - The user's password, if applicable, (may be cached in memory for a
|
||||
|
@ -85,10 +82,14 @@ export default createReactClass({
|
|||
|
||||
serverConfig: PropTypes.instanceOf(ValidatedServerConfig).isRequired,
|
||||
isSyncing: PropTypes.bool,
|
||||
},
|
||||
};
|
||||
|
||||
getInitialState: function() {
|
||||
return {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
this._unmounted = false;
|
||||
|
||||
this.state = {
|
||||
busy: false,
|
||||
busyLoggingIn: null,
|
||||
errorText: null,
|
||||
|
@ -113,11 +114,6 @@ export default createReactClass({
|
|||
serverErrorIsFatal: false,
|
||||
serverDeadError: "",
|
||||
};
|
||||
},
|
||||
|
||||
// TODO: [REACT-WARNING] Move this to constructor
|
||||
UNSAFE_componentWillMount: function() {
|
||||
this._unmounted = false;
|
||||
|
||||
// map from login step type to a function which will render a control
|
||||
// letting you do that login type
|
||||
|
@ -130,33 +126,32 @@ export default createReactClass({
|
|||
};
|
||||
|
||||
this._initLoginLogic();
|
||||
},
|
||||
}
|
||||
|
||||
componentWillUnmount: function() {
|
||||
componentWillUnmount() {
|
||||
this._unmounted = true;
|
||||
},
|
||||
}
|
||||
|
||||
// TODO: [REACT-WARNING] Replace with appropriate lifecycle event
|
||||
// eslint-disable-next-line camelcase
|
||||
UNSAFE_componentWillReceiveProps(newProps) {
|
||||
if (newProps.serverConfig.hsUrl === this.props.serverConfig.hsUrl &&
|
||||
newProps.serverConfig.isUrl === this.props.serverConfig.isUrl) return;
|
||||
|
||||
// Ensure that we end up actually logging in to the right place
|
||||
this._initLoginLogic(newProps.serverConfig.hsUrl, newProps.serverConfig.isUrl);
|
||||
},
|
||||
}
|
||||
|
||||
onPasswordLoginError: function(errorText) {
|
||||
onPasswordLoginError = errorText => {
|
||||
this.setState({
|
||||
errorText,
|
||||
loginIncorrect: Boolean(errorText),
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
isBusy: function() {
|
||||
return this.state.busy || this.props.busy;
|
||||
},
|
||||
isBusy = () => this.state.busy || this.props.busy;
|
||||
|
||||
onPasswordLogin: async function(username, phoneCountry, phoneNumber, password) {
|
||||
onPasswordLogin = async (username, phoneCountry, phoneNumber, password) => {
|
||||
if (!this.state.serverIsAlive) {
|
||||
this.setState({busy: true});
|
||||
// Do a quick liveliness check on the URLs
|
||||
|
@ -263,13 +258,13 @@ export default createReactClass({
|
|||
loginIncorrect: error.httpStatus === 401 || error.httpStatus === 403,
|
||||
});
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
onUsernameChanged: function(username) {
|
||||
onUsernameChanged = username => {
|
||||
this.setState({ username: username });
|
||||
},
|
||||
};
|
||||
|
||||
onUsernameBlur: async function(username) {
|
||||
onUsernameBlur = async username => {
|
||||
const doWellknownLookup = username[0] === "@";
|
||||
this.setState({
|
||||
username: username,
|
||||
|
@ -314,19 +309,19 @@ export default createReactClass({
|
|||
});
|
||||
}
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
onPhoneCountryChanged: function(phoneCountry) {
|
||||
onPhoneCountryChanged = phoneCountry => {
|
||||
this.setState({ phoneCountry: phoneCountry });
|
||||
},
|
||||
};
|
||||
|
||||
onPhoneNumberChanged: function(phoneNumber) {
|
||||
onPhoneNumberChanged = phoneNumber => {
|
||||
this.setState({
|
||||
phoneNumber: phoneNumber,
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
onPhoneNumberBlur: function(phoneNumber) {
|
||||
onPhoneNumberBlur = phoneNumber => {
|
||||
// Validate the phone number entered
|
||||
if (!PHONE_NUMBER_REGEX.test(phoneNumber)) {
|
||||
this.setState({
|
||||
|
@ -339,15 +334,15 @@ export default createReactClass({
|
|||
canTryLogin: true,
|
||||
});
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
onRegisterClick: function(ev) {
|
||||
onRegisterClick = ev => {
|
||||
ev.preventDefault();
|
||||
ev.stopPropagation();
|
||||
this.props.onRegisterClick();
|
||||
},
|
||||
};
|
||||
|
||||
onTryRegisterClick: function(ev) {
|
||||
onTryRegisterClick = ev => {
|
||||
const step = this._getCurrentFlowStep();
|
||||
if (step === 'm.login.sso' || step === 'm.login.cas') {
|
||||
// If we're showing SSO it means that registration is also probably disabled,
|
||||
|
@ -361,23 +356,23 @@ export default createReactClass({
|
|||
// Don't intercept - just go through to the register page
|
||||
this.onRegisterClick(ev);
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
async onServerDetailsNextPhaseClick() {
|
||||
onServerDetailsNextPhaseClick = () => {
|
||||
this.setState({
|
||||
phase: PHASE_LOGIN,
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
onEditServerDetailsClick(ev) {
|
||||
onEditServerDetailsClick = ev => {
|
||||
ev.preventDefault();
|
||||
ev.stopPropagation();
|
||||
this.setState({
|
||||
phase: PHASE_SERVER_DETAILS,
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
_initLoginLogic: async function(hsUrl, isUrl) {
|
||||
async _initLoginLogic(hsUrl, isUrl) {
|
||||
hsUrl = hsUrl || this.props.serverConfig.hsUrl;
|
||||
isUrl = isUrl || this.props.serverConfig.isUrl;
|
||||
|
||||
|
@ -465,9 +460,9 @@ export default createReactClass({
|
|||
busy: false,
|
||||
});
|
||||
});
|
||||
},
|
||||
}
|
||||
|
||||
_isSupportedFlow: function(flow) {
|
||||
_isSupportedFlow(flow) {
|
||||
// technically the flow can have multiple steps, but no one does this
|
||||
// for login and loginLogic doesn't support it so we can ignore it.
|
||||
if (!this._stepRendererMap[flow.type]) {
|
||||
|
@ -475,11 +470,11 @@ export default createReactClass({
|
|||
return false;
|
||||
}
|
||||
return true;
|
||||
},
|
||||
}
|
||||
|
||||
_getCurrentFlowStep: function() {
|
||||
_getCurrentFlowStep() {
|
||||
return this._loginLogic ? this._loginLogic.getCurrentFlowStep() : null;
|
||||
},
|
||||
}
|
||||
|
||||
_errorTextFromError(err) {
|
||||
let errCode = err.errcode;
|
||||
|
@ -526,7 +521,7 @@ export default createReactClass({
|
|||
}
|
||||
|
||||
return errorText;
|
||||
},
|
||||
}
|
||||
|
||||
renderServerComponent() {
|
||||
const ServerConfig = sdk.getComponent("auth.ServerConfig");
|
||||
|
@ -552,7 +547,7 @@ export default createReactClass({
|
|||
delayTimeMs={250}
|
||||
{...serverDetailsProps}
|
||||
/>;
|
||||
},
|
||||
}
|
||||
|
||||
renderLoginComponentForStep() {
|
||||
if (PHASES_ENABLED && this.state.phase !== PHASE_LOGIN) {
|
||||
|
@ -572,9 +567,9 @@ export default createReactClass({
|
|||
}
|
||||
|
||||
return null;
|
||||
},
|
||||
}
|
||||
|
||||
_renderPasswordStep: function() {
|
||||
_renderPasswordStep = () => {
|
||||
const PasswordLogin = sdk.getComponent('auth.PasswordLogin');
|
||||
|
||||
let onEditServerDetailsClick = null;
|
||||
|
@ -603,9 +598,9 @@ export default createReactClass({
|
|||
busy={this.props.isSyncing || this.state.busyLoggingIn}
|
||||
/>
|
||||
);
|
||||
},
|
||||
};
|
||||
|
||||
_renderSsoStep: function(loginType) {
|
||||
_renderSsoStep = loginType => {
|
||||
const SignInToText = sdk.getComponent('views.auth.SignInToText');
|
||||
|
||||
let onEditServerDetailsClick = null;
|
||||
|
@ -634,9 +629,9 @@ export default createReactClass({
|
|||
/>
|
||||
</div>
|
||||
);
|
||||
},
|
||||
};
|
||||
|
||||
render: function() {
|
||||
render() {
|
||||
const Loader = sdk.getComponent("elements.Spinner");
|
||||
const InlineSpinner = sdk.getComponent("elements.InlineSpinner");
|
||||
const AuthHeader = sdk.getComponent("auth.AuthHeader");
|
||||
|
@ -704,5 +699,5 @@ export default createReactClass({
|
|||
</AuthBody>
|
||||
</AuthPage>
|
||||
);
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,29 +15,24 @@ limitations under the License.
|
|||
*/
|
||||
|
||||
import React from 'react';
|
||||
import createReactClass from 'create-react-class';
|
||||
import PropTypes from 'prop-types';
|
||||
import * as sdk from '../../../index';
|
||||
import {MatrixClientPeg} from '../../../MatrixClientPeg';
|
||||
import { _t } from '../../../languageHandler';
|
||||
import AuthPage from "../../views/auth/AuthPage";
|
||||
|
||||
export default createReactClass({
|
||||
displayName: 'PostRegistration',
|
||||
|
||||
propTypes: {
|
||||
export default class PostRegistration extends React.Component {
|
||||
static propTypes = {
|
||||
onComplete: PropTypes.func.isRequired,
|
||||
},
|
||||
};
|
||||
|
||||
getInitialState: function() {
|
||||
return {
|
||||
avatarUrl: null,
|
||||
errorString: null,
|
||||
busy: false,
|
||||
};
|
||||
},
|
||||
state = {
|
||||
avatarUrl: null,
|
||||
errorString: null,
|
||||
busy: false,
|
||||
};
|
||||
|
||||
componentDidMount: function() {
|
||||
componentDidMount() {
|
||||
// There is some assymetry between ChangeDisplayName and ChangeAvatar,
|
||||
// as ChangeDisplayName will auto-get the name but ChangeAvatar expects
|
||||
// the URL to be passed to you (because it's also used for room avatars).
|
||||
|
@ -55,9 +50,9 @@ export default createReactClass({
|
|||
busy: false,
|
||||
});
|
||||
});
|
||||
},
|
||||
}
|
||||
|
||||
render: function() {
|
||||
render() {
|
||||
const ChangeDisplayName = sdk.getComponent('settings.ChangeDisplayName');
|
||||
const ChangeAvatar = sdk.getComponent('settings.ChangeAvatar');
|
||||
const AuthHeader = sdk.getComponent('auth.AuthHeader');
|
||||
|
@ -78,5 +73,5 @@ export default createReactClass({
|
|||
</AuthBody>
|
||||
</AuthPage>
|
||||
);
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,7 +19,6 @@ limitations under the License.
|
|||
|
||||
import Matrix from 'matrix-js-sdk';
|
||||
import React from 'react';
|
||||
import createReactClass from 'create-react-class';
|
||||
import PropTypes from 'prop-types';
|
||||
import * as sdk from '../../../index';
|
||||
import { _t, _td } from '../../../languageHandler';
|
||||
|
@ -43,10 +42,8 @@ const PHASE_REGISTRATION = 1;
|
|||
// Enable phases for registration
|
||||
const PHASES_ENABLED = true;
|
||||
|
||||
export default createReactClass({
|
||||
displayName: 'Registration',
|
||||
|
||||
propTypes: {
|
||||
export default class Registration extends React.Component {
|
||||
static propTypes = {
|
||||
// Called when the user has logged in. Params:
|
||||
// - object with userId, deviceId, homeserverUrl, identityServerUrl, accessToken
|
||||
// - The user's password, if available and applicable (may be cached in memory
|
||||
|
@ -65,12 +62,13 @@ export default createReactClass({
|
|||
onLoginClick: PropTypes.func.isRequired,
|
||||
onServerConfigChange: PropTypes.func.isRequired,
|
||||
defaultDeviceDisplayName: PropTypes.string,
|
||||
},
|
||||
};
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
getInitialState: function() {
|
||||
const serverType = ServerType.getTypeFromServerConfig(this.props.serverConfig);
|
||||
|
||||
return {
|
||||
this.state = {
|
||||
busy: false,
|
||||
errorText: null,
|
||||
// We remember the values entered by the user because
|
||||
|
@ -118,14 +116,15 @@ export default createReactClass({
|
|||
// this is the user ID that's logged in.
|
||||
differentLoggedInUserId: null,
|
||||
};
|
||||
},
|
||||
}
|
||||
|
||||
componentDidMount: function() {
|
||||
componentDidMount() {
|
||||
this._unmounted = false;
|
||||
this._replaceClient();
|
||||
},
|
||||
}
|
||||
|
||||
// TODO: [REACT-WARNING] Replace with appropriate lifecycle event
|
||||
// eslint-disable-next-line camelcase
|
||||
UNSAFE_componentWillReceiveProps(newProps) {
|
||||
if (newProps.serverConfig.hsUrl === this.props.serverConfig.hsUrl &&
|
||||
newProps.serverConfig.isUrl === this.props.serverConfig.isUrl) return;
|
||||
|
@ -142,7 +141,7 @@ export default createReactClass({
|
|||
phase: this.getDefaultPhaseForServerType(serverType),
|
||||
});
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
getDefaultPhaseForServerType(type) {
|
||||
switch (type) {
|
||||
|
@ -155,9 +154,9 @@ export default createReactClass({
|
|||
case ServerType.ADVANCED:
|
||||
return PHASE_SERVER_DETAILS;
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
onServerTypeChange(type) {
|
||||
onServerTypeChange = type => {
|
||||
this.setState({
|
||||
serverType: type,
|
||||
});
|
||||
|
@ -184,9 +183,9 @@ export default createReactClass({
|
|||
this.setState({
|
||||
phase: this.getDefaultPhaseForServerType(type),
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
_replaceClient: async function(serverConfig) {
|
||||
async _replaceClient(serverConfig) {
|
||||
this.setState({
|
||||
errorText: null,
|
||||
serverDeadError: null,
|
||||
|
@ -286,18 +285,18 @@ export default createReactClass({
|
|||
showGenericError(e);
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
onFormSubmit: function(formVals) {
|
||||
onFormSubmit = formVals => {
|
||||
this.setState({
|
||||
errorText: "",
|
||||
busy: true,
|
||||
formVals: formVals,
|
||||
doingUIAuth: true,
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
_requestEmailToken: function(emailAddress, clientSecret, sendAttempt, sessionId) {
|
||||
_requestEmailToken = (emailAddress, clientSecret, sendAttempt, sessionId) => {
|
||||
return this.state.matrixClient.requestRegisterEmailToken(
|
||||
emailAddress,
|
||||
clientSecret,
|
||||
|
@ -309,9 +308,9 @@ export default createReactClass({
|
|||
session_id: sessionId,
|
||||
}),
|
||||
);
|
||||
},
|
||||
}
|
||||
|
||||
_onUIAuthFinished: async function(success, response, extra) {
|
||||
_onUIAuthFinished = async (success, response, extra) => {
|
||||
if (!success) {
|
||||
let msg = response.message || response.toString();
|
||||
// can we give a better error message?
|
||||
|
@ -395,9 +394,9 @@ export default createReactClass({
|
|||
}
|
||||
|
||||
this.setState(newState);
|
||||
},
|
||||
};
|
||||
|
||||
_setupPushers: function() {
|
||||
_setupPushers() {
|
||||
if (!this.props.brand) {
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
@ -418,15 +417,15 @@ export default createReactClass({
|
|||
}, (error) => {
|
||||
console.error("Couldn't get pushers: " + error);
|
||||
});
|
||||
},
|
||||
}
|
||||
|
||||
onLoginClick: function(ev) {
|
||||
onLoginClick = ev => {
|
||||
ev.preventDefault();
|
||||
ev.stopPropagation();
|
||||
this.props.onLoginClick();
|
||||
},
|
||||
};
|
||||
|
||||
onGoToFormClicked(ev) {
|
||||
onGoToFormClicked = ev => {
|
||||
ev.preventDefault();
|
||||
ev.stopPropagation();
|
||||
this._replaceClient();
|
||||
|
@ -435,23 +434,23 @@ export default createReactClass({
|
|||
doingUIAuth: false,
|
||||
phase: PHASE_REGISTRATION,
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
async onServerDetailsNextPhaseClick() {
|
||||
onServerDetailsNextPhaseClick = async () => {
|
||||
this.setState({
|
||||
phase: PHASE_REGISTRATION,
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
onEditServerDetailsClick(ev) {
|
||||
onEditServerDetailsClick = ev => {
|
||||
ev.preventDefault();
|
||||
ev.stopPropagation();
|
||||
this.setState({
|
||||
phase: PHASE_SERVER_DETAILS,
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
_makeRegisterRequest: function(auth) {
|
||||
_makeRegisterRequest = auth => {
|
||||
// We inhibit login if we're trying to register with an email address: this
|
||||
// avoids a lot of complex race conditions that can occur if we try to log
|
||||
// the user in one one or both of the tabs they might end up with after
|
||||
|
@ -471,20 +470,20 @@ export default createReactClass({
|
|||
if (auth) registerParams.auth = auth;
|
||||
if (inhibitLogin !== undefined && inhibitLogin !== null) registerParams.inhibit_login = inhibitLogin;
|
||||
return this.state.matrixClient.registerRequest(registerParams);
|
||||
},
|
||||
};
|
||||
|
||||
_getUIAuthInputs: function() {
|
||||
_getUIAuthInputs() {
|
||||
return {
|
||||
emailAddress: this.state.formVals.email,
|
||||
phoneCountry: this.state.formVals.phoneCountry,
|
||||
phoneNumber: this.state.formVals.phoneNumber,
|
||||
};
|
||||
},
|
||||
}
|
||||
|
||||
// Links to the login page shown after registration is completed are routed through this
|
||||
// which checks the user hasn't already logged in somewhere else (perhaps we should do
|
||||
// this more generally?)
|
||||
_onLoginClickWithCheck: async function(ev) {
|
||||
_onLoginClickWithCheck = async ev => {
|
||||
ev.preventDefault();
|
||||
|
||||
const sessionLoaded = await Lifecycle.loadSession({ignoreGuest: true});
|
||||
|
@ -492,7 +491,7 @@ export default createReactClass({
|
|||
// ok fine, there's still no session: really go to the login page
|
||||
this.props.onLoginClick();
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
renderServerComponent() {
|
||||
const ServerTypeSelector = sdk.getComponent("auth.ServerTypeSelector");
|
||||
|
@ -553,7 +552,7 @@ export default createReactClass({
|
|||
/>
|
||||
{serverDetails}
|
||||
</div>;
|
||||
},
|
||||
}
|
||||
|
||||
renderRegisterComponent() {
|
||||
if (PHASES_ENABLED && this.state.phase !== PHASE_REGISTRATION) {
|
||||
|
@ -608,9 +607,9 @@ export default createReactClass({
|
|||
serverRequiresIdServer={this.state.serverRequiresIdServer}
|
||||
/>;
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
render: function() {
|
||||
render() {
|
||||
const AuthHeader = sdk.getComponent('auth.AuthHeader');
|
||||
const AuthBody = sdk.getComponent("auth.AuthBody");
|
||||
const AccessibleButton = sdk.getComponent('elements.AccessibleButton');
|
||||
|
@ -706,5 +705,5 @@ export default createReactClass({
|
|||
</AuthBody>
|
||||
</AuthPage>
|
||||
);
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue