From d1af11c67eda75ce3678cc0b80045e5a191ced7c Mon Sep 17 00:00:00 2001 From: hristoterezov Date: Wed, 25 Apr 2018 14:06:46 +0300 Subject: [PATCH] ref(sendInvitesForItems): convert to action. --- react/features/invite/actions.js | 94 ++++++++++++- .../invite/components/AddPeopleDialog.web.js | 98 ++----------- react/features/invite/functions.js | 133 +++--------------- react/features/invite/middleware.native.js | 24 +--- 4 files changed, 130 insertions(+), 219 deletions(-) diff --git a/react/features/invite/actions.js b/react/features/invite/actions.js index 6db681510..921f091b4 100644 --- a/react/features/invite/actions.js +++ b/react/features/invite/actions.js @@ -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} 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) { + return ( + dispatch: Dispatch<*>, + getState: Function): Promise> => { + 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) + ); + }; +} diff --git a/react/features/invite/components/AddPeopleDialog.web.js b/react/features/invite/components/AddPeopleDialog.web.js index d0d495e8c..6913c8ec4 100644 --- a/react/features/invite/components/AddPeopleDialog.web.js +++ b/react/features/invite/components/AddPeopleDialog.web.js @@ -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, * _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)); diff --git a/react/features/invite/functions.js b/react/features/invite/functions.js index 1531ad133..9afc1e63c 100644 --- a/react/features/invite/functions.js +++ b/react/features/invite/functions.js @@ -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} The list of rooms that should be invited. - * @returns {void} - */ - inviteVideoRooms: (Object, Array) => 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} 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, - options: SendInvitesOptions -): Promise> { +export function getInviteTypeCounts(inviteItems: Array = []) { + 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; } diff --git a/react/features/invite/middleware.native.js b/react/features/invite/middleware.native.js index c78740740..5de8bc7b6 100644 --- a/react/features/invite/middleware.native.js +++ b/react/features/invite/middleware.native.js @@ -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,