ref(sendInvitesForItems): convert to action.

This commit is contained in:
hristoterezov 2018-04-25 14:06:46 +03:00 committed by Saúl Ibarra Corretgé
parent 3091d9e6dd
commit d1af11c67e
4 changed files with 130 additions and 219 deletions

View File

@ -1,11 +1,21 @@
// @flow
import { getInviteURL } from '../base/connection';
import { inviteVideoRooms } from '../videosipgw';
import {
BEGIN_ADD_PEOPLE,
UPDATE_DIAL_IN_NUMBERS_FAILED,
UPDATE_DIAL_IN_NUMBERS_SUCCESS
} from './actionTypes';
import { getDialInConferenceID, getDialInNumbers } from './functions';
import {
getDialInConferenceID,
getDialInNumbers,
getDigitsOnly,
invitePeopleAndChatRooms
} from './functions';
const logger = require('jitsi-meet-logger').getLogger(__filename);
/**
* Creates a (redux) action to signal that a click/tap has been performed on
@ -64,3 +74,85 @@ export function updateDialInNumbers() {
});
};
}
/**
* Send invites for a list of items (may be a combination of users, rooms, phone
* numbers, and video rooms).
*
* @param {Array<Object>} invites - Items for which invites should be sent.
* @returns {Promise} Promise containing the list of invites that were not sent.
*/
export function sendInvitesForItems(invites: Array<Object>) {
return (
dispatch: Dispatch<*>,
getState: Function): Promise<Array<Object>> => {
let allInvitePromises = [];
let invitesLeftToSend = [ ...invites ];
const state = getState();
const { conference } = state['features/base/conference'];
const { inviteServiceUrl } = state['features/base/config'];
const inviteUrl = getInviteURL(state);
const jwt = state['features/base/jwt'].jwt;
// First create all promises for dialing out.
if (conference) {
const phoneNumbers
= invitesLeftToSend.filter(({ type }) => type === 'phone');
// For each number, dial out. On success, remove the number from
// {@link invitesLeftToSend}.
const phoneInvitePromises = phoneNumbers.map(item => {
const numberToInvite = getDigitsOnly(item.number);
return conference.dial(numberToInvite)
.then(() => {
invitesLeftToSend
= invitesLeftToSend.filter(invite =>
invite !== item);
})
.catch(error => logger.error(
'Error inviting phone number:', error));
});
allInvitePromises = allInvitePromises.concat(phoneInvitePromises);
}
const usersAndRooms = invitesLeftToSend.filter(item =>
item.type === 'user' || item.type === 'room');
if (usersAndRooms.length) {
// Send a request to invite all the rooms and users. On success,
// filter all rooms and users from {@link invitesLeftToSend}.
const peopleInvitePromise = invitePeopleAndChatRooms(
inviteServiceUrl,
inviteUrl,
jwt,
usersAndRooms)
.then(() => {
invitesLeftToSend = invitesLeftToSend.filter(item =>
item.type !== 'user' && item.type !== 'room');
})
.catch(error => logger.error(
'Error inviting people:', error));
allInvitePromises.push(peopleInvitePromise);
}
// Sipgw calls are fire and forget. Invite them to the conference
// then immediately remove them from {@link invitesLeftToSend}.
const vrooms = invitesLeftToSend.filter(item =>
item.type === 'videosipgw');
conference
&& vrooms.length > 0
&& dispatch(inviteVideoRooms(conference, vrooms));
invitesLeftToSend = invitesLeftToSend.filter(item =>
item.type !== 'videosipgw');
return (
Promise.all(allInvitePromises)
.then(() => invitesLeftToSend)
);
};
}

View File

