Merge pull request #9184 from jitsi/tavram/invite-types

fix(invite) fix notifications for phone invites
This commit is contained in:
Avram Tudor 2021-05-11 16:23:20 +03:00 committed by GitHub
commit 6e91665987
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 60 additions and 28 deletions

View File

@ -15,6 +15,7 @@ import {
UPDATE_DIAL_IN_NUMBERS_FAILED, UPDATE_DIAL_IN_NUMBERS_FAILED,
UPDATE_DIAL_IN_NUMBERS_SUCCESS UPDATE_DIAL_IN_NUMBERS_SUCCESS
} from './actionTypes'; } from './actionTypes';
import { INVITE_TYPES } from './constants';
import { import {
getDialInConferenceID, getDialInConferenceID,
getDialInNumbers, getDialInNumbers,
@ -76,7 +77,7 @@ export function invite(
if (showCalleeInfo if (showCalleeInfo
&& !calleeInfoVisible && !calleeInfoVisible
&& invitees.length === 1 && invitees.length === 1
&& invitees[0].type === 'user' && invitees[0].type === INVITE_TYPES.USER
&& participants.length === 1) { && participants.length === 1) {
dispatch(setCalleeInfoVisible(true, invitees[0])); dispatch(setCalleeInfoVisible(true, invitees[0]));
} }
@ -110,7 +111,7 @@ export function invite(
// First create all promises for dialing out. // First create all promises for dialing out.
const phoneNumbers const phoneNumbers
= invitesLeftToSend.filter(({ type }) => type === 'phone'); = invitesLeftToSend.filter(({ type }) => type === INVITE_TYPES.PHONE);
// For each number, dial out. On success, remove the number from // For each number, dial out. On success, remove the number from
// {@link invitesLeftToSend}. // {@link invitesLeftToSend}.
@ -131,7 +132,7 @@ export function invite(
const usersAndRooms const usersAndRooms
= invitesLeftToSend.filter( = invitesLeftToSend.filter(
({ type }) => type === 'user' || type === 'room'); ({ type }) => [ INVITE_TYPES.USER, INVITE_TYPES.ROOM ].includes(type));
if (usersAndRooms.length) { if (usersAndRooms.length) {
// Send a request to invite all the rooms and users. On success, // Send a request to invite all the rooms and users. On success,
@ -146,7 +147,7 @@ export function invite(
.then(() => { .then(() => {
invitesLeftToSend invitesLeftToSend
= invitesLeftToSend.filter( = invitesLeftToSend.filter(
({ type }) => type !== 'user' && type !== 'room'); ({ type }) => ![ INVITE_TYPES.USER, INVITE_TYPES.ROOM ].includes(type));
}) })
.catch(error => { .catch(error => {
dispatch(setCalleeInfoVisible(false)); dispatch(setCalleeInfoVisible(false));
@ -159,17 +160,17 @@ export function invite(
// Sipgw calls are fire and forget. Invite them to the conference, then // Sipgw calls are fire and forget. Invite them to the conference, then
// immediately remove them from invitesLeftToSend. // immediately remove them from invitesLeftToSend.
const vrooms const vrooms
= invitesLeftToSend.filter(({ type }) => type === 'videosipgw'); = invitesLeftToSend.filter(({ type }) => type === INVITE_TYPES.VIDEO_ROOM);
conference conference
&& vrooms.length > 0 && vrooms.length > 0
&& dispatch(inviteVideoRooms(conference, vrooms)); && dispatch(inviteVideoRooms(conference, vrooms));
invitesLeftToSend invitesLeftToSend
= invitesLeftToSend.filter(({ type }) => type !== 'videosipgw'); = invitesLeftToSend.filter(({ type }) => type !== INVITE_TYPES.VIDEO_ROOM);
const sipEndpoints const sipEndpoints
= invitesLeftToSend.filter(({ type }) => type === 'sip'); = invitesLeftToSend.filter(({ type }) => type === INVITE_TYPES.SIP);
conference && inviteSipEndpoints( conference && inviteSipEndpoints(
sipEndpoints, sipEndpoints,
@ -182,7 +183,7 @@ export function invite(
); );
invitesLeftToSend invitesLeftToSend
= invitesLeftToSend.filter(({ type }) => type !== 'sip'); = invitesLeftToSend.filter(({ type }) => type !== INVITE_TYPES.SIP);
return ( return (
Promise.all(allInvitePromises) Promise.all(allInvitePromises)

View File

@ -8,6 +8,7 @@ import {
showNotification showNotification
} from '../../../notifications'; } from '../../../notifications';
import { invite } from '../../actions'; import { invite } from '../../actions';
import { INVITE_TYPES } from '../../constants';
import { import {
getInviteResultsForQuery, getInviteResultsForQuery,
getInviteTypeCounts, getInviteTypeCounts,
@ -100,6 +101,24 @@ export default class AbstractAddPeopleDialog<P: Props, S: State>
this._query = this._query.bind(this); this._query = this._query.bind(this);
} }
/**
* Retrieves the notification display name for the invitee.
*
* @param {Object} invitee - The invitee object.
* @returns {string}
*/
_getDisplayName(invitee) {
if (invitee.type === INVITE_TYPES.PHONE) {
return invitee.number;
}
if (invitee.type === INVITE_TYPES.SIP) {
return invitee.address;
}
return invitee.name;
}
/** /**
* Invite people and numbers to the conference. The logic works by inviting * Invite people and numbers to the conference. The logic works by inviting
* numbers, people/rooms, sip endpoints and videosipgw in parallel. All invitees are * numbers, people/rooms, sip endpoints and videosipgw in parallel. All invitees are
@ -160,7 +179,7 @@ export default class AbstractAddPeopleDialog<P: Props, S: State>
if (invitedCount >= 3) { if (invitedCount >= 3) {
notificationProps = { notificationProps = {
titleArguments: { titleArguments: {
name: invitees[0].name || invitees[0].address, name: this._getDisplayName(invitees[0]),
count: invitedCount - 1 count: invitedCount - 1
}, },
titleKey: 'notify.invitedThreePlusMembers' titleKey: 'notify.invitedThreePlusMembers'
@ -168,15 +187,15 @@ export default class AbstractAddPeopleDialog<P: Props, S: State>
} else if (invitedCount === 2) { } else if (invitedCount === 2) {
notificationProps = { notificationProps = {
titleArguments: { titleArguments: {
first: invitees[0].name || invitees[0].address, first: this._getDisplayName(invitees[0]),
second: invitees[1].name || invitees[1].address second: this._getDisplayName(invitees[1])
}, },
titleKey: 'notify.invitedTwoMembers' titleKey: 'notify.invitedTwoMembers'
}; };
} else if (invitedCount) { } else if (invitedCount) {
notificationProps = { notificationProps = {
titleArguments: { titleArguments: {
name: invitees[0].name || invitees[0].address name: this._getDisplayName(invitees[0])
}, },
titleKey: 'notify.invitedOneMember' titleKey: 'notify.invitedOneMember'
}; };

View File

@ -31,7 +31,7 @@ import {
} from '../../../../base/react'; } from '../../../../base/react';
import { connect } from '../../../../base/redux'; import { connect } from '../../../../base/redux';
import { beginShareRoom } from '../../../../share-room'; import { beginShareRoom } from '../../../../share-room';
import { ADD_PEOPLE_DIALOG_VIEW_ID } from '../../../constants'; import { ADD_PEOPLE_DIALOG_VIEW_ID, INVITE_TYPES } from '../../../constants';
import AbstractAddPeopleDialog, { import AbstractAddPeopleDialog, {
type Props as AbstractProps, type Props as AbstractProps,
type State as AbstractState, type State as AbstractState,
@ -242,13 +242,13 @@ class AddPeopleDialog extends AbstractAddPeopleDialog<Props, State> {
const { item } = flatListItem; const { item } = flatListItem;
switch (item.type) { switch (item.type) {
case 'phone': case INVITE_TYPES.PHONE:
return { return {
avatar: IconPhone, avatar: IconPhone,
key: item.number, key: item.number,
title: item.number title: item.number
}; };
case 'user': case INVITE_TYPES.USER:
return { return {
avatar: item.avatar, avatar: item.avatar,
key: item.id || item.user_id, key: item.id || item.user_id,
@ -273,7 +273,7 @@ class AddPeopleDialog extends AbstractAddPeopleDialog<Props, State> {
* @returns {string} * @returns {string}
*/ */
_keyExtractor(item) { _keyExtractor(item) {
return item.type === 'user' ? item.id || item.user_id : item.number; return item.type === INVITE_TYPES.USER ? item.id || item.user_id : item.number;
} }
_onClearField: () => void _onClearField: () => void
@ -340,7 +340,7 @@ class AddPeopleDialog extends AbstractAddPeopleDialog<Props, State> {
_onPressItem(item) { _onPressItem(item) {
return () => { return () => {
const { inviteItems } = this.state; const { inviteItems } = this.state;
const finderKey = item.type === 'phone' ? 'number' : 'user_id'; const finderKey = item.type === INVITE_TYPES.PHONE ? 'number' : 'user_id';
if (inviteItems.find( if (inviteItems.find(
_.matchesProperty(finderKey, item[finderKey]))) { _.matchesProperty(finderKey, item[finderKey]))) {
@ -504,10 +504,10 @@ class AddPeopleDialog extends AbstractAddPeopleDialog<Props, State> {
} }
switch (item.type) { switch (item.type) {
case 'phone': case INVITE_TYPES.PHONE:
selected = inviteItems.find(_.matchesProperty('number', item.number)); selected = inviteItems.find(_.matchesProperty('number', item.number));
break; break;
case 'user': case INVITE_TYPES.USER:
selected = item.id selected = item.id
? inviteItems.find(_.matchesProperty('id', item.id)) ? inviteItems.find(_.matchesProperty('id', item.id))
: inviteItems.find(_.matchesProperty('user_id', item.user_id)); : inviteItems.find(_.matchesProperty('user_id', item.user_id));

View File

@ -12,6 +12,7 @@ import { MultiSelectAutocomplete } from '../../../../base/react';
import { connect } from '../../../../base/redux'; import { connect } from '../../../../base/redux';
import { isVpaasMeeting } from '../../../../billing-counter/functions'; import { isVpaasMeeting } from '../../../../billing-counter/functions';
import { hideAddPeopleDialog } from '../../../actions'; import { hideAddPeopleDialog } from '../../../actions';
import { INVITE_TYPES } from '../../../constants';
import AbstractAddPeopleDialog, { import AbstractAddPeopleDialog, {
type Props as AbstractProps, type Props as AbstractProps,
type State, type State,
@ -181,7 +182,7 @@ class InviteContactsForm extends AbstractAddPeopleDialog<Props, State> {
* @returns {Object} The item to display as selected in the input. * @returns {Object} The item to display as selected in the input.
*/ */
_onItemSelected(item) { _onItemSelected(item) {
if (item.item.type === 'phone') { if (item.item.type === INVITE_TYPES.PHONE) {
item.content = item.item.number; item.content = item.item.number;
} }
@ -285,7 +286,7 @@ class InviteContactsForm extends AbstractAddPeopleDialog<Props, State> {
*/ */
_parseQueryResults(response = []) { _parseQueryResults(response = []) {
const { t, _dialOutEnabled } = this.props; const { t, _dialOutEnabled } = this.props;
const users = response.filter(item => item.type !== 'phone' && item.type !== 'sip'); const users = response.filter(item => item.type === INVITE_TYPES.USER);
const userDisplayItems = []; const userDisplayItems = [];
for (const user of users) { for (const user of users) {
@ -309,7 +310,7 @@ class InviteContactsForm extends AbstractAddPeopleDialog<Props, State> {
content: `${phone} (${name})`, content: `${phone} (${name})`,
elemBefore: elemAvatar, elemBefore: elemAvatar,
item: { item: {
type: 'phone', type: INVITE_TYPES.PHONE,
number: phone number: phone
}, },
tag: { tag: {
@ -320,7 +321,7 @@ class InviteContactsForm extends AbstractAddPeopleDialog<Props, State> {
} }
} }
const numbers = response.filter(item => item.type === 'phone'); const numbers = response.filter(item => item.type === INVITE_TYPES.PHONE);
const telephoneIcon = this._renderTelephoneIcon(); const telephoneIcon = this._renderTelephoneIcon();
const numberDisplayItems = numbers.map(number => { const numberDisplayItems = numbers.map(number => {
@ -349,7 +350,7 @@ class InviteContactsForm extends AbstractAddPeopleDialog<Props, State> {
}); });
const sipAddresses = response.filter(item => item.type === 'sip'); const sipAddresses = response.filter(item => item.type === INVITE_TYPES.SIP);
const sipDisplayItems = sipAddresses.map(sip => { const sipDisplayItems = sipAddresses.map(sip => {
return { return {

View File

@ -48,3 +48,14 @@ export const OUTGOING_CALL_START_SOUND_ID = 'OUTGOING_CALL_START_SOUND_ID';
*/ */
// eslint-disable-next-line max-len // eslint-disable-next-line max-len
export const SIP_ADDRESS_REGEX = /^[a-zA-Z]+(?:([^\s>:@]+)(?::([^\s@>]+))?@)?([\w\-.]+)(?::(\d+))?((?:;[^\s=?>;]+(?:=[^\s?;]+)?)*)(?:\?(([^\s&=>]+=[^\s&=>]+)(&[^\s&=>]+=[^\s&=>]+)*))?$/; export const SIP_ADDRESS_REGEX = /^[a-zA-Z]+(?:([^\s>:@]+)(?::([^\s@>]+))?@)?([\w\-.]+)(?::(\d+))?((?:;[^\s=?>;]+(?:=[^\s?;]+)?)*)(?:\?(([^\s&=>]+=[^\s&=>]+)(&[^\s&=>]+=[^\s&=>]+)*))?$/;
/**
* Different invite types mapping
*/
export const INVITE_TYPES = {
PHONE: 'phone',
ROOM: 'room',
SIP: 'sip',
USER: 'user',
VIDEO_ROOM: 'videosipgw'
};

View File

@ -10,7 +10,7 @@ import { toState } from '../base/redux';
import { doGetJSON, parseURIString } from '../base/util'; import { doGetJSON, parseURIString } from '../base/util';
import { isVpaasMeeting } from '../billing-counter/functions'; import { isVpaasMeeting } from '../billing-counter/functions';
import { SIP_ADDRESS_REGEX } from './constants'; import { INVITE_TYPES, SIP_ADDRESS_REGEX } from './constants';
import logger from './logger'; import logger from './logger';
declare var $: Function; declare var $: Function;
@ -227,13 +227,13 @@ export function getInviteResultsForQuery(
* the phone number can then be cleaned up when convenient. * the phone number can then be cleaned up when convenient.
*/ */
const hasPhoneResult const hasPhoneResult
= peopleResults.find(result => result.type === 'phone'); = peopleResults.find(result => result.type === INVITE_TYPES.PHONE);
if (!hasPhoneResult && typeof phoneResults.allow === 'boolean') { if (!hasPhoneResult && typeof phoneResults.allow === 'boolean') {
results.push({ results.push({
allowed: phoneResults.allow, allowed: phoneResults.allow,
country: phoneResults.country, country: phoneResults.country,
type: 'phone', type: INVITE_TYPES.PHONE,
number: phoneResults.phone, number: phoneResults.phone,
originalEntry: text, originalEntry: text,
showCountryCodeReminder: !hasCountryCode showCountryCodeReminder: !hasCountryCode
@ -242,7 +242,7 @@ export function getInviteResultsForQuery(
if (sipInviteEnabled && isASipAddress(text)) { if (sipInviteEnabled && isASipAddress(text)) {
results.push({ results.push({
type: 'sip', type: INVITE_TYPES.SIP,
address: text address: text
}); });
} }