diff --git a/src/component-index.js b/src/component-index.js
index 11c711d239..08477c676e 100644
--- a/src/component-index.js
+++ b/src/component-index.js
@@ -52,7 +52,6 @@ module.exports.components['views.dialogs.DeactivateAccountDialog'] = require('./
module.exports.components['views.dialogs.EncryptedEventDialog'] = require('./components/views/dialogs/EncryptedEventDialog');
module.exports.components['views.dialogs.ErrorDialog'] = require('./components/views/dialogs/ErrorDialog');
module.exports.components['views.dialogs.LogoutPrompt'] = require('./components/views/dialogs/LogoutPrompt');
-module.exports.components['views.dialogs.MultiInviteDialog'] = require('./components/views/dialogs/MultiInviteDialog');
module.exports.components['views.dialogs.NeedToRegisterDialog'] = require('./components/views/dialogs/NeedToRegisterDialog');
module.exports.components['views.dialogs.QuestionDialog'] = require('./components/views/dialogs/QuestionDialog');
module.exports.components['views.dialogs.SetDisplayNameDialog'] = require('./components/views/dialogs/SetDisplayNameDialog');
@@ -91,7 +90,6 @@ module.exports.components['views.rooms.Autocomplete'] = require('./components/vi
module.exports.components['views.rooms.AuxPanel'] = require('./components/views/rooms/AuxPanel');
module.exports.components['views.rooms.EntityTile'] = require('./components/views/rooms/EntityTile');
module.exports.components['views.rooms.EventTile'] = require('./components/views/rooms/EventTile');
-module.exports.components['views.rooms.InviteMemberList'] = require('./components/views/rooms/InviteMemberList');
module.exports.components['views.rooms.LinkPreviewWidget'] = require('./components/views/rooms/LinkPreviewWidget');
module.exports.components['views.rooms.MemberDeviceInfo'] = require('./components/views/rooms/MemberDeviceInfo');
module.exports.components['views.rooms.MemberInfo'] = require('./components/views/rooms/MemberInfo');
diff --git a/src/components/views/dialogs/MultiInviteDialog.js b/src/components/views/dialogs/MultiInviteDialog.js
deleted file mode 100644
index a8d7aec495..0000000000
--- a/src/components/views/dialogs/MultiInviteDialog.js
+++ /dev/null
@@ -1,218 +0,0 @@
-/*
-Copyright 2016 OpenMarket Ltd
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-*/
-
-import React from 'react';
-
-import {getAddressType, inviteToRoom} from '../../../Invite';
-import sdk from '../../../index';
-
-export default class MultiInviteDialog extends React.Component {
- constructor(props, context) {
- super(props, context);
-
- this._onCancel = this._onCancel.bind(this);
- this._startInviting = this._startInviting.bind(this);
- this._canceled = false;
-
- this.state = {
- busy: false,
- completionStates: {}, // State of each address (invited or error)
- errorTexts: {}, // Textual error per address
- done: false,
- };
- for (let i = 0; i < this.props.inputs.length; ++i) {
- const input = this.props.inputs[i];
- if (getAddressType(input) === null) {
- this.state.completionStates[i] = 'error';
- this.state.errorTexts[i] = 'Unrecognised address';
- }
- }
- }
-
- componentWillUnmount() {
- this._canceled = true;
- }
-
- _onCancel() {
- this._canceled = true;
- this.props.onFinished(false);
- }
-
- _startInviting() {
- this.setState({
- busy: true,
- done: false,
- });
- this._inviteMore(0);
- }
-
- _inviteMore(nextIndex) {
- if (this._canceled) {
- return;
- }
-
- if (nextIndex == this.props.inputs.length) {
- this.setState({
- busy: false,
- done: true,
- });
- return;
- }
-
- const input = this.props.inputs[nextIndex];
-
- // don't try to invite it if it's an invalid address
- // (it will already be marked as an error though,
- // so no need to do so again)
- if (getAddressType(input) === null) {
- this._inviteMore(nextIndex + 1);
- return;
- }
-
- // don't re-invite (there's no way in the UI to do this, but
- // for sanity's sake)
- if (this.state.completionStates[nextIndex] == 'invited') {
- this._inviteMore(nextIndex + 1);
- return;
- }
-
- inviteToRoom(this.props.roomId, input).then(() => {
- if (this._canceled) { return; }
-
- this.setState((s) => {
- s.completionStates[nextIndex] = 'invited'
- return s;
- });
- this._inviteMore(nextIndex + 1);
- }, (err) => {
- if (this._canceled) { return; }
-
- let errorText;
- let fatal = false;
- if (err.errcode == 'M_FORBIDDEN') {
- fatal = true;
- errorText = 'You do not have permission to invite people to this room.';
- } else if (err.errcode == 'M_LIMIT_EXCEEDED') {
- // we're being throttled so wait a bit & try again
- setTimeout(() => {
- this._inviteMore(nextIndex);
- }, 5000);
- return;
- } else {
- errorText = 'Unknown server error';
- }
- this.setState((s) => {
- s.completionStates[nextIndex] = 'error';
- s.errorTexts[nextIndex] = errorText;
- s.busy = !fatal;
- s.done = fatal;
- return s;
- });
- if (!fatal) {
- this._inviteMore(nextIndex + 1);
- }
- });
- }
-
- _getProgressIndicator() {
- let numErrors = 0;
- for (const k of Object.keys(this.state.completionStates)) {
- if (this.state.completionStates[k] == 'error') {
- ++numErrors;
- }
- }
- let errorText;
- if (numErrors > 0) {
- const plural = numErrors > 1 ? 's' : '';
- errorText = ({numErrors} error{plural})
- }
- return
- {Object.keys(this.state.completionStates).length} / {this.props.inputs.length} {errorText}
- ;
- }
-
- render() {
- const Spinner = sdk.getComponent("elements.Spinner");
- const inviteTiles = [];
-
- for (let i = 0; i < this.props.inputs.length; ++i) {
- const input = this.props.inputs[i];
- let statusClass = '';
- let statusElement;
- if (this.state.completionStates[i] == 'error') {
- statusClass = 'error';
- statusElement =
{this.state.errorTexts[i]}
;
- } else if (this.state.completionStates[i] == 'invited') {
- statusClass = 'invited';
- }
- inviteTiles.push(
-
- {input}
- {statusElement}
-
- );
- }
-
- let controls = [];
- if (this.state.busy) {
- controls.push();
- controls.push();
- controls.push({this._getProgressIndicator()});
- } else if (this.state.done) {
- controls.push(
-
- );
- controls.push({this._getProgressIndicator()});
- } else {
- controls.push(
- );
- controls.push();
- }
-
- return (
-
-
- Inviting {this.props.inputs.length} People
-
-
-
- {controls}
-
-
- );
- }
-}
-
-MultiInviteDialog.propTypes = {
- onFinished: React.PropTypes.func.isRequired,
- inputs: React.PropTypes.array.isRequired,
- roomId: React.PropTypes.string.isRequired,
-};
diff --git a/src/components/views/rooms/InviteMemberList.js b/src/components/views/rooms/InviteMemberList.js
deleted file mode 100644
index ee5eabbeab..0000000000
--- a/src/components/views/rooms/InviteMemberList.js
+++ /dev/null
@@ -1,132 +0,0 @@
-/*
-Copyright 2015, 2016 OpenMarket Ltd
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-*/
-import React from 'react';
-import sdk from '../../../index';
-import Entities from '../../../Entities';
-import MatrixClientPeg from '../../../MatrixClientPeg';
-import rate_limited_func from '../../../ratelimitedfunc';
-
-const INITIAL_SEARCH_RESULTS_COUNT = 10;
-
-module.exports = React.createClass({
- displayName: 'InviteMemberList',
-
- propTypes: {
- roomId: React.PropTypes.string.isRequired,
- onInvite: React.PropTypes.func.isRequired, // fn(inputText)
- onThirdPartyInvite: React.PropTypes.func.isRequired, // fn(inputText)
- onSearchQueryChanged: React.PropTypes.func // fn(inputText)
- },
-
- getDefaultProps: function() {
- return {
- onSearchQueryChanged: function() {}
- };
- },
-
- componentWillMount: function() {
- var cli = MatrixClientPeg.get();
- cli.on("RoomState.members", this.onRoomStateMember);
-
- this._emailEntity = null;
-
- // we have to update the list whenever membership changes
- // particularly to avoid bug https://github.com/vector-im/vector-web/issues/1813
- this._updateList();
- },
-
- componentDidMount: function() {
- // initialise the email tile
- this.onSearchQueryChanged('');
- },
-
- componentWillUnmount: function() {
- var cli = MatrixClientPeg.get();
- if (cli) {
- cli.removeListener("RoomState.members", this.onRoomStateMember);
- }
- // cancel any pending calls to the rate_limited_funcs
- this._updateList.cancelPendingCall();
- },
-
- _updateList: new rate_limited_func(function() {
- this._room = MatrixClientPeg.get().getRoom(this.props.roomId);
- // Load the complete user list for inviting new users
- if (this._room) {
- this._userList = MatrixClientPeg.get().getUsers().filter((u) => {
- return (!this._room.hasMembershipState(u.userId, "join") &&
- !this._room.hasMembershipState(u.userId, "invite"));
- });
- }
- }, 500),
-
- onRoomStateMember: function(ev, state, member) {
- this._updateList();
- },
-
- onInvite: function(ev) {
- this.props.onInvite(this._input);
- },
-
- onThirdPartyInvite: function(ev) {
- this.props.onThirdPartyInvite(this._input);
- },
-
- onSearchQueryChanged: function(input) {
- this._input = input;
- var EntityTile = sdk.getComponent("rooms.EntityTile");
- var BaseAvatar = sdk.getComponent("avatars.BaseAvatar");
-
- // var label = input;
- // if (input[0] === "@") {
- // label = input;
- // }
- // else {
- // label = "Email: " + input;
- // }
-
- // this._emailEntity = new Entities.newEntity(
- // }
- // className="mx_EntityTile_invitePlaceholder"
- // presenceState="online" onClick={this.onThirdPartyInvite} name={"Invite by email"}
- // />,
- // function(query) {
- // return true; // always show this
- // }
- // );
-
- this.props.onSearchQueryChanged(input);
- },
-
- render: function() {
- var SearchableEntityList = sdk.getComponent("rooms.SearchableEntityList");
- var entities = Entities.fromUsers(this._userList || [], true, this.props.onInvite);
-
- // Add an "Email: foo@bar.com" tile as the first tile
- // if (this._emailEntity) {
- // entities.unshift(this._emailEntity);
- // }
-
- return (
-
- );
- }
-});
diff --git a/src/components/views/rooms/MemberList.js b/src/components/views/rooms/MemberList.js
index fa3a7504f3..deedded4fa 100644
--- a/src/components/views/rooms/MemberList.js
+++ b/src/components/views/rooms/MemberList.js
@@ -34,10 +34,6 @@ var SHARE_HISTORY_WARNING =
turn off, 'Share message history with new users' in the settings for this room.
-var shown_invite_warning_this_session = false;
-// global promise so people can bulk invite and they all get resolved
-var invite_defer = q.defer();
-
module.exports = React.createClass({
displayName: 'MemberList',
@@ -47,6 +43,7 @@ module.exports = React.createClass({
// ideally we'd size this to the page height, but
// in practice I find that a little constraining
truncateAt: INITIAL_LOAD_NUM_MEMBERS,
+ searchQuery: "",
};
if (!this.props.roomId) return state;
var cli = MatrixClientPeg.get();
@@ -160,143 +157,6 @@ module.exports = React.createClass({
});
}, 500),
- onThirdPartyInvite: function(inputText) {
- var TextInputDialog = sdk.getComponent("dialogs.TextInputDialog");
- Modal.createDialog(TextInputDialog, {
- title: "Invite members by email",
- description: "Please enter one or more email addresses",
- value: inputText,
- button: "Invite",
- onFinished: (should_invite, addresses)=>{
- if (should_invite) {
- // defer the actual invite to the next event loop to give this
- // Modal a chance to unmount in case onInvite() triggers a new one
- setTimeout(()=>{
- this.onInvite(addresses);
- }, 0);
- }
- }
- });
- },
-
- _doInvite(address) {
- Invite.inviteToRoom(this.props.roomId, address).catch((err) => {
- if (err !== null) {
- console.error("Failed to invite: %s", JSON.stringify(err));
- if (err.errcode == 'M_FORBIDDEN') {
- Modal.createDialog(ErrorDialog, {
- title: "Unable to Invite",
- description: "You do not have permission to invite people to this room."
- });
- } else {
- Modal.createDialog(ErrorDialog, {
- title: "Server error whilst inviting",
- description: err.message
- });
- }
- }
- }).finally(() => {
- this.setState({
- inviting: false
- });
- // XXX: hacky focus on the invite box
- setTimeout(function() {
- var inviteBox = document.getElementById("mx_SearchableEntityList_query");
- if (inviteBox) {
- inviteBox.focus();
- }
- }, 0);
- }).done();
- this.setState({
- inviting: true
- });
- },
-
- onInvite: function(inputText) {
- var ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
- var NeedToRegisterDialog = sdk.getComponent("dialogs.NeedToRegisterDialog");
- var self = this;
- inputText = inputText.trim(); // react requires es5-shim so we know trim() exists
-
- if (MatrixClientPeg.get().isGuest()) {
- Modal.createDialog(NeedToRegisterDialog, {
- title: "Unable to Invite",
- description: "Guest user can't invite new users. Please register to be able to invite new users into a room."
- });
- return;
- }
-
- // email addresses and user IDs do not allow space, comma, semicolon so split
- // on them for bulk inviting.
- // '+' here will treat multiple consecutive separators as one separator, so
- // ', ' separators will also do the right thing.
- const inputs = inputText.split(/[, ;]+/).filter((x) => {
- return x.trim().length > 0;
- });
-
- let validInputs = 0;
- for (const input of inputs) {
- if (Invite.getAddressType(input) != null) {
- ++validInputs;
- }
- }
-
- if (validInputs == 0) {
- Modal.createDialog(ErrorDialog, {
- title: "Invite Error",
- description: "Malformed ID. Should be an email address or a Matrix ID like '@localpart:domain'"
- });
- return;
- }
-
- var inviteWarningDefer = q.defer();
-
- var room = MatrixClientPeg.get().getRoom(this.props.roomId);
- var history_visibility = room.currentState.getStateEvents('m.room.history_visibility', '');
- if (history_visibility) history_visibility = history_visibility.getContent().history_visibility;
-
- if (history_visibility == 'shared' && !shown_invite_warning_this_session) {
- inviteWarningDefer = invite_defer; // whether we continue depends on this defer
- var QuestionDialog = sdk.getComponent("dialogs.QuestionDialog");
- Modal.createDialog(QuestionDialog, {
- title: "Warning",
- description: SHARE_HISTORY_WARNING,
- button: "Invite",
- onFinished: function(should_invite) {
- if (should_invite) {
- shown_invite_warning_this_session = true;
- invite_defer.resolve();
- } else {
- invite_defer.reject(null);
- // reset the promise so we don't auto-reject all invites from
- // now on.
- invite_defer = q.defer();
- }
- }
- });
- } else {
- inviteWarningDefer.resolve();
- }
-
- const promise = inviteWarningDefer.promise;
-
- if (inputs.length == 1) {
- // for a single address, we just send the invite
- promise.done(() => {
- this._doInvite(inputs[0]);
- });
- } else {
- // if there are several, display the confirmation/progress dialog
- promise.done(() => {
- const MultiInviteDialog = sdk.getComponent('views.dialogs.MultiInviteDialog');
- Modal.createDialog(MultiInviteDialog, {
- roomId: this.props.roomId,
- inputs: inputs,
- });
- });
- }
- },
-
getMemberDict: function() {
if (!this.props.roomId) return {};
var cli = MatrixClientPeg.get();
@@ -423,10 +283,8 @@ module.exports = React.createClass({
return userB.getLastActiveTs() - userA.getLastActiveTs();
},
- onSearchQueryChanged: function(input) {
- this.setState({
- searchQuery: input
- });
+ onSearchQueryChanged: function(ev) {
+ this.setState({ searchQuery: ev.target.value });
},
makeMemberTiles: function(membership, query) {
@@ -489,8 +347,6 @@ module.exports = React.createClass({
},
render: function() {
- var InviteMemberList = sdk.getComponent("rooms.InviteMemberList");
-
var invitedSection = null;
var invitedMemberTiles = this.makeMemberTiles('invite', this.state.searchQuery);
if (invitedMemberTiles.length > 0) {
@@ -504,35 +360,25 @@ module.exports = React.createClass({
);
}
- var inviteMemberListSection;
- if (this.state.inviting) {
- var Loader = sdk.getComponent("elements.Spinner");
- inviteMemberListSection = (
-
- );
- }
- else {
- inviteMemberListSection = (
-
- );
- }
-
+ var inputBox = (
+
+ );
var TruncatedList = sdk.getComponent("elements.TruncatedList");
return (
- {inviteMemberListSection}
-
-
- {this.makeMemberTiles('join', this.state.searchQuery)}
-
- {invitedSection}
-
+ { inputBox }
+
+
+ {this.makeMemberTiles('join', this.state.searchQuery)}
+
+ {invitedSection}
+
);
}