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:
parent
7396db71fd
commit
f7ddcbbbf3
|
@ -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",
|
||||||
|
|
|
@ -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 => {
|
||||||
|
|
|
@ -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 }
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
};
|
};
|
||||||
|
|
|
@ -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
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -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> }
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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> }
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue