Coding style/naming: invite & invitee(s)
Hristo Terezov, Chris Cordle, and I/Lyubomir Marinov agreed that we'd try to use "invite" & "invitee(s)" in Web/React's iframe API, mobile/react-native's SDK invite API, and internally for the purposes of consistency, ease of understanding, etc.
This commit is contained in:
parent
69eefc82a5
commit
520bb8bd22
|
@ -1,12 +1,12 @@
|
|||
// @flow
|
||||
|
||||
import * as JitsiMeetConferenceEvents from '../../ConferenceEvents';
|
||||
import { parseJWTFromURLParams } from '../../react/features/base/jwt';
|
||||
import {
|
||||
createApiEvent,
|
||||
sendAnalytics
|
||||
} from '../../react/features/analytics';
|
||||
import { sendInvitesForItems } from '../../react/features/invite';
|
||||
import { parseJWTFromURLParams } from '../../react/features/base/jwt';
|
||||
import { invite } from '../../react/features/invite';
|
||||
import { getJitsiMeetTransport } from '../transport';
|
||||
|
||||
import { API_ID } from './constants';
|
||||
|
@ -114,15 +114,20 @@ function initCommands() {
|
|||
switch (name) {
|
||||
case 'invite':
|
||||
APP.store.dispatch(
|
||||
sendInvitesForItems(request.invitees))
|
||||
.then(failedInvites => {
|
||||
const failed = failedInvites.length === 0;
|
||||
invite(request.invitees))
|
||||
.then(failedInvitees => {
|
||||
let error;
|
||||
let result;
|
||||
|
||||
if (failedInvitees.length) {
|
||||
error = new Error('One or more invites failed!');
|
||||
} else {
|
||||
result = true;
|
||||
}
|
||||
|
||||
callback({
|
||||
result: failed ? undefined : true,
|
||||
error: failed
|
||||
? new Error('One or more invites failed!')
|
||||
: undefined
|
||||
error,
|
||||
result
|
||||
});
|
||||
});
|
||||
break;
|
||||
|
|
|
@ -32,6 +32,91 @@ export function beginAddPeople() {
|
|||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Invites (i.e. sends invites to) an array of invitees (which may be a
|
||||
* combination of users, rooms, phone numbers, and video rooms).
|
||||
*
|
||||
* @param {Array<Object>} invitees - The recepients to send invites to.
|
||||
* @returns {Promise<Array<Object>>} A {@code Promise} resolving with an array
|
||||
* of invitees who were not invited (i.e. invites were not sent to them).
|
||||
*/
|
||||
export function invite(invitees: Array<Object>) {
|
||||
return (
|
||||
dispatch: Dispatch<*>,
|
||||
getState: Function): Promise<Array<Object>> => {
|
||||
let allInvitePromises = [];
|
||||
let invitesLeftToSend = [ ...invitees ];
|
||||
|
||||
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'];
|
||||
|
||||
// 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(
|
||||
invitee => invitee !== item);
|
||||
})
|
||||
.catch(error =>
|
||||
logger.error('Error inviting phone number:', error));
|
||||
});
|
||||
|
||||
allInvitePromises = allInvitePromises.concat(phoneInvitePromises);
|
||||
}
|
||||
|
||||
const usersAndRooms
|
||||
= invitesLeftToSend.filter(
|
||||
({ type }) => type === 'user' || 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(
|
||||
({ type }) => type !== 'user' && 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 invitesLeftToSend.
|
||||
const vrooms
|
||||
= invitesLeftToSend.filter(({ type }) => type === 'videosipgw');
|
||||
|
||||
conference
|
||||
&& vrooms.length > 0
|
||||
&& dispatch(inviteVideoRooms(conference, vrooms));
|
||||
|
||||
invitesLeftToSend
|
||||
= invitesLeftToSend.filter(({ type }) => type !== 'videosipgw');
|
||||
|
||||
return (
|
||||
Promise.all(allInvitePromises)
|
||||
.then(() => invitesLeftToSend));
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends AJAX requests for dial-in numbers and conference ID.
|
||||
*
|
||||
|
@ -74,85 +159,3 @@ 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)
|
||||
);
|
||||
};
|
||||
}
|
||||
|
|
|
@ -11,11 +11,8 @@ import { Dialog, hideDialog } from '../../base/dialog';
|
|||
import { translate } from '../../base/i18n';
|
||||
import { MultiSelectAutocomplete } from '../../base/react';
|
||||
|
||||
import {
|
||||
getInviteResultsForQuery,
|
||||
getInviteTypeCounts
|
||||
} from '../functions';
|
||||
import { sendInvitesForItems } from '../actions';
|
||||
import { invite } from '../actions';
|
||||
import { getInviteResultsForQuery, getInviteTypeCounts } from '../functions';
|
||||
|
||||
const logger = require('jitsi-meet-logger').getLogger(__filename);
|
||||
|
||||
|
@ -68,7 +65,7 @@ class AddPeopleDialog extends Component<*, *> {
|
|||
dialOutEnabled: PropTypes.bool,
|
||||
|
||||
/**
|
||||
* The redux dispatch method.
|
||||
* The redux {@code dispatch} function.
|
||||
*/
|
||||
dispatch: PropTypes.func,
|
||||
|
||||
|
@ -282,8 +279,8 @@ class AddPeopleDialog extends Component<*, *> {
|
|||
*/
|
||||
_onSubmit() {
|
||||
const { inviteItems } = this.state;
|
||||
const items = inviteItems.map(item => item.item);
|
||||
const inviteTypeCounts = getInviteTypeCounts(items);
|
||||
const invitees = inviteItems.map(({ item }) => item);
|
||||
const inviteTypeCounts = getInviteTypeCounts(invitees);
|
||||
|
||||
sendAnalytics(createInviteDialogEvent(
|
||||
'clicked', 'inviteButton', {
|
||||
|
@ -301,7 +298,7 @@ class AddPeopleDialog extends Component<*, *> {
|
|||
|
||||
const { dispatch } = this.props;
|
||||
|
||||
dispatch(sendInvitesForItems(items))
|
||||
dispatch(invite(invitees))
|
||||
.then(invitesLeftToSend => {
|
||||
// If any invites are left that means something failed to send
|
||||
// so treat it as an error.
|
||||
|
@ -322,15 +319,12 @@ class AddPeopleDialog extends Component<*, *> {
|
|||
addToCallError: true
|
||||
});
|
||||
|
||||
const unsentInviteIDs = invitesLeftToSend.map(invite =>
|
||||
invite.id || invite.number
|
||||
);
|
||||
|
||||
const itemsToSelect = inviteItems.filter(invite =>
|
||||
unsentInviteIDs.includes(
|
||||
invite.item.id || invite.item.number
|
||||
)
|
||||
);
|
||||
const unsentInviteIDs
|
||||
= invitesLeftToSend.map(invitee =>
|
||||
invitee.id || invitee.number);
|
||||
const itemsToSelect
|
||||
= inviteItems.filter(({ item }) =>
|
||||
unsentInviteIDs.includes(item.id || item.number));
|
||||
|
||||
if (this._multiselect) {
|
||||
this._multiselect.setSelectedItems(itemsToSelect);
|
||||
|
@ -431,7 +425,6 @@ class AddPeopleDialog extends Component<*, *> {
|
|||
_peopleSearchQueryTypes,
|
||||
_peopleSearchUrl
|
||||
} = this.props;
|
||||
|
||||
const options = {
|
||||
dialOutAuthUrl: _dialOutAuthUrl,
|
||||
addPeopleEnabled,
|
||||
|
|
|
@ -76,9 +76,8 @@ export function getDialInNumbers(url: string): Promise<*> {
|
|||
/**
|
||||
* Removes all non-numeric characters from a string.
|
||||
*
|
||||
* @param {string} text - The string from which to remove all characters
|
||||
* except numbers.
|
||||
* @private
|
||||
* @param {string} text - The string from which to remove all characters except
|
||||
* numbers.
|
||||
* @returns {string} A string with only numbers.
|
||||
*/
|
||||
export function getDigitsOnly(text: string = ''): string {
|
||||
|
@ -126,8 +125,8 @@ export type GetInviteResultsOptions = {
|
|||
* Combines directory search with phone number validation to produce a single
|
||||
* set of invite search results.
|
||||
*
|
||||
* @param {string} query - Text to search.
|
||||
* @param {GetInviteResultsOptions} options - Options to use when searching.
|
||||
* @param {string} query - Text to search.
|
||||
* @param {GetInviteResultsOptions} options - Options to use when searching.
|
||||
* @returns {Promise<*>}
|
||||
*/
|
||||
export function getInviteResultsForQuery(
|
||||
|
@ -166,19 +165,18 @@ export function getInviteResultsForQuery(
|
|||
let numberToVerify = text;
|
||||
|
||||
// When the number to verify does not start with a +, we assume no
|
||||
// proper country code has been entered. In such a case, prepend 1
|
||||
// for the country code. The service currently takes care of
|
||||
// prepending the +.
|
||||
// proper country code has been entered. In such a case, prepend 1 for
|
||||
// the country code. The service currently takes care of prepending the
|
||||
// +.
|
||||
if (!hasCountryCode && !text.startsWith('1')) {
|
||||
numberToVerify = `1${numberToVerify}`;
|
||||
}
|
||||
|
||||
// The validation service works properly when the query is digits
|
||||
// only so ensure only digits get sent.
|
||||
// The validation service works properly when the query is digits only
|
||||
// so ensure only digits get sent.
|
||||
numberToVerify = getDigitsOnly(numberToVerify);
|
||||
|
||||
phoneNumberPromise
|
||||
= checkDialNumber(numberToVerify, dialOutAuthUrl);
|
||||
phoneNumberPromise = checkDialNumber(numberToVerify, dialOutAuthUrl);
|
||||
} else {
|
||||
phoneNumberPromise = Promise.resolve({});
|
||||
}
|
||||
|
@ -190,18 +188,16 @@ export function getInviteResultsForQuery(
|
|||
];
|
||||
|
||||
/**
|
||||
* This check for phone results is for the day the call to
|
||||
* searching people might return phone results as well. When
|
||||
* that day comes this check will make it so the server checks
|
||||
* are honored and the local appending of the number is not
|
||||
* done. The local appending of the phone number can then be
|
||||
* cleaned up when convenient.
|
||||
* This check for phone results is for the day the call to searching
|
||||
* people might return phone results as well. When that day comes
|
||||
* this check will make it so the server checks are honored and the
|
||||
* local appending of the number is not done. The local appending of
|
||||
* the phone number can then be cleaned up when convenient.
|
||||
*/
|
||||
const hasPhoneResult = peopleResults.find(
|
||||
result => result.type === 'phone');
|
||||
const hasPhoneResult
|
||||
= peopleResults.find(result => result.type === 'phone');
|
||||
|
||||
if (!hasPhoneResult
|
||||
&& typeof phoneResults.allow === 'boolean') {
|
||||
if (!hasPhoneResult && typeof phoneResults.allow === 'boolean') {
|
||||
results.push({
|
||||
allowed: phoneResults.allow,
|
||||
country: phoneResults.country,
|
||||
|
@ -216,6 +212,28 @@ export function getInviteResultsForQuery(
|
|||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 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}.
|
||||
* @returns {Object} An object with keys as user types and values as the number
|
||||
* of invites for that type.
|
||||
*/
|
||||
export function getInviteTypeCounts(inviteItems: Array<Object> = []) {
|
||||
const inviteTypeCounts = {};
|
||||
|
||||
inviteItems.forEach(({ type }) => {
|
||||
if (!inviteTypeCounts[type]) {
|
||||
inviteTypeCounts[type] = 0;
|
||||
}
|
||||
inviteTypeCounts[type]++;
|
||||
});
|
||||
|
||||
return inviteTypeCounts;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a post request to an invite service.
|
||||
*
|
||||
|
@ -223,8 +241,8 @@ export function getInviteResultsForQuery(
|
|||
* invitation.
|
||||
* @param {string} inviteUrl - The url to the conference.
|
||||
* @param {string} jwt - The jwt token to pass to the search service.
|
||||
* @param {Immutable.List} inviteItems - The list of the "user" or "room"
|
||||
* type items to invite.
|
||||
* @param {Immutable.List} inviteItems - The list of the "user" or "room" type
|
||||
* items to invite.
|
||||
* @returns {Promise} - The promise created by the request.
|
||||
*/
|
||||
export function invitePeopleAndChatRooms( // eslint-disable-line max-params
|
||||
|
@ -282,24 +300,37 @@ export function isAddPeopleEnabled(state: Object): boolean {
|
|||
* @returns {boolean} Indication of whether dial out is currently enabled.
|
||||
*/
|
||||
export function isDialOutEnabled(state: Object): boolean {
|
||||
const participant = getLocalParticipant(state);
|
||||
const { conference } = state['features/base/conference'];
|
||||
const { isGuest } = state['features/base/jwt'];
|
||||
const { enableUserRolesBasedOnToken } = state['features/base/config'];
|
||||
const participant = getLocalParticipant(state);
|
||||
let dialOutEnabled
|
||||
= participant && participant.role === PARTICIPANT_ROLE.MODERATOR
|
||||
&& conference && conference.isSIPCallingSupported()
|
||||
&& (!enableUserRolesBasedOnToken || !isGuest);
|
||||
|
||||
return participant && participant.role === PARTICIPANT_ROLE.MODERATOR
|
||||
&& conference && conference.isSIPCallingSupported()
|
||||
&& (!enableUserRolesBasedOnToken || !isGuest);
|
||||
if (dialOutEnabled) {
|
||||
// XXX The mobile/react-native app is capable of disabling of dial-out.
|
||||
// Anyway, the Web/React app does not have that capability so default
|
||||
// appropriately.
|
||||
const { app } = state['features/app'];
|
||||
|
||||
dialOutEnabled = app && app.props.dialoOutEnabled;
|
||||
|
||||
return (
|
||||
(typeof dialOutEnabled === 'undefined') || Boolean(dialOutEnabled));
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether a string looks like it could be for a phone number.
|
||||
*
|
||||
* @param {string} text - The text to check whether or not it could be a
|
||||
* phone number.
|
||||
* @private
|
||||
* @returns {boolean} True if the string looks like it could be a phone
|
||||
* @param {string} text - The text to check whether or not it could be a phone
|
||||
* number.
|
||||
* @private
|
||||
* @returns {boolean} True if the string looks like it could be a phone number.
|
||||
*/
|
||||
function isMaybeAPhoneNumber(text: string): boolean {
|
||||
if (!isPhoneNumberRegex().test(text)) {
|
||||
|
@ -365,28 +396,3 @@ export function searchDirectory( // eslint-disable-line max-params
|
|||
return Promise.reject(error);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
export function getInviteTypeCounts(inviteItems: Array<Object> = []) {
|
||||
const inviteTypeCounts = {};
|
||||
|
||||
inviteItems.forEach(({ type }) => {
|
||||
|
||||
if (!inviteTypeCounts[type]) {
|
||||
inviteTypeCounts[type] = 0;
|
||||
}
|
||||
|
||||
inviteTypeCounts[type]++;
|
||||
});
|
||||
|
||||
return inviteTypeCounts;
|
||||
}
|
||||
|
|
|
@ -6,6 +6,7 @@ import { NativeEventEmitter, NativeModules } from 'react-native';
|
|||
import { MiddlewareRegistry } from '../base/redux';
|
||||
import { APP_WILL_MOUNT, APP_WILL_UNMOUNT } from '../app';
|
||||
|
||||
import { invite } from './actions';
|
||||
import {
|
||||
BEGIN_ADD_PEOPLE,
|
||||
_SET_EMITTER_SUBSCRIPTIONS
|
||||
|
@ -15,7 +16,6 @@ import {
|
|||
isAddPeopleEnabled,
|
||||
isDialOutEnabled
|
||||
} from './functions';
|
||||
import { sendInvitesForItems } from './actions';
|
||||
import './middleware.any';
|
||||
|
||||
/**
|
||||
|
@ -133,8 +133,7 @@ function _beginAddPeople({ getState }, next, action) {
|
|||
* @param {Object} event - The details of the event.
|
||||
* @returns {void}
|
||||
*/
|
||||
function _onInvite(
|
||||
{ addPeopleControllerScope, externalAPIScope, invitees }) {
|
||||
function _onInvite({ addPeopleControllerScope, externalAPIScope, invitees }) {
|
||||
const { dispatch, getState } = this; // eslint-disable-line no-invalid-this
|
||||
|
||||
// If there are multiple JitsiMeetView instances alive, they will all get
|
||||
|
@ -145,7 +144,7 @@ function _onInvite(
|
|||
return;
|
||||
}
|
||||
|
||||
dispatch(sendInvitesForItems(invitees))
|
||||
dispatch(invite(invitees))
|
||||
.then(failedInvitees =>
|
||||
Invite.inviteSettled(
|
||||
externalAPIScope,
|
||||
|
|
Loading…
Reference in New Issue