@ -7,16 +7,15 @@ import React, { Component } from 'react';
import { connect } from 'react-redux';
import { createInviteDialogEvent, sendAnalytics } from '../../analytics';
import { getInviteURL } from '../../base/connection';
import { Dialog, hideDialog } from '../../base/dialog';
import { translate } from '../../base/i18n';
import { MultiSelectAutocomplete } from '../../base/react';
import { inviteVideoRooms } from '../../videosipgw';
import {
sendInvitesForItems,
getInviteResultsForQuery
getInviteResultsForQuery,
getInviteTypeCounts
} from '../functions';
import { sendInvitesForItems } from '../actions';
const logger = require('jitsi-meet-logger').getLogger(__filename);
@ -43,16 +42,6 @@ class AddPeopleDialog extends Component<*, *> {
*/
_dialOutAuthUrl: PropTypes.string,
/**
* The URL pointing to the service allowing for people invite.
*/
_inviteServiceUrl: PropTypes.string,
/**
* The url of the conference to invite people to.
*/
_inviteUrl: PropTypes.string,
/**
* The JWT token.
*/
@ -79,14 +68,9 @@ class AddPeopleDialog extends Component<*, *> {
dialOutEnabled: PropTypes.bool,
/**
* The function closing the dialog.
* The redux dispatch method.
*/
hideDialog: PropTypes.func,
/**
* Used to invite video rooms.
*/
inviteVideoRooms: PropTypes.func,
dispatch: PropTypes.func,
/**
* Invoked to obtain translated strings.
@ -236,32 +220,6 @@ class AddPeopleDialog extends Component<*, *> {
);
}
/**
* Helper for determining how many of each type of user is being invited.
* Used for logging and sending analytics related to invites.
*
* @param {Array} inviteItems - An array with the invite items, as created
* in {@link _parseQueryResults}.
* @private
* @returns {Object} An object with keys as user types and values as the
* number of invites for that type.
*/
_getInviteTypeCounts(inviteItems = []) {
const inviteTypeCounts = {};
inviteItems.forEach(i => {
const type = i.item.type;
if (!inviteTypeCounts[type]) {
inviteTypeCounts[type] = 0;
}
inviteTypeCounts[type]++;
});
return inviteTypeCounts;
}
_isAddDisabled: () => boolean;
/**
@ -323,8 +281,9 @@ class AddPeopleDialog extends Component<*, *> {
* @returns {void}
*/
_onSubmit() {
const inviteTypeCounts
= this._getInviteTypeCounts(this.state.inviteItems);
const { inviteItems } = this.state;
const items = inviteItems.map(item => item.item);
const inviteTypeCounts = getInviteTypeCounts(items);
sendAnalytics(createInviteDialogEvent(
'clicked', 'inviteButton', {
@ -340,31 +299,15 @@ class AddPeopleDialog extends Component<*, *> {
addToCallInProgress: true
});
const {
_conference,
_inviteServiceUrl,
_inviteUrl,
_jwt
} = this.props;
const { dispatch } = this.props;
const inviteItems = this.state.inviteItems;
const items = inviteItems.map(item => item.item);
const options = {
conference: _conference,
inviteServiceUrl: _inviteServiceUrl,
inviteUrl: _inviteUrl,
inviteVideoRooms: this.props.inviteVideoRooms,
jwt: _jwt
};
sendInvitesForItems(items, options)
dispatch(sendInvitesForItems(items))
.then(invitesLeftToSend => {
// If any invites are left that means something failed to send
// so treat it as an error.
if (invitesLeftToSend.length) {
const erroredInviteTypeCounts
= this._getInviteTypeCounts(invitesLeftToSend);
= getInviteTypeCounts(invitesLeftToSend);
logger.error(`${invitesLeftToSend.length} invites failed`,
erroredInviteTypeCounts);
@ -400,7 +343,7 @@ class AddPeopleDialog extends Component<*, *> {
addToCallInProgress: false
});
this.props.hideDialog();
dispatch(hideDialog());
});
}
@ -580,40 +523,25 @@ class AddPeopleDialog extends Component<*, *> {
* @param {Object} state - The Redux state.
* @private
* @returns {{
* _conference: Object,
* _dialOutAuthUrl: string,
* _inviteServiceUrl: string,
* _inviteUrl: string,
* _jwt: string,
* _peopleSearchQueryTypes: Array<string>,
* _peopleSearchUrl: string
* }}
*/
function _mapStateToProps(state) {
const { conference } = state['features/base/conference'];
const {
dialOutAuthUrl,
inviteServiceUrl,
peopleSearchQueryTypes,
peopleSearchUrl
} = state['features/base/config'];
return {
_conference: conference,
_dialOutAuthUrl: dialOutAuthUrl,
_inviteServiceUrl: inviteServiceUrl,
_inviteUrl: getInviteURL(state),
_jwt: state['features/base/jwt'].jwt,
_peopleSearchQueryTypes: peopleSearchQueryTypes,
_peopleSearchUrl: peopleSearchUrl
};
}
export default translate(
connect(
_mapStateToProps,
/* mapDispatchToProps */ {
hideDialog,
inviteVideoRooms
})(
AddPeopleDialog));
export default translate(connect(_mapStateToProps)(AddPeopleDialog));

View File

@ -81,7 +81,7 @@ export function getDialInNumbers(url: string): Promise<*> {
* @private
* @returns {string} A string with only numbers.
*/
function getDigitsOnly(text: string = ''): string {
export function getDigitsOnly(text: string = ''): string {
return text.replace(/\D/g, '');
}
@ -227,7 +227,7 @@ export function getInviteResultsForQuery(
* type items to invite.
* @returns {Promise} - The promise created by the request.
*/
function invitePeopleAndChatRooms( // eslint-disable-line max-params
export function invitePeopleAndChatRooms( // eslint-disable-line max-params
inviteServiceUrl: string,
inviteUrl: string,
jwt: string,
@ -367,123 +367,26 @@ export function searchDirectory( // eslint-disable-line max-params
}
/**
* Type of the options to use when sending invites.
*/
export type SendInvitesOptions = {
/**
* Conference object used to dial out.
*/
conference: Object,
/**
* The URL to send invites through.
*/
inviteServiceUrl: string,
/**
* The URL sent with each invite.
*/
inviteUrl: string,
/**
* The function to use to invite video rooms.
*
* @param {Object} The conference to which the video rooms should be
* invited.
* @param {Array<Object>} The list of rooms that should be invited.
* @returns {void}
*/
inviteVideoRooms: (Object, Array<Object>) => void,
/**
* The jwt token to pass to the invite service.
*/
jwt: string
};
/**
* Send invites for a list of items (may be a combination of users, rooms, phone
* numbers, and video rooms).
* Helper for determining how many of each type of user is being invited.
* Used for logging and sending analytics related to invites.
*
* @param {Array<Object>} invites - Items for which invites should be sent.
* @param {SendInvitesOptions} options - Options to use when sending the
* provided invites.
* @returns {Promise} Promise containing the list of invites that were not sent.
* @param {Array} inviteItems - An array with the invite items, as created
* in {@link _parseQueryResults}.
* @private
* @returns {Object} An object with keys as user types and values as the
* number of invites for that type.
*/
export function sendInvitesForItems(
invites: Array<Object>,
options: SendInvitesOptions
): Promise<Array<Object>> {
export function getInviteTypeCounts(inviteItems: Array<Object> = []) {
const inviteTypeCounts = {};
const {
conference,
inviteServiceUrl,
inviteUrl,
inviteVideoRooms,
jwt
} = options;
inviteItems.forEach(({ type }) => {
let allInvitePromises = [];
let invitesLeftToSend = [ ...invites ];
if (!inviteTypeCounts[type]) {
inviteTypeCounts[type] = 0;
}
// First create all promises for dialing out.
if (conference) {
const phoneNumbers = invitesLeftToSend.filter(
item => item.type === 'phone');
inviteTypeCounts[type]++;
});
// For each number, dial out. On success, remove the number from
// {@link invitesLeftToSend}.
const phoneInvitePromises = phoneNumbers.map(item => {
const numberToInvite = getDigitsOnly(item.number);
return conference.dial(numberToInvite)
.then(() => {
invitesLeftToSend
= invitesLeftToSend.filter(invite =>
invite !== item);
})
.catch(error => logger.error(
'Error inviting phone number:', error));
});
allInvitePromises = allInvitePromises.concat(phoneInvitePromises);
}
const usersAndRooms = invitesLeftToSend.filter(item =>
item.type === 'user' || item.type === 'room');
if (usersAndRooms.length) {
// Send a request to invite all the rooms and users. On success,
// filter all rooms and users from {@link invitesLeftToSend}.
const peopleInvitePromise = invitePeopleAndChatRooms(
inviteServiceUrl,
inviteUrl,
jwt,
usersAndRooms)
.then(() => {
invitesLeftToSend = invitesLeftToSend.filter(item =>
item.type !== 'user' && item.type !== 'room');
})
.catch(error => logger.error(
'Error inviting people:', error));
allInvitePromises.push(peopleInvitePromise);
}
// Sipgw calls are fire and forget. Invite them to the conference
// then immediately remove them from {@link invitesLeftToSend}.
const vrooms = invitesLeftToSend.filter(item =>
item.type === 'videosipgw');
conference
&& vrooms.length > 0
&& inviteVideoRooms(conference, vrooms);
invitesLeftToSend = invitesLeftToSend.filter(item =>
item.type !== 'videosipgw');
return Promise.all(allInvitePromises)
.then(() => invitesLeftToSend);
return inviteTypeCounts;
}

View File

@ -5,8 +5,6 @@ import { NativeEventEmitter, NativeModules } from 'react-native';
import { MiddlewareRegistry } from '../base/redux';
import { APP_WILL_MOUNT, APP_WILL_UNMOUNT } from '../app';
import { getInviteURL } from '../base/connection';
import { inviteVideoRooms } from '../videosipgw';
import {
BEGIN_ADD_PEOPLE,
@ -15,9 +13,9 @@ import {
import {
getInviteResultsForQuery,
isAddPeopleEnabled,
isDialOutEnabled,
sendInvitesForItems
isDialOutEnabled
} from './functions';
import { sendInvitesForItems } from './actions';
import './middleware.any';
/**
@ -137,27 +135,17 @@ function _beginAddPeople({ getState }, next, action) {
*/
function _onInvite(
{ addPeopleControllerScope, externalAPIScope, invitees }) {
const { getState } = this; // eslint-disable-line no-invalid-this
const state = getState();
const { dispatch, getState } = this; // eslint-disable-line no-invalid-this
// If there are multiple JitsiMeetView instances alive, they will all get
// the event, since there is a single bridge, so make sure we don't act if
// the event is not for us.
if (state['features/app'].app.props.externalAPIScope !== externalAPIScope) {
if (getState()['features/app'].app.props.externalAPIScope
!== externalAPIScope) {
return;
}
const { conference } = state['features/base/conference'];
const { inviteServiceUrl } = state['features/base/config'];
const options = {
conference,
inviteServiceUrl,
inviteUrl: getInviteURL(state),
inviteVideoRooms,
jwt: state['features/base/jwt'].jwt
};
sendInvitesForItems(invitees, options)
dispatch(sendInvitesForItems(invitees))
.then(failedInvitees =>
Invite.inviteSettled(
externalAPIScope,