2016-09-13 13:47:56 +00:00
|
|
|
/*
|
|
|
|
Copyright 2016 OpenMarket Ltd
|
2017-08-16 13:58:30 +00:00
|
|
|
Copyright 2017 New Vector Ltd
|
2016-09-13 13:47:56 +00:00
|
|
|
|
|
|
|
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.
|
|
|
|
*/
|
|
|
|
|
2017-08-16 13:58:30 +00:00
|
|
|
import MatrixClientPeg from '../MatrixClientPeg';
|
2017-08-15 12:42:23 +00:00
|
|
|
import {getAddressType} from '../UserAddress';
|
2017-08-16 13:58:30 +00:00
|
|
|
import {inviteToRoom} from '../RoomInvite';
|
2018-05-01 10:18:45 +00:00
|
|
|
import GroupStore from '../stores/GroupStore';
|
2017-07-12 12:58:14 +00:00
|
|
|
import Promise from 'bluebird';
|
2016-09-13 13:47:56 +00:00
|
|
|
|
|
|
|
/**
|
2017-08-16 13:58:30 +00:00
|
|
|
* Invites multiple addresses to a room or group, handling rate limiting from the server
|
2016-09-13 13:47:56 +00:00
|
|
|
*/
|
|
|
|
export default class MultiInviter {
|
2017-08-16 13:58:30 +00:00
|
|
|
/**
|
|
|
|
* @param {string} targetId The ID of the room or group to invite to
|
|
|
|
*/
|
|
|
|
constructor(targetId) {
|
|
|
|
if (targetId[0] === '+') {
|
|
|
|
this.roomId = null;
|
|
|
|
this.groupId = targetId;
|
|
|
|
} else {
|
|
|
|
this.roomId = targetId;
|
|
|
|
this.groupId = null;
|
|
|
|
}
|
2016-09-13 13:47:56 +00:00
|
|
|
|
|
|
|
this.canceled = false;
|
|
|
|
this.addrs = [];
|
|
|
|
this.busy = false;
|
|
|
|
this.completionStates = {}; // State of each address (invited or error)
|
|
|
|
this.errorTexts = {}; // Textual error per address
|
|
|
|
this.deferred = null;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Invite users to this room. This may only be called once per
|
|
|
|
* instance of the class.
|
|
|
|
*
|
|
|
|
* @param {array} addresses Array of addresses to invite
|
|
|
|
* @returns {Promise} Resolved when all invitations in the queue are complete
|
|
|
|
*/
|
|
|
|
invite(addrs) {
|
|
|
|
if (this.addrs.length > 0) {
|
|
|
|
throw new Error("Already inviting/invited");
|
|
|
|
}
|
|
|
|
this.addrs.push(...addrs);
|
|
|
|
|
|
|
|
for (const addr of this.addrs) {
|
|
|
|
if (getAddressType(addr) === null) {
|
|
|
|
this.completionStates[addr] = 'error';
|
|
|
|
this.errorTexts[addr] = 'Unrecognised address';
|
|
|
|
}
|
|
|
|
}
|
2017-07-12 13:04:20 +00:00
|
|
|
this.deferred = Promise.defer();
|
2016-09-13 13:47:56 +00:00
|
|
|
this._inviteMore(0);
|
|
|
|
|
|
|
|
return this.deferred.promise;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Stops inviting. Causes promises returned by invite() to be rejected.
|
|
|
|
*/
|
|
|
|
cancel() {
|
|
|
|
if (!this.busy) return;
|
|
|
|
|
|
|
|
this._canceled = true;
|
|
|
|
this.deferred.reject(new Error('canceled'));
|
|
|
|
}
|
|
|
|
|
|
|
|
getCompletionState(addr) {
|
|
|
|
return this.completionStates[addr];
|
|
|
|
}
|
|
|
|
|
|
|
|
getErrorText(addr) {
|
|
|
|
return this.errorTexts[addr];
|
|
|
|
}
|
|
|
|
|
|
|
|
_inviteMore(nextIndex) {
|
|
|
|
if (this._canceled) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (nextIndex == this.addrs.length) {
|
|
|
|
this.busy = false;
|
|
|
|
this.deferred.resolve(this.completionStates);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
const addr = this.addrs[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(addr) === 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.completionStates[addr] == 'invited') {
|
|
|
|
this._inviteMore(nextIndex + 1);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2017-08-16 13:58:30 +00:00
|
|
|
let doInvite;
|
|
|
|
if (this.groupId !== null) {
|
2018-05-01 10:18:45 +00:00
|
|
|
doInvite = GroupStore.inviteUserToGroup(this.groupId, addr);
|
2017-08-16 13:58:30 +00:00
|
|
|
} else {
|
|
|
|
doInvite = inviteToRoom(this.roomId, addr);
|
|
|
|
}
|
|
|
|
|
|
|
|
doInvite.then(() => {
|
2016-09-13 13:47:56 +00:00
|
|
|
if (this._canceled) { return; }
|
|
|
|
|
|
|
|
this.completionStates[addr] = 'invited';
|
|
|
|
|
|
|
|
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.completionStates[addr] = 'error';
|
|
|
|
this.errorTexts[addr] = errorText;
|
|
|
|
this.busy = !fatal;
|
|
|
|
|
|
|
|
if (!fatal) {
|
|
|
|
this._inviteMore(nextIndex + 1);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|