Merge pull request #9204 from jitsi/tavram/sip-fixes

fix(sip) allow sip invites to contain phone numbers
This commit is contained in:
Avram Tudor 2021-05-14 14:33:14 +03:00 committed by GitHub
commit 7db9fc94e2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 62 additions and 37 deletions

View File

@ -5,6 +5,7 @@
"copyInvite": "Copy meeting invitation",
"copyLink": "Copy meeting link",
"copyStream": "Copy live streaming link",
"contacts": "contacts",
"countryNotSupported": "We do not support this destination yet.",
"countryReminder": "Calling outside the US? Please make sure you start with the country code!",
"defaultEmail": "Your Default Email",
@ -16,19 +17,14 @@
"inviteMoreMailSubject": "Join {{appName}} meeting",
"inviteMorePrompt": "Invite more people",
"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",
"noValidNumbers": "Please enter a phone number",
"outlookEmail": "Outlook Email",
"searchNumbers": "Add phone numbers",
"searchPeople": "Search for people",
"searchPeopleAndNumbers": "Search for people or add their phone numbers",
"phoneNumbers": "phone numbers",
"searching": "Searching...",
"shareInvite": "Share meeting invitation",
"shareLink": "Share the meeting link to invite others",
"shareStream": "Share the live streaming link",
"sip": "SIP: {{address}}",
"sipAddresses": "sip addresses",
"telephone": "Telephone: {{number}}",
"title": "Invite people to this meeting",
"yahooEmail": "Yahoo Email"
@ -379,6 +375,7 @@
"inviteLiveStream": "To view the live stream of this meeting, click this link: {{url}}",
"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}}",
"inviteSipEndpoint": "To join using the SIP address, enter this: {{sipUri}}",
"inviteURLFirstPartGeneral": "You are invited to join a meeting.",
"inviteURLFirstPartPersonal": "{{name}} is inviting you to a meeting.\n",
"inviteURLSecondPart": "\nJoin the meeting:\n{{url}}\n",

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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