fix(sip) allow sip invites to contain phone numbers

display sip address in invites
hide lobby password for sip users
tidy up invite placeholders
This commit is contained in:
Tudor-Ovidiu Avram 2021-05-12 16:21:22 +03:00
parent 7396db71fd
commit f7ddcbbbf3
9 changed files with 62 additions and 37 deletions

View File

@ -5,6 +5,7 @@
"copyInvite": "Copy meeting invitation", "copyInvite": "Copy meeting invitation",
"copyLink": "Copy meeting link", "copyLink": "Copy meeting link",
"copyStream": "Copy live streaming link", "copyStream": "Copy live streaming link",
"contacts": "contacts",
"countryNotSupported": "We do not support this destination yet.", "countryNotSupported": "We do not support this destination yet.",
"countryReminder": "Calling outside the US? Please make sure you start with the country code!", "countryReminder": "Calling outside the US? Please make sure you start with the country code!",
"defaultEmail": "Your Default Email", "defaultEmail": "Your Default Email",
@ -16,19 +17,14 @@
"inviteMoreMailSubject": "Join {{appName}} meeting", "inviteMoreMailSubject": "Join {{appName}} meeting",
"inviteMorePrompt": "Invite more people", "inviteMorePrompt": "Invite more people",
"linkCopied": "Link copied to clipboard", "linkCopied": "Link copied to clipboard",
"loading": "Searching for people and phone numbers",
"loadingNumber": "Validating phone number",
"loadingPeople": "Searching for people to invite",
"noResults": "No matching search results", "noResults": "No matching search results",
"noValidNumbers": "Please enter a phone number",
"outlookEmail": "Outlook Email", "outlookEmail": "Outlook Email",
"searchNumbers": "Add phone numbers", "phoneNumbers": "phone numbers",
"searchPeople": "Search for people", "searching": "Searching...",
"searchPeopleAndNumbers": "Search for people or add their phone numbers",
"shareInvite": "Share meeting invitation", "shareInvite": "Share meeting invitation",
"shareLink": "Share the meeting link to invite others", "shareLink": "Share the meeting link to invite others",
"shareStream": "Share the live streaming link", "shareStream": "Share the live streaming link",
"sip": "SIP: {{address}}", "sipAddresses": "sip addresses",
"telephone": "Telephone: {{number}}", "telephone": "Telephone: {{number}}",
"title": "Invite people to this meeting", "title": "Invite people to this meeting",
"yahooEmail": "Yahoo Email" "yahooEmail": "Yahoo Email"
@ -379,6 +375,7 @@
"inviteLiveStream": "To view the live stream of this meeting, click this link: {{url}}", "inviteLiveStream": "To view the live stream of this meeting, click this link: {{url}}",
"invitePhone": "To join by phone instead, tap this: {{number}},,{{conferenceID}}#\n", "invitePhone": "To join by phone instead, tap this: {{number}},,{{conferenceID}}#\n",
"invitePhoneAlternatives": "Looking for a different dial-in number?\nSee meeting dial-in numbers: {{url}}\n\n\nIf also dialing-in through a room phone, join without connecting to audio: {{silentUrl}}", "invitePhoneAlternatives": "Looking for a different dial-in number?\nSee meeting dial-in numbers: {{url}}\n\n\nIf also dialing-in through a room phone, join without connecting to audio: {{silentUrl}}",
"inviteSipEndpoint": "To join using the SIP address, enter this: {{sipUri}}",
"inviteURLFirstPartGeneral": "You are invited to join a meeting.", "inviteURLFirstPartGeneral": "You are invited to join a meeting.",
"inviteURLFirstPartPersonal": "{{name}} is inviting you to a meeting.\n", "inviteURLFirstPartPersonal": "{{name}} is inviting you to a meeting.\n",
"inviteURLSecondPart": "\nJoin the meeting:\n{{url}}\n", "inviteURLSecondPart": "\nJoin the meeting:\n{{url}}\n",

View File

@ -215,7 +215,7 @@ export function updateDialInNumbers() {
getDialInNumbers(dialInNumbersUrl, room, mucURL), getDialInNumbers(dialInNumbersUrl, room, mucURL),
getDialInConferenceID(dialInConfCodeUrl, room, mucURL) getDialInConferenceID(dialInConfCodeUrl, room, mucURL)
]) ])
.then(([ dialInNumbers, { conference, id, message } ]) => { .then(([ dialInNumbers, { conference, id, message, sipUri } ]) => {
if (!conference || !id) { if (!conference || !id) {
return Promise.reject(message); return Promise.reject(message);
} }
@ -223,7 +223,8 @@ export function updateDialInNumbers() {
dispatch({ dispatch({
type: UPDATE_DIAL_IN_NUMBERS_SUCCESS, type: UPDATE_DIAL_IN_NUMBERS_SUCCESS,
conferenceID: id, conferenceID: id,
dialInNumbers dialInNumbers,
sipUri
}); });
}) })
.catch(error => { .catch(error => {

View File

@ -57,6 +57,8 @@ class InviteContactsForm extends AbstractAddPeopleDialog<Props, State> {
_resourceClient: Object; _resourceClient: Object;
_translations: Object;
state = { state = {
addToCallError: false, addToCallError: false,
addToCallInProgress: false, addToCallInProgress: false,
@ -86,6 +88,16 @@ class InviteContactsForm extends AbstractAddPeopleDialog<Props, State> {
makeQuery: this._query, makeQuery: this._query,
parseResults: this._parseQueryResults parseResults: this._parseQueryResults
}; };
const { t } = props;
this._translations = {
_dialOutEnabled: t('addPeople.phoneNumbers'),
_addPeopleEnabled: t('addPeople.contacts'),
_sipInviteEnabled: t('addPeople.sipAddresses')
};
} }
/** /**
@ -118,30 +130,29 @@ class InviteContactsForm extends AbstractAddPeopleDialog<Props, State> {
_addPeopleEnabled, _addPeopleEnabled,
_dialOutEnabled, _dialOutEnabled,
_isVpaas, _isVpaas,
_sipInviteEnabled,
t t
} = this.props; } = this.props;
const footerText = this._renderFooterText(); const footerText = this._renderFooterText();
let isMultiSelectDisabled = this.state.addToCallInProgress; let isMultiSelectDisabled = this.state.addToCallInProgress;
let placeholder; const loadingMessage = 'addPeople.searching';
let loadingMessage; const noMatches = 'addPeople.noResults';
let noMatches;
if (_addPeopleEnabled && _dialOutEnabled) { const features = {
loadingMessage = 'addPeople.loading'; _dialOutEnabled,
noMatches = 'addPeople.noResults'; _addPeopleEnabled,
placeholder = 'addPeople.searchPeopleAndNumbers'; _sipInviteEnabled
} else if (_addPeopleEnabled) { };
loadingMessage = 'addPeople.loadingPeople';
noMatches = 'addPeople.noResults'; const computedPlaceholder = Object.keys(features)
placeholder = 'addPeople.searchPeople'; .filter(v => Boolean(features[v]))
} else if (_dialOutEnabled) { .map(v => this._translations[v])
loadingMessage = 'addPeople.loadingNumber'; .join(', ');
noMatches = 'addPeople.noValidNumbers';
placeholder = 'addPeople.searchNumbers'; const placeholder = computedPlaceholder ? `${t('dialog.add')} ${computedPlaceholder}` : t('addPeople.disabled');
} else {
if (!computedPlaceholder) {
isMultiSelectDisabled = true; isMultiSelectDisabled = true;
noMatches = 'addPeople.noResults';
placeholder = 'addPeople.disabled';
} }
return ( return (
@ -156,7 +167,7 @@ class InviteContactsForm extends AbstractAddPeopleDialog<Props, State> {
noMatchesFound = { t(noMatches) } noMatchesFound = { t(noMatches) }
onItemSelected = { this._onItemSelected } onItemSelected = { this._onItemSelected }
onSelectionChange = { this._onSelectionChange } onSelectionChange = { this._onSelectionChange }
placeholder = { t(placeholder) } placeholder = { placeholder }
ref = { this._setMultiSelectElement } ref = { this._setMultiSelectElement }
resourceClient = { this._resourceClient } resourceClient = { this._resourceClient }
shouldFitContainer = { true } shouldFitContainer = { true }

View File

@ -47,7 +47,7 @@ export const OUTGOING_CALL_START_SOUND_ID = 'OUTGOING_CALL_START_SOUND_ID';
* Regex for matching sip addresses. * Regex for matching sip addresses.
*/ */
// 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-Z0-9]+(?:([^\s>:@]+)(?::([^\s@>]+))?@)?([\w\-.]+)(?::(\d+))?((?:;[^\s=?>;]+(?:=[^\s?;]+)?)*)(?:\?(([^\s&=>]+=[^\s&=>]+)(&[^\s&=>]+=[^\s&=>]+)*))?$/;
/** /**
* Different invite types mapping * Different invite types mapping

View File

@ -301,6 +301,14 @@ export function getInviteText({
invite = `${invite}\n${dial}\n${moreNumbers}`; invite = `${invite}\n${dial}\n${moreNumbers}`;
} }
if (dialIn.sipUri) {
const sipText = t('info.inviteSipEndpoint', {
sipUri: dialIn.sipUri
});
invite = `${invite}\n${sipText}`;
}
return invite; return invite;
} }

View File

@ -60,6 +60,7 @@ ReducerRegistry.register('features/invite', (state = DEFAULT_STATE, action) => {
...state, ...state,
conferenceID: action.conferenceID, conferenceID: action.conferenceID,
numbers: action.dialInNumbers, numbers: action.dialInNumbers,
sipUri: action.sipUri,
numbersEnabled: true, numbersEnabled: true,
numbersFetched: true numbersFetched: true
}; };

View File

@ -46,6 +46,11 @@ export type Props = {
*/ */
_passwordJoinFailed: boolean, _passwordJoinFailed: boolean,
/**
* True if the password field should be available for lobby participants.
*/
_renderPassword: boolean,
/** /**
* The Redux dispatch function. * The Redux dispatch function.
*/ */
@ -365,6 +370,7 @@ export function _mapStateToProps(state: Object): $Shape<Props> {
const localParticipant = getLocalParticipant(state); const localParticipant = getLocalParticipant(state);
const participantId = localParticipant?.id; const participantId = localParticipant?.id;
const { knocking, passwordJoinFailed } = state['features/lobby']; const { knocking, passwordJoinFailed } = state['features/lobby'];
const { iAmSipGateway } = state['features/base/config'];
return { return {
_knocking: knocking, _knocking: knocking,
@ -372,6 +378,7 @@ export function _mapStateToProps(state: Object): $Shape<Props> {
_participantEmail: localParticipant?.email, _participantEmail: localParticipant?.email,
_participantId: participantId, _participantId: participantId,
_participantName: localParticipant?.name, _participantName: localParticipant?.name,
_passwordJoinFailed: passwordJoinFailed _passwordJoinFailed: passwordJoinFailed,
_renderPassword: !iAmSipGateway
}; };
} }

View File

@ -208,7 +208,7 @@ class LobbyScreen extends AbstractLobbyScreen {
* @inheritdoc * @inheritdoc
*/ */
_renderStandardButtons() { _renderStandardButtons() {
const { _knocking, t } = this.props; const { _knocking, _renderPassword, t } = this.props;
return ( return (
<> <>
@ -223,7 +223,7 @@ class LobbyScreen extends AbstractLobbyScreen {
{ t('lobby.knockButton') } { t('lobby.knockButton') }
</Text> </Text>
</TouchableOpacity> } </TouchableOpacity> }
<TouchableOpacity { _renderPassword && <TouchableOpacity
onPress = { this._onSwitchToPasswordMode } onPress = { this._onSwitchToPasswordMode }
style = { [ style = { [
styles.button, styles.button,
@ -232,7 +232,7 @@ class LobbyScreen extends AbstractLobbyScreen {
<Text> <Text>
{ t('lobby.enterPasswordButton') } { t('lobby.enterPasswordButton') }
</Text> </Text>
</TouchableOpacity> </TouchableOpacity> }
</> </>
); );
} }

View File

@ -152,7 +152,7 @@ class LobbyScreen extends AbstractLobbyScreen {
* @inheritdoc * @inheritdoc
*/ */
_renderStandardButtons() { _renderStandardButtons() {
const { _knocking, t } = this.props; const { _knocking, _renderPassword, t } = this.props;
return ( return (
<> <>
@ -163,12 +163,12 @@ class LobbyScreen extends AbstractLobbyScreen {
type = 'primary'> type = 'primary'>
{ t('lobby.knockButton') } { t('lobby.knockButton') }
</ActionButton> } </ActionButton> }
<ActionButton {_renderPassword && <ActionButton
onClick = { this._onSwitchToPasswordMode } onClick = { this._onSwitchToPasswordMode }
testId = 'lobby.enterPasswordButton' testId = 'lobby.enterPasswordButton'
type = 'secondary'> type = 'secondary'>
{ t('lobby.enterPasswordButton') } { t('lobby.enterPasswordButton') }
</ActionButton> </ActionButton> }
</> </>
); );
} }