Convert MultiInviter, RoomInvite and UserAddress to Typescript
parent
3162d144e2
commit
27e27b7a87
|
@ -1,7 +1,5 @@
|
||||||
/*
|
/*
|
||||||
Copyright 2016 OpenMarket Ltd
|
Copyright 2016 - 2021 The Matrix.org Foundation C.I.C.
|
||||||
Copyright 2017, 2018 New Vector Ltd
|
|
||||||
Copyright 2020 The Matrix.org Foundation C.I.C.
|
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
you may not use this file except in compliance with the License.
|
you may not use this file except in compliance with the License.
|
||||||
|
@ -16,15 +14,18 @@ See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import React from 'react';
|
import React from "react";
|
||||||
import {MatrixClientPeg} from './MatrixClientPeg';
|
import { Room } from "matrix-js-sdk/src/models/room";
|
||||||
import MultiInviter from './utils/MultiInviter';
|
import { MatrixEvent } from "matrix-js-sdk/src/models/event";
|
||||||
|
|
||||||
|
import { MatrixClientPeg } from './MatrixClientPeg';
|
||||||
|
import MultiInviter, { CompletionStates } from './utils/MultiInviter';
|
||||||
import Modal from './Modal';
|
import Modal from './Modal';
|
||||||
import * as sdk from './';
|
import * as sdk from './';
|
||||||
import { _t } from './languageHandler';
|
import { _t } from './languageHandler';
|
||||||
import InviteDialog, {KIND_DM, KIND_INVITE} from "./components/views/dialogs/InviteDialog";
|
import InviteDialog, { KIND_DM, KIND_INVITE } from "./components/views/dialogs/InviteDialog";
|
||||||
import CommunityPrototypeInviteDialog from "./components/views/dialogs/CommunityPrototypeInviteDialog";
|
import CommunityPrototypeInviteDialog from "./components/views/dialogs/CommunityPrototypeInviteDialog";
|
||||||
import {CommunityPrototypeStore} from "./stores/CommunityPrototypeStore";
|
import { CommunityPrototypeStore } from "./stores/CommunityPrototypeStore";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Invites multiple addresses to a room
|
* Invites multiple addresses to a room
|
||||||
|
@ -32,15 +33,18 @@ import {CommunityPrototypeStore} from "./stores/CommunityPrototypeStore";
|
||||||
* no option to cancel.
|
* no option to cancel.
|
||||||
*
|
*
|
||||||
* @param {string} roomId The ID of the room to invite to
|
* @param {string} roomId The ID of the room to invite to
|
||||||
* @param {string[]} addrs Array of strings of addresses to invite. May be matrix IDs or 3pids.
|
* @param {string[]} addresses Array of strings of addresses to invite. May be matrix IDs or 3pids.
|
||||||
* @returns {Promise} Promise
|
* @returns {Promise} Promise
|
||||||
*/
|
*/
|
||||||
export function inviteMultipleToRoom(roomId, addrs) {
|
export function inviteMultipleToRoom(
|
||||||
|
roomId: string,
|
||||||
|
addresses: string[],
|
||||||
|
): Promise<{ states: CompletionStates, inviter: MultiInviter }> {
|
||||||
const inviter = new MultiInviter(roomId);
|
const inviter = new MultiInviter(roomId);
|
||||||
return inviter.invite(addrs).then(states => Promise.resolve({states, inviter}));
|
return inviter.invite(addresses).then(states => Promise.resolve({ states, inviter }));
|
||||||
}
|
}
|
||||||
|
|
||||||
export function showStartChatInviteDialog(initialText) {
|
export function showStartChatInviteDialog(initialText = ""): void {
|
||||||
// This dialog handles the room creation internally - we don't need to worry about it.
|
// This dialog handles the room creation internally - we don't need to worry about it.
|
||||||
const InviteDialog = sdk.getComponent("dialogs.InviteDialog");
|
const InviteDialog = sdk.getComponent("dialogs.InviteDialog");
|
||||||
Modal.createTrackedDialog(
|
Modal.createTrackedDialog(
|
||||||
|
@ -49,7 +53,7 @@ export function showStartChatInviteDialog(initialText) {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function showRoomInviteDialog(roomId, initialText = "") {
|
export function showRoomInviteDialog(roomId: string, initialText = ""): void {
|
||||||
// This dialog handles the room creation internally - we don't need to worry about it.
|
// This dialog handles the room creation internally - we don't need to worry about it.
|
||||||
Modal.createTrackedDialog(
|
Modal.createTrackedDialog(
|
||||||
"Invite Users", "", InviteDialog, {
|
"Invite Users", "", InviteDialog, {
|
||||||
|
@ -61,14 +65,14 @@ export function showRoomInviteDialog(roomId, initialText = "") {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function showCommunityRoomInviteDialog(roomId, communityName) {
|
export function showCommunityRoomInviteDialog(roomId: string, communityName: string): void {
|
||||||
Modal.createTrackedDialog(
|
Modal.createTrackedDialog(
|
||||||
'Invite Users to Community', '', CommunityPrototypeInviteDialog, {communityName, roomId},
|
'Invite Users to Community', '', CommunityPrototypeInviteDialog, {communityName, roomId},
|
||||||
/*className=*/null, /*isPriority=*/false, /*isStatic=*/true,
|
/*className=*/null, /*isPriority=*/false, /*isStatic=*/true,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function showCommunityInviteDialog(communityId) {
|
export function showCommunityInviteDialog(communityId: string): void {
|
||||||
const chat = CommunityPrototypeStore.instance.getGeneralChat(communityId);
|
const chat = CommunityPrototypeStore.instance.getGeneralChat(communityId);
|
||||||
if (chat) {
|
if (chat) {
|
||||||
const name = CommunityPrototypeStore.instance.getCommunityName(communityId);
|
const name = CommunityPrototypeStore.instance.getCommunityName(communityId);
|
||||||
|
@ -83,7 +87,7 @@ export function showCommunityInviteDialog(communityId) {
|
||||||
* @param {MatrixEvent} event The event to check
|
* @param {MatrixEvent} event The event to check
|
||||||
* @returns {boolean} True if valid, false otherwise
|
* @returns {boolean} True if valid, false otherwise
|
||||||
*/
|
*/
|
||||||
export function isValid3pidInvite(event) {
|
export function isValid3pidInvite(event: MatrixEvent): boolean {
|
||||||
if (!event || event.getType() !== "m.room.third_party_invite") return false;
|
if (!event || event.getType() !== "m.room.third_party_invite") return false;
|
||||||
|
|
||||||
// any events without these keys are not valid 3pid invites, so we ignore them
|
// any events without these keys are not valid 3pid invites, so we ignore them
|
||||||
|
@ -96,7 +100,7 @@ export function isValid3pidInvite(event) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function inviteUsersToRoom(roomId, userIds) {
|
export function inviteUsersToRoom(roomId: string, userIds: string[]): Promise<void> {
|
||||||
return inviteMultipleToRoom(roomId, userIds).then((result) => {
|
return inviteMultipleToRoom(roomId, userIds).then((result) => {
|
||||||
const room = MatrixClientPeg.get().getRoom(roomId);
|
const room = MatrixClientPeg.get().getRoom(roomId);
|
||||||
showAnyInviteErrors(result.states, room, result.inviter);
|
showAnyInviteErrors(result.states, room, result.inviter);
|
||||||
|
@ -110,9 +114,9 @@ export function inviteUsersToRoom(roomId, userIds) {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export function showAnyInviteErrors(addrs, room, inviter) {
|
export function showAnyInviteErrors(states: CompletionStates, room: Room, inviter: MultiInviter): boolean {
|
||||||
// Show user any errors
|
// Show user any errors
|
||||||
const failedUsers = Object.keys(addrs).filter(a => addrs[a] === 'error');
|
const failedUsers = Object.keys(states).filter(a => states[a] === 'error');
|
||||||
if (failedUsers.length === 1 && inviter.fatal) {
|
if (failedUsers.length === 1 && inviter.fatal) {
|
||||||
// Just get the first message because there was a fatal problem on the first
|
// Just get the first message because there was a fatal problem on the first
|
||||||
// user. This usually means that no other users were attempted, making it
|
// user. This usually means that no other users were attempted, making it
|
||||||
|
@ -126,7 +130,7 @@ export function showAnyInviteErrors(addrs, room, inviter) {
|
||||||
} else {
|
} else {
|
||||||
const errorList = [];
|
const errorList = [];
|
||||||
for (const addr of failedUsers) {
|
for (const addr of failedUsers) {
|
||||||
if (addrs[addr] === "error") {
|
if (states[addr] === "error") {
|
||||||
const reason = inviter.getErrorText(addr);
|
const reason = inviter.getErrorText(addr);
|
||||||
errorList.push(addr + ": " + reason);
|
errorList.push(addr + ": " + reason);
|
||||||
}
|
}
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
Copyright 2017 New Vector Ltd
|
Copyright 2017 - 2021 The Matrix.org Foundation C.I.C.
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
you may not use this file except in compliance with the License.
|
you may not use this file except in compliance with the License.
|
||||||
|
@ -14,15 +14,19 @@ See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
const emailRegex = /^\S+@\S+\.\S+$/;
|
import PropTypes from "prop-types";
|
||||||
|
|
||||||
|
const emailRegex = /^\S+@\S+\.\S+$/;
|
||||||
const mxUserIdRegex = /^@\S+:\S+$/;
|
const mxUserIdRegex = /^@\S+:\S+$/;
|
||||||
const mxRoomIdRegex = /^!\S+:\S+$/;
|
const mxRoomIdRegex = /^!\S+:\S+$/;
|
||||||
|
|
||||||
import PropTypes from 'prop-types';
|
export const addressTypes = ['mx-user-id', 'mx-room-id', 'email'];
|
||||||
export const addressTypes = [
|
|
||||||
'mx-user-id', 'mx-room-id', 'email',
|
export enum AddressType {
|
||||||
];
|
Email = "email",
|
||||||
|
MatrixUserId = "mx-user-id",
|
||||||
|
MatrixRoomId = "mx-room-id",
|
||||||
|
}
|
||||||
|
|
||||||
// PropType definition for an object describing
|
// PropType definition for an object describing
|
||||||
// an address that can be invited to a room (which
|
// an address that can be invited to a room (which
|
||||||
|
@ -40,18 +44,13 @@ export const UserAddressType = PropTypes.shape({
|
||||||
isKnown: PropTypes.bool,
|
isKnown: PropTypes.bool,
|
||||||
});
|
});
|
||||||
|
|
||||||
export function getAddressType(inputText) {
|
export function getAddressType(inputText: string): AddressType | null {
|
||||||
const isEmailAddress = emailRegex.test(inputText);
|
if (emailRegex.test(inputText)) {
|
||||||
const isUserId = mxUserIdRegex.test(inputText);
|
return AddressType.Email;
|
||||||
const isRoomId = mxRoomIdRegex.test(inputText);
|
} else if (mxUserIdRegex.test(inputText)) {
|
||||||
|
return AddressType.MatrixUserId;
|
||||||
// sanity check the input for user IDs
|
} else if (mxRoomIdRegex.test(inputText)) {
|
||||||
if (isEmailAddress) {
|
return AddressType.MatrixRoomId;
|
||||||
return 'email';
|
|
||||||
} else if (isUserId) {
|
|
||||||
return 'mx-user-id';
|
|
||||||
} else if (isRoomId) {
|
|
||||||
return 'mx-room-id';
|
|
||||||
} else {
|
} else {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
|
@ -1,6 +1,5 @@
|
||||||
/*
|
/*
|
||||||
Copyright 2016 OpenMarket Ltd
|
Copyright 2016 - 2021 The Matrix.org Foundation C.I.C.
|
||||||
Copyright 2017, 2018 New Vector Ltd
|
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
you may not use this file except in compliance with the License.
|
you may not use this file except in compliance with the License.
|
||||||
|
@ -15,23 +14,51 @@ See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {MatrixClientPeg} from '../MatrixClientPeg';
|
import { MatrixError } from "matrix-js-sdk/src/http-api";
|
||||||
import {getAddressType} from '../UserAddress';
|
|
||||||
|
import { MatrixClientPeg } from '../MatrixClientPeg';
|
||||||
|
import { AddressType, getAddressType } from '../UserAddress';
|
||||||
import GroupStore from '../stores/GroupStore';
|
import GroupStore from '../stores/GroupStore';
|
||||||
import {_t} from "../languageHandler";
|
import { _t } from "../languageHandler";
|
||||||
import * as sdk from "../index";
|
|
||||||
import Modal from "../Modal";
|
import Modal from "../Modal";
|
||||||
import SettingsStore from "../settings/SettingsStore";
|
import SettingsStore from "../settings/SettingsStore";
|
||||||
import {defer} from "./promise";
|
import { defer, IDeferred } from "./promise";
|
||||||
|
import AskInviteAnywayDialog from "../components/views/dialogs/AskInviteAnywayDialog";
|
||||||
|
|
||||||
|
export enum InviteState {
|
||||||
|
Invited = "invited",
|
||||||
|
Error = "error",
|
||||||
|
}
|
||||||
|
|
||||||
|
interface IError {
|
||||||
|
errorText: string;
|
||||||
|
errcode: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const UNKNOWN_PROFILE_ERRORS = ['M_NOT_FOUND', 'M_USER_NOT_FOUND', 'M_PROFILE_UNDISCLOSED', 'M_PROFILE_NOT_FOUND'];
|
||||||
|
|
||||||
|
export type CompletionStates = Record<string, InviteState>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Invites multiple addresses to a room or group, handling rate limiting from the server
|
* Invites multiple addresses to a room or group, handling rate limiting from the server
|
||||||
*/
|
*/
|
||||||
export default class MultiInviter {
|
export default class MultiInviter {
|
||||||
|
private readonly roomId?: string;
|
||||||
|
private readonly groupId?: string;
|
||||||
|
|
||||||
|
private canceled = false;
|
||||||
|
private addresses: string[] = [];
|
||||||
|
private busy = false;
|
||||||
|
private _fatal = false;
|
||||||
|
private completionStates: CompletionStates = {}; // State of each address (invited or error)
|
||||||
|
private errors: Record<string, IError> = {}; // { address: {errorText, errcode} }
|
||||||
|
private deferred: IDeferred<CompletionStates> = null;
|
||||||
|
private reason: string = null;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {string} targetId The ID of the room or group to invite to
|
* @param {string} targetId The ID of the room or group to invite to
|
||||||
*/
|
*/
|
||||||
constructor(targetId) {
|
constructor(targetId: string) {
|
||||||
if (targetId[0] === '+') {
|
if (targetId[0] === '+') {
|
||||||
this.roomId = null;
|
this.roomId = null;
|
||||||
this.groupId = targetId;
|
this.groupId = targetId;
|
||||||
|
@ -39,41 +66,38 @@ export default class MultiInviter {
|
||||||
this.roomId = targetId;
|
this.roomId = targetId;
|
||||||
this.groupId = null;
|
this.groupId = null;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
this.canceled = false;
|
public get fatal() {
|
||||||
this.addrs = [];
|
return this._fatal;
|
||||||
this.busy = false;
|
|
||||||
this.completionStates = {}; // State of each address (invited or error)
|
|
||||||
this.errors = {}; // { address: {errorText, errcode} }
|
|
||||||
this.deferred = null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Invite users to this room. This may only be called once per
|
* Invite users to this room. This may only be called once per
|
||||||
* instance of the class.
|
* instance of the class.
|
||||||
*
|
*
|
||||||
* @param {array} addrs Array of addresses to invite
|
* @param {array} addresses Array of addresses to invite
|
||||||
* @param {string} reason Reason for inviting (optional)
|
* @param {string} reason Reason for inviting (optional)
|
||||||
* @returns {Promise} Resolved when all invitations in the queue are complete
|
* @returns {Promise} Resolved when all invitations in the queue are complete
|
||||||
*/
|
*/
|
||||||
invite(addrs, reason) {
|
public invite(addresses, reason?: string): Promise<CompletionStates> {
|
||||||
if (this.addrs.length > 0) {
|
if (this.addresses.length > 0) {
|
||||||
throw new Error("Already inviting/invited");
|
throw new Error("Already inviting/invited");
|
||||||
}
|
}
|
||||||
this.addrs.push(...addrs);
|
this.addresses.push(...addresses);
|
||||||
this.reason = reason;
|
this.reason = reason;
|
||||||
|
|
||||||
for (const addr of this.addrs) {
|
for (const addr of this.addresses) {
|
||||||
if (getAddressType(addr) === null) {
|
if (getAddressType(addr) === null) {
|
||||||
this.completionStates[addr] = 'error';
|
this.completionStates[addr] = InviteState.Error;
|
||||||
this.errors[addr] = {
|
this.errors[addr] = {
|
||||||
errcode: 'M_INVALID',
|
errcode: 'M_INVALID',
|
||||||
errorText: _t('Unrecognised address'),
|
errorText: _t('Unrecognised address'),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
this.deferred = defer();
|
this.deferred = defer<CompletionStates>();
|
||||||
this._inviteMore(0);
|
this.inviteMore(0);
|
||||||
|
|
||||||
return this.deferred.promise;
|
return this.deferred.promise;
|
||||||
}
|
}
|
||||||
|
@ -81,33 +105,36 @@ export default class MultiInviter {
|
||||||
/**
|
/**
|
||||||
* Stops inviting. Causes promises returned by invite() to be rejected.
|
* Stops inviting. Causes promises returned by invite() to be rejected.
|
||||||
*/
|
*/
|
||||||
cancel() {
|
public cancel(): void {
|
||||||
if (!this.busy) return;
|
if (!this.busy) return;
|
||||||
|
|
||||||
this._canceled = true;
|
this.canceled = true;
|
||||||
this.deferred.reject(new Error('canceled'));
|
this.deferred.reject(new Error('canceled'));
|
||||||
}
|
}
|
||||||
|
|
||||||
getCompletionState(addr) {
|
public getCompletionState(addr: string): InviteState {
|
||||||
return this.completionStates[addr];
|
return this.completionStates[addr];
|
||||||
}
|
}
|
||||||
|
|
||||||
getErrorText(addr) {
|
public getErrorText(addr: string): string {
|
||||||
return this.errors[addr] ? this.errors[addr].errorText : null;
|
return this.errors[addr] ? this.errors[addr].errorText : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
async _inviteToRoom(roomId, addr, ignoreProfile) {
|
private async inviteToRoom(roomId: string, addr: string, ignoreProfile = false): Promise<{}> {
|
||||||
const addrType = getAddressType(addr);
|
const addrType = getAddressType(addr);
|
||||||
|
|
||||||
if (addrType === 'email') {
|
if (addrType === AddressType.Email) {
|
||||||
return MatrixClientPeg.get().inviteByEmail(roomId, addr);
|
return MatrixClientPeg.get().inviteByEmail(roomId, addr);
|
||||||
} else if (addrType === 'mx-user-id') {
|
} else if (addrType === AddressType.MatrixUserId) {
|
||||||
const room = MatrixClientPeg.get().getRoom(roomId);
|
const room = MatrixClientPeg.get().getRoom(roomId);
|
||||||
if (!room) throw new Error("Room not found");
|
if (!room) throw new Error("Room not found");
|
||||||
|
|
||||||
const member = room.getMember(addr);
|
const member = room.getMember(addr);
|
||||||
if (member && ['join', 'invite'].includes(member.membership)) {
|
if (member && ['join', 'invite'].includes(member.membership)) {
|
||||||
throw {errcode: "RIOT.ALREADY_IN_ROOM", error: "Member already invited"};
|
throw new new MatrixError({
|
||||||
|
errcode: "RIOT.ALREADY_IN_ROOM",
|
||||||
|
error: "Member already invited",
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!ignoreProfile && SettingsStore.getValue("promptBeforeInviteUnknownUsers", this.roomId)) {
|
if (!ignoreProfile && SettingsStore.getValue("promptBeforeInviteUnknownUsers", this.roomId)) {
|
||||||
|
@ -124,28 +151,28 @@ export default class MultiInviter {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_doInvite(address, ignoreProfile) {
|
private doInvite(address: string, ignoreProfile = false): Promise<void> {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise<void>((resolve, reject) => {
|
||||||
console.log(`Inviting ${address}`);
|
console.log(`Inviting ${address}`);
|
||||||
|
|
||||||
let doInvite;
|
let doInvite;
|
||||||
if (this.groupId !== null) {
|
if (this.groupId !== null) {
|
||||||
doInvite = GroupStore.inviteUserToGroup(this.groupId, address);
|
doInvite = GroupStore.inviteUserToGroup(this.groupId, address);
|
||||||
} else {
|
} else {
|
||||||
doInvite = this._inviteToRoom(this.roomId, address, ignoreProfile);
|
doInvite = this.inviteToRoom(this.roomId, address, ignoreProfile);
|
||||||
}
|
}
|
||||||
|
|
||||||
doInvite.then(() => {
|
doInvite.then(() => {
|
||||||
if (this._canceled) {
|
if (this.canceled) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.completionStates[address] = 'invited';
|
this.completionStates[address] = InviteState.Invited;
|
||||||
delete this.errors[address];
|
delete this.errors[address];
|
||||||
|
|
||||||
resolve();
|
resolve();
|
||||||
}).catch((err) => {
|
}).catch((err) => {
|
||||||
if (this._canceled) {
|
if (this.canceled) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -161,7 +188,7 @@ export default class MultiInviter {
|
||||||
} else if (err.errcode === 'M_LIMIT_EXCEEDED') {
|
} else if (err.errcode === 'M_LIMIT_EXCEEDED') {
|
||||||
// we're being throttled so wait a bit & try again
|
// we're being throttled so wait a bit & try again
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
this._doInvite(address, ignoreProfile).then(resolve, reject);
|
this.doInvite(address, ignoreProfile).then(resolve, reject);
|
||||||
}, 5000);
|
}, 5000);
|
||||||
return;
|
return;
|
||||||
} else if (['M_NOT_FOUND', 'M_USER_NOT_FOUND'].includes(err.errcode)) {
|
} else if (['M_NOT_FOUND', 'M_USER_NOT_FOUND'].includes(err.errcode)) {
|
||||||
|
@ -171,7 +198,7 @@ export default class MultiInviter {
|
||||||
} else if (err.errcode === 'M_PROFILE_NOT_FOUND' && !ignoreProfile) {
|
} else if (err.errcode === 'M_PROFILE_NOT_FOUND' && !ignoreProfile) {
|
||||||
// Invite without the profile check
|
// Invite without the profile check
|
||||||
console.warn(`User ${address} does not have a profile - inviting anyways automatically`);
|
console.warn(`User ${address} does not have a profile - inviting anyways automatically`);
|
||||||
this._doInvite(address, true).then(resolve, reject);
|
this.doInvite(address, true).then(resolve, reject);
|
||||||
} else if (err.errcode === "M_BAD_STATE") {
|
} else if (err.errcode === "M_BAD_STATE") {
|
||||||
errorText = _t("The user must be unbanned before they can be invited.");
|
errorText = _t("The user must be unbanned before they can be invited.");
|
||||||
} else if (err.errcode === "M_UNSUPPORTED_ROOM_VERSION") {
|
} else if (err.errcode === "M_UNSUPPORTED_ROOM_VERSION") {
|
||||||
|
@ -180,14 +207,14 @@ export default class MultiInviter {
|
||||||
errorText = _t('Unknown server error');
|
errorText = _t('Unknown server error');
|
||||||
}
|
}
|
||||||
|
|
||||||
this.completionStates[address] = 'error';
|
this.completionStates[address] = InviteState.Error;
|
||||||
this.errors[address] = {errorText, errcode: err.errcode};
|
this.errors[address] = { errorText, errcode: err.errcode };
|
||||||
|
|
||||||
this.busy = !fatal;
|
this.busy = !fatal;
|
||||||
this.fatal = fatal;
|
this._fatal = fatal;
|
||||||
|
|
||||||
if (fatal) {
|
if (fatal) {
|
||||||
reject();
|
reject(err);
|
||||||
} else {
|
} else {
|
||||||
resolve();
|
resolve();
|
||||||
}
|
}
|
||||||
|
@ -195,22 +222,22 @@ export default class MultiInviter {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
_inviteMore(nextIndex, ignoreProfile) {
|
private inviteMore(nextIndex: number, ignoreProfile = false): void {
|
||||||
if (this._canceled) {
|
if (this.canceled) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (nextIndex === this.addrs.length) {
|
if (nextIndex === this.addresses.length) {
|
||||||
this.busy = false;
|
this.busy = false;
|
||||||
if (Object.keys(this.errors).length > 0 && !this.groupId) {
|
if (Object.keys(this.errors).length > 0 && !this.groupId) {
|
||||||
// There were problems inviting some people - see if we can invite them
|
// There were problems inviting some people - see if we can invite them
|
||||||
// without caring if they exist or not.
|
// without caring if they exist or not.
|
||||||
const unknownProfileErrors = ['M_NOT_FOUND', 'M_USER_NOT_FOUND', 'M_PROFILE_UNDISCLOSED', 'M_PROFILE_NOT_FOUND'];
|
const unknownProfileUsers = Object.keys(this.errors)
|
||||||
const unknownProfileUsers = Object.keys(this.errors).filter(a => unknownProfileErrors.includes(this.errors[a].errcode));
|
.filter(a => UNKNOWN_PROFILE_ERRORS.includes(this.errors[a].errcode));
|
||||||
|
|
||||||
if (unknownProfileUsers.length > 0) {
|
if (unknownProfileUsers.length > 0) {
|
||||||
const inviteUnknowns = () => {
|
const inviteUnknowns = () => {
|
||||||
const promises = unknownProfileUsers.map(u => this._doInvite(u, true));
|
const promises = unknownProfileUsers.map(u => this.doInvite(u, true));
|
||||||
Promise.all(promises).then(() => this.deferred.resolve(this.completionStates));
|
Promise.all(promises).then(() => this.deferred.resolve(this.completionStates));
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -219,15 +246,17 @@ export default class MultiInviter {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const AskInviteAnywayDialog = sdk.getComponent("dialogs.AskInviteAnywayDialog");
|
|
||||||
console.log("Showing failed to invite dialog...");
|
console.log("Showing failed to invite dialog...");
|
||||||
Modal.createTrackedDialog('Failed to invite', '', AskInviteAnywayDialog, {
|
Modal.createTrackedDialog('Failed to invite', '', AskInviteAnywayDialog, {
|
||||||
unknownProfileUsers: unknownProfileUsers.map(u => {return {userId: u, errorText: this.errors[u].errorText};}),
|
unknownProfileUsers: unknownProfileUsers.map(u => ({
|
||||||
|
userId: u,
|
||||||
|
errorText: this.errors[u].errorText,
|
||||||
|
})),
|
||||||
onInviteAnyways: () => inviteUnknowns(),
|
onInviteAnyways: () => inviteUnknowns(),
|
||||||
onGiveUp: () => {
|
onGiveUp: () => {
|
||||||
// Fake all the completion states because we already warned the user
|
// Fake all the completion states because we already warned the user
|
||||||
for (const addr of unknownProfileUsers) {
|
for (const addr of unknownProfileUsers) {
|
||||||
this.completionStates[addr] = 'invited';
|
this.completionStates[addr] = InviteState.Invited;
|
||||||
}
|
}
|
||||||
this.deferred.resolve(this.completionStates);
|
this.deferred.resolve(this.completionStates);
|
||||||
},
|
},
|
||||||
|
@ -239,25 +268,25 @@ export default class MultiInviter {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const addr = this.addrs[nextIndex];
|
const addr = this.addresses[nextIndex];
|
||||||
|
|
||||||
// don't try to invite it if it's an invalid address
|
// don't try to invite it if it's an invalid address
|
||||||
// (it will already be marked as an error though,
|
// (it will already be marked as an error though,
|
||||||
// so no need to do so again)
|
// so no need to do so again)
|
||||||
if (getAddressType(addr) === null) {
|
if (getAddressType(addr) === null) {
|
||||||
this._inviteMore(nextIndex + 1);
|
this.inviteMore(nextIndex + 1);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// don't re-invite (there's no way in the UI to do this, but
|
// don't re-invite (there's no way in the UI to do this, but
|
||||||
// for sanity's sake)
|
// for sanity's sake)
|
||||||
if (this.completionStates[addr] === 'invited') {
|
if (this.completionStates[addr] === InviteState.Invited) {
|
||||||
this._inviteMore(nextIndex + 1);
|
this.inviteMore(nextIndex + 1);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this._doInvite(addr, ignoreProfile).then(() => {
|
this.doInvite(addr, ignoreProfile).then(() => {
|
||||||
this._inviteMore(nextIndex + 1, ignoreProfile);
|
this.inviteMore(nextIndex + 1, ignoreProfile);
|
||||||
}).catch(() => this.deferred.resolve(this.completionStates));
|
}).catch(() => this.deferred.resolve(this.completionStates));
|
||||||
}
|
}
|
||||||
}
|
}
|
Loading…
Reference in New Issue