Adds dial in default number and pin to the text for calendar/share. (#3421)

* Adds dial in default number and pin to the text for calendar/share.

* Handles fail to fetch numbers or conference id.
This commit is contained in:
Дамян Минков 2018-09-07 17:48:58 -05:00 committed by GitHub
parent 1d128e027a
commit 7674e90d4d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 180 additions and 119 deletions

View File

@ -430,7 +430,7 @@
"share": "share":
{ {
"mainText": "Click the following link to join the meeting:\n__roomUrl__", "mainText": "Click the following link to join the meeting:\n__roomUrl__",
"dialInfoText": "\n\n=====\n\nJust want to dial in on your phone?\n\nClick this link to see the dial in phone numbers for this meetings\n__dialInfoPageUrl__" "dialInfoText": "\n\n=====\n\nJust want to dial in on your phone?\n\n__defaultDialInNumber__Click this link to see the dial in phone numbers for this meeting\n__dialInfoPageUrl__"
}, },
"connection": "connection":
{ {

View File

@ -329,36 +329,35 @@ export const microsoftCalendarApi = {
return Promise.reject('Not authorized, please sign in!'); return Promise.reject('Not authorized, please sign in!');
} }
const { dialInNumbersUrl } = getState()['features/base/config']; return getShareInfoText(getState(), location, true/* use html */)
const text = getShareInfoText( .then(text => {
location, dialInNumbersUrl !== undefined, true/* use html */); const client = Client.init({
authProvider: done => done(null, token)
});
const client = Client.init({
authProvider: done => done(null, token)
});
return client
.api(`/me/events/${id}`)
.get()
.then(description => {
const body = description.body;
if (description.bodyPreview) {
body.content = `${description.bodyPreview}<br><br>`;
}
// replace all new lines from the text with html <br>
// to make it pretty
body.content += text.split('\n').join('<br>');
return client return client
.api(`/me/calendar/events/${id}`) .api(`/me/events/${id}`)
.patch({ .get()
body, .then(description => {
location: { const body = description.body;
'displayName': location
if (description.bodyPreview) {
body.content
= `${description.bodyPreview}<br><br>`;
} }
// replace all new lines from the text with html
// <br> to make it pretty
body.content += text.split('\n').join('<br>');
return client
.api(`/me/calendar/events/${id}`)
.patch({
body,
location: {
'displayName': location
}
});
}); });
}); });
}; };

View File

@ -211,13 +211,8 @@ export function updateProfile() {
*/ */
export function updateCalendarEvent( export function updateCalendarEvent(
id: string, calendarId: string, location: string) { id: string, calendarId: string, location: string) {
return (dispatch: Dispatch<*>, getState: Function) => { return (dispatch: Dispatch<*>, getState: Function) =>
getShareInfoText(getState(), location)
const { dialInNumbersUrl } = getState()['features/base/config']; .then(text =>
const text = getShareInfoText(location, dialInNumbersUrl !== undefined);
return googleApi.get()
.then(() =>
googleApi._updateCalendarEntry(id, calendarId, location, text)); googleApi._updateCalendarEntry(id, calendarId, location, text));
};
} }

View File

@ -10,8 +10,7 @@ import { DialInSummary } from '../dial-in-summary';
import NoRoomError from './NoRoomError'; import NoRoomError from './NoRoomError';
document.addEventListener('DOMContentLoaded', () => { document.addEventListener('DOMContentLoaded', () => {
const params = parseURLParams(window.location, true, 'search'); const { room } = parseURLParams(window.location, true, 'search');
const { room } = params;
ReactDOM.render( ReactDOM.render(
<I18nextProvider i18n = { i18next }> <I18nextProvider i18n = { i18next }>
@ -19,7 +18,7 @@ document.addEventListener('DOMContentLoaded', () => {
? <DialInSummary ? <DialInSummary
className = 'dial-in-page' className = 'dial-in-page'
clickableNumbers = { false } clickableNumbers = { false }
room = { params.room } /> room = { room } />
: <NoRoomError className = 'dial-in-page' /> } : <NoRoomError className = 'dial-in-page' /> }
</I18nextProvider>, </I18nextProvider>,
document.getElementById('react') document.getElementById('react')

View File

@ -7,7 +7,7 @@ import { getInviteURL } from '../../../base/connection';
import { translate } from '../../../base/i18n'; import { translate } from '../../../base/i18n';
import { isLocalParticipantModerator } from '../../../base/participants'; import { isLocalParticipantModerator } from '../../../base/participants';
import { getDialInfoPageURL } from '../../functions'; import { _getDefaultPhoneNumber, getDialInfoPageURL } from '../../functions';
import DialInNumber from './DialInNumber'; import DialInNumber from './DialInNumber';
import PasswordForm from './PasswordForm'; import PasswordForm from './PasswordForm';
@ -49,6 +49,11 @@ class InfoDialog extends Component {
*/ */
_inviteURL: PropTypes.string, _inviteURL: PropTypes.string,
/**
* The current location url of the conference.
*/
_locationURL: PropTypes.object,
/** /**
* The value for how the conference is locked (or undefined if not * The value for how the conference is locked (or undefined if not
* locked) as defined by room-lock constants. * locked) as defined by room-lock constants.
@ -118,7 +123,7 @@ class InfoDialog extends Component {
if (numbers) { if (numbers) {
this.state.phoneNumber this.state.phoneNumber
= this._getDefaultPhoneNumber(numbers, defaultCountry); = _getDefaultPhoneNumber(numbers, defaultCountry);
} }
/** /**
@ -157,8 +162,7 @@ class InfoDialog extends Component {
const { defaultCountry, numbers } = nextProps.dialIn; const { defaultCountry, numbers } = nextProps.dialIn;
this.setState({ this.setState({
phoneNumber: phoneNumber: _getDefaultPhoneNumber(numbers, defaultCountry)
this._getDefaultPhoneNumber(numbers, defaultCountry)
}); });
} }
} }
@ -231,35 +235,6 @@ class InfoDialog extends Component {
); );
} }
/**
* Sets the internal state of which dial-in number to display.
*
* @param {Array<string>|Object} dialInNumbers - The array or object of
* numbers to choose a number from.
* @param {string} defaultCountry - The country code for the country
* whose phone number should display.
* @private
* @returns {string|null}
*/
_getDefaultPhoneNumber(dialInNumbers, defaultCountry = 'US') {
if (Array.isArray(dialInNumbers)) {
// Dumbly return the first number if an array.
return dialInNumbers[0];
} else if (Object.keys(dialInNumbers).length > 0) {
const defaultNumbers = dialInNumbers[defaultCountry];
if (defaultNumbers) {
return defaultNumbers[0];
}
const firstRegion = Object.keys(dialInNumbers)[0];
return firstRegion && firstRegion[0];
}
return null;
}
/** /**
* Generates the URL for the static dial in info page. * Generates the URL for the static dial in info page.
* *
@ -268,7 +243,8 @@ class InfoDialog extends Component {
*/ */
_getDialInfoPageURL() { _getDialInfoPageURL() {
return getDialInfoPageURL( return getDialInfoPageURL(
encodeURIComponent(this.props._conferenceName)); encodeURIComponent(this.props._conferenceName),
this.props._locationURL);
} }
/** /**
@ -525,6 +501,7 @@ class InfoDialog extends Component {
* _conference: Object, * _conference: Object,
* _conferenceName: string, * _conferenceName: string,
* _inviteURL: string, * _inviteURL: string,
* _locationURL: string,
* _locked: string, * _locked: string,
* _password: string * _password: string
* }} * }}
@ -542,6 +519,7 @@ function _mapStateToProps(state) {
_conference: conference, _conference: conference,
_conferenceName: room, _conferenceName: room,
_inviteURL: getInviteURL(state), _inviteURL: getInviteURL(state),
_locationURL: state['features/base/connection'].locationURL,
_locked: locked, _locked: locked,
_password: password _password: password
}; };

View File

@ -403,15 +403,16 @@ export function searchDirectory( // eslint-disable-line max-params
* Returns descriptive text that can be used to invite participants to a meeting * Returns descriptive text that can be used to invite participants to a meeting
* (share via mobile or use it for calendar event description). * (share via mobile or use it for calendar event description).
* *
* @param {Object} state - The current state.
* @param {string} inviteUrl - The conference/location URL. * @param {string} inviteUrl - The conference/location URL.
* @param {boolean} includeDialInfo - Whether to include or not the dialing
* information link.
* @param {boolean} useHtml - Whether to return html text. * @param {boolean} useHtml - Whether to return html text.
* @returns {string} * @returns {Promise<string>} A {@code Promise} resolving with a
* descriptive text that can be used to invite participants to a meeting.
*/ */
export function getShareInfoText( export function getShareInfoText(
inviteUrl: string, includeDialInfo: boolean, useHtml: ?boolean) { state: Object, inviteUrl: string, useHtml: ?boolean): Promise<string> {
let roomUrl = inviteUrl; let roomUrl = inviteUrl;
const includeDialInfo = state['features/base/config'] !== undefined;
if (useHtml) { if (useHtml) {
roomUrl = `<a href="${roomUrl}">${roomUrl}</a>`; roomUrl = `<a href="${roomUrl}">${roomUrl}</a>`;
@ -421,29 +422,90 @@ export function getShareInfoText(
if (includeDialInfo) { if (includeDialInfo) {
const { room } = parseURIString(inviteUrl); const { room } = parseURIString(inviteUrl);
let dialInfoPageUrl = getDialInfoPageURL(room); let numbersPromise;
if (useHtml) { if (state['features/invite'].numbers
dialInfoPageUrl && state['features/invite'].conferenceID) {
= `<a href="${dialInfoPageUrl}">${dialInfoPageUrl}</a>`; numbersPromise = Promise.resolve(state['features/invite']);
} else {
// we are requesting numbers and conferenceId directly
// not using updateDialInNumbers, because custom room
// is specified and we do not want to store the data
// in the state
const { dialInConfCodeUrl, dialInNumbersUrl, hosts }
= state['features/base/config'];
const mucURL = hosts && hosts.muc;
if (!dialInConfCodeUrl || !dialInNumbersUrl || !mucURL) {
// URLs for fetching dial in numbers not defined
return Promise.reject();
}
numbersPromise = Promise.all([
getDialInNumbers(dialInNumbersUrl),
getDialInConferenceID(dialInConfCodeUrl, room, mucURL)
]).then(([ { defaultCountry, numbers }, {
conference, id, message } ]) => {
if (!conference || !id) {
return Promise.reject(message);
}
return {
defaultCountry,
numbers,
conferenceID: id
};
});
} }
infoText += i18next.t('share.dialInfoText', { dialInfoPageUrl }); return numbersPromise.then(
({ conferenceID, defaultCountry, numbers }) => {
const phoneNumber
= _getDefaultPhoneNumber(numbers, defaultCountry) || '';
return `${
i18next.t('info.dialInNumber')} ${
phoneNumber} ${
i18next.t('info.dialInConferenceID')} ${
conferenceID}#\n\n`;
})
.catch(error =>
logger.error('Error fetching numbers or conferenceID', error))
.then(defaultDialInNumber => {
let dialInfoPageUrl = getDialInfoPageURL(
room,
state['features/base/connection'].locationURL);
if (useHtml) {
dialInfoPageUrl
= `<a href="${dialInfoPageUrl}">${dialInfoPageUrl}</a>`;
}
infoText += i18next.t('share.dialInfoText', {
defaultDialInNumber,
dialInfoPageUrl });
return infoText;
});
} }
return infoText; return Promise.resolve(infoText);
} }
/** /**
* Generates the URL for the static dial in info page. * Generates the URL for the static dial in info page.
* *
* @param {string} conferenceName - The conference name. * @param {string} conferenceName - The conference name.
* @private * @param {Object} locationURL - The current location URL, the object coming
* from state ['features/base/connection'].locationURL.
* @returns {string} * @returns {string}
*/ */
export function getDialInfoPageURL(conferenceName: string) { export function getDialInfoPageURL(
const origin = window.location.origin; conferenceName: string,
const pathParts = window.location.pathname.split('/'); locationURL: Object) {
const origin = locationURL.origin;
const pathParts = locationURL.pathname.split('/');
pathParts.length = pathParts.length - 1; pathParts.length = pathParts.length - 1;
@ -457,3 +519,34 @@ export function getDialInfoPageURL(conferenceName: string) {
return `${origin}${newPath}/static/dialInInfo.html?room=${conferenceName}`; return `${origin}${newPath}/static/dialInInfo.html?room=${conferenceName}`;
} }
/**
* Sets the internal state of which dial-in number to display.
*
* @param {Array<string>|Object} dialInNumbers - The array or object of
* numbers to choose a number from.
* @param {string} defaultCountry - The country code for the country
* whose phone number should display.
* @private
* @returns {string|null}
*/
export function _getDefaultPhoneNumber(
dialInNumbers: Object,
defaultCountry: string = 'US') {
if (Array.isArray(dialInNumbers)) {
// Dumbly return the first number if an array.
return dialInNumbers[0];
} else if (Object.keys(dialInNumbers).length > 0) {
const defaultNumbers = dialInNumbers[defaultCountry];
if (defaultNumbers) {
return defaultNumbers[0];
}
const firstRegion = Object.keys(dialInNumbers)[0];
return firstRegion && firstRegion[0];
}
return null;
}

View File

@ -19,9 +19,7 @@ export function beginShareRoom(roomURL: ?string): Function {
} }
roomURL && dispatch({ roomURL && dispatch({
type: BEGIN_SHARE_ROOM, type: BEGIN_SHARE_ROOM,
roomURL, roomURL
includeDialInfo: getState()['features/base/config']
.dialInNumbersUrl !== undefined
}); });
}; };
} }

View File

@ -21,7 +21,7 @@ const logger = require('jitsi-meet-logger').getLogger(__filename);
MiddlewareRegistry.register(store => next => action => { MiddlewareRegistry.register(store => next => action => {
switch (action.type) { switch (action.type) {
case BEGIN_SHARE_ROOM: case BEGIN_SHARE_ROOM:
_shareRoom(action.roomURL, action.includeDialInfo, store.dispatch); _shareRoom(action.roomURL, store);
break; break;
} }
@ -32,36 +32,35 @@ MiddlewareRegistry.register(store => next => action => {
* Open the native sheet for sharing a specific conference/room URL. * Open the native sheet for sharing a specific conference/room URL.
* *
* @param {string} roomURL - The URL of the conference/room to be shared. * @param {string} roomURL - The URL of the conference/room to be shared.
* @param {boolean} includeDialInfo - Whether to include or not the dialing * @param {Store} store - Redux store.
* information link.
* @param {Dispatch} dispatch - The Redux dispatch function.
* @private * @private
* @returns {void} * @returns {void}
*/ */
function _shareRoom( function _shareRoom(roomURL: string, { dispatch, getState }) {
roomURL: string, includeDialInfo: boolean, dispatch: Function) { getShareInfoText(getState(), roomURL)
const message = getShareInfoText(roomURL, includeDialInfo); .then(message => {
const title = `${getName()} Conference`; const title = `${getName()} Conference`;
const onFulfilled const onFulfilled
= (shared: boolean) => dispatch(endShareRoom(roomURL, shared)); = (shared: boolean) => dispatch(endShareRoom(roomURL, shared));
Share.share( Share.share(
/* content */ { /* content */ {
message, message,
title title
}, },
/* options */ { /* options */ {
dialogTitle: title, // Android dialogTitle: title, // Android
subject: title // iOS subject: title // iOS
}) })
.then( .then(
/* onFulfilled */ value => { /* onFulfilled */ value => {
onFulfilled(value.action === Share.sharedAction); onFulfilled(value.action === Share.sharedAction);
}, },
/* onRejected */ reason => { /* onRejected */ reason => {
logger.error( logger.error(
`Failed to share conference/room URL ${roomURL}:`, `Failed to share conference/room URL ${roomURL}:`,
reason); reason);
onFulfilled(false); onFulfilled(false);
}); });
});
} }