From 7674e90d4de44b2071a3f5b6c9913f2a78958c87 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=94=D0=B0=D0=BC=D1=8F=D0=BD=20=D0=9C=D0=B8=D0=BD=D0=BA?= =?UTF-8?q?=D0=BE=D0=B2?= Date: Fri, 7 Sep 2018 17:48:58 -0500 Subject: [PATCH] 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. --- lang/main.json | 2 +- .../calendar-sync/web/microsoftCalendar.js | 53 ++++---- react/features/google-api/actions.js | 11 +- .../dial-in-info-page/DialInInfoApp.web.js | 5 +- .../components/info-dialog/InfoDialog.web.js | 46 ++----- react/features/invite/functions.js | 121 ++++++++++++++++-- react/features/share-room/actions.js | 4 +- react/features/share-room/middleware.js | 57 ++++----- 8 files changed, 180 insertions(+), 119 deletions(-) diff --git a/lang/main.json b/lang/main.json index d56344c9a..31095b1f8 100644 --- a/lang/main.json +++ b/lang/main.json @@ -430,7 +430,7 @@ "share": { "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": { diff --git a/react/features/calendar-sync/web/microsoftCalendar.js b/react/features/calendar-sync/web/microsoftCalendar.js index cca71455b..0d34fc297 100644 --- a/react/features/calendar-sync/web/microsoftCalendar.js +++ b/react/features/calendar-sync/web/microsoftCalendar.js @@ -329,36 +329,35 @@ export const microsoftCalendarApi = { return Promise.reject('Not authorized, please sign in!'); } - const { dialInNumbersUrl } = getState()['features/base/config']; - const text = getShareInfoText( - location, dialInNumbersUrl !== undefined, true/* use html */); - - - 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}

`; - } - - // replace all new lines from the text with html
- // to make it pretty - body.content += text.split('\n').join('
'); + return getShareInfoText(getState(), location, true/* use html */) + .then(text => { + const client = Client.init({ + authProvider: done => done(null, token) + }); return client - .api(`/me/calendar/events/${id}`) - .patch({ - body, - location: { - 'displayName': location + .api(`/me/events/${id}`) + .get() + .then(description => { + const body = description.body; + + if (description.bodyPreview) { + body.content + = `${description.bodyPreview}

`; } + + // replace all new lines from the text with html + //
to make it pretty + body.content += text.split('\n').join('
'); + + return client + .api(`/me/calendar/events/${id}`) + .patch({ + body, + location: { + 'displayName': location + } + }); }); }); }; diff --git a/react/features/google-api/actions.js b/react/features/google-api/actions.js index b26cb3602..0447587fc 100644 --- a/react/features/google-api/actions.js +++ b/react/features/google-api/actions.js @@ -211,13 +211,8 @@ export function updateProfile() { */ export function updateCalendarEvent( id: string, calendarId: string, location: string) { - return (dispatch: Dispatch<*>, getState: Function) => { - - const { dialInNumbersUrl } = getState()['features/base/config']; - const text = getShareInfoText(location, dialInNumbersUrl !== undefined); - - return googleApi.get() - .then(() => + return (dispatch: Dispatch<*>, getState: Function) => + getShareInfoText(getState(), location) + .then(text => googleApi._updateCalendarEntry(id, calendarId, location, text)); - }; } diff --git a/react/features/invite/components/dial-in-info-page/DialInInfoApp.web.js b/react/features/invite/components/dial-in-info-page/DialInInfoApp.web.js index 62c978334..224563b5f 100644 --- a/react/features/invite/components/dial-in-info-page/DialInInfoApp.web.js +++ b/react/features/invite/components/dial-in-info-page/DialInInfoApp.web.js @@ -10,8 +10,7 @@ import { DialInSummary } from '../dial-in-summary'; import NoRoomError from './NoRoomError'; document.addEventListener('DOMContentLoaded', () => { - const params = parseURLParams(window.location, true, 'search'); - const { room } = params; + const { room } = parseURLParams(window.location, true, 'search'); ReactDOM.render( @@ -19,7 +18,7 @@ document.addEventListener('DOMContentLoaded', () => { ? + room = { room } /> : } , document.getElementById('react') diff --git a/react/features/invite/components/info-dialog/InfoDialog.web.js b/react/features/invite/components/info-dialog/InfoDialog.web.js index c90ec99c4..4a7aec781 100644 --- a/react/features/invite/components/info-dialog/InfoDialog.web.js +++ b/react/features/invite/components/info-dialog/InfoDialog.web.js @@ -7,7 +7,7 @@ import { getInviteURL } from '../../../base/connection'; import { translate } from '../../../base/i18n'; import { isLocalParticipantModerator } from '../../../base/participants'; -import { getDialInfoPageURL } from '../../functions'; +import { _getDefaultPhoneNumber, getDialInfoPageURL } from '../../functions'; import DialInNumber from './DialInNumber'; import PasswordForm from './PasswordForm'; @@ -49,6 +49,11 @@ class InfoDialog extends Component { */ _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 * locked) as defined by room-lock constants. @@ -118,7 +123,7 @@ class InfoDialog extends Component { if (numbers) { this.state.phoneNumber - = this._getDefaultPhoneNumber(numbers, defaultCountry); + = _getDefaultPhoneNumber(numbers, defaultCountry); } /** @@ -157,8 +162,7 @@ class InfoDialog extends Component { const { defaultCountry, numbers } = nextProps.dialIn; this.setState({ - phoneNumber: - this._getDefaultPhoneNumber(numbers, defaultCountry) + phoneNumber: _getDefaultPhoneNumber(numbers, defaultCountry) }); } } @@ -231,35 +235,6 @@ class InfoDialog extends Component { ); } - /** - * Sets the internal state of which dial-in number to display. - * - * @param {Array|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. * @@ -268,7 +243,8 @@ class InfoDialog extends Component { */ _getDialInfoPageURL() { return getDialInfoPageURL( - encodeURIComponent(this.props._conferenceName)); + encodeURIComponent(this.props._conferenceName), + this.props._locationURL); } /** @@ -525,6 +501,7 @@ class InfoDialog extends Component { * _conference: Object, * _conferenceName: string, * _inviteURL: string, + * _locationURL: string, * _locked: string, * _password: string * }} @@ -542,6 +519,7 @@ function _mapStateToProps(state) { _conference: conference, _conferenceName: room, _inviteURL: getInviteURL(state), + _locationURL: state['features/base/connection'].locationURL, _locked: locked, _password: password }; diff --git a/react/features/invite/functions.js b/react/features/invite/functions.js index 467c0dfe3..f9c5518ab 100644 --- a/react/features/invite/functions.js +++ b/react/features/invite/functions.js @@ -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 * (share via mobile or use it for calendar event description). * + * @param {Object} state - The current state. * @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. - * @returns {string} + * @returns {Promise} A {@code Promise} resolving with a + * descriptive text that can be used to invite participants to a meeting. */ export function getShareInfoText( - inviteUrl: string, includeDialInfo: boolean, useHtml: ?boolean) { + state: Object, inviteUrl: string, useHtml: ?boolean): Promise { let roomUrl = inviteUrl; + const includeDialInfo = state['features/base/config'] !== undefined; if (useHtml) { roomUrl = `${roomUrl}`; @@ -421,29 +422,90 @@ export function getShareInfoText( if (includeDialInfo) { const { room } = parseURIString(inviteUrl); - let dialInfoPageUrl = getDialInfoPageURL(room); + let numbersPromise; - if (useHtml) { - dialInfoPageUrl - = `${dialInfoPageUrl}`; + if (state['features/invite'].numbers + && state['features/invite'].conferenceID) { + 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 + = `${dialInfoPageUrl}`; + } + + 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. * * @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} */ -export function getDialInfoPageURL(conferenceName: string) { - const origin = window.location.origin; - const pathParts = window.location.pathname.split('/'); +export function getDialInfoPageURL( + conferenceName: string, + locationURL: Object) { + const origin = locationURL.origin; + const pathParts = locationURL.pathname.split('/'); pathParts.length = pathParts.length - 1; @@ -457,3 +519,34 @@ export function getDialInfoPageURL(conferenceName: string) { return `${origin}${newPath}/static/dialInInfo.html?room=${conferenceName}`; } + +/** + * Sets the internal state of which dial-in number to display. + * + * @param {Array|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; +} diff --git a/react/features/share-room/actions.js b/react/features/share-room/actions.js index c6f8af4f3..09b12e774 100644 --- a/react/features/share-room/actions.js +++ b/react/features/share-room/actions.js @@ -19,9 +19,7 @@ export function beginShareRoom(roomURL: ?string): Function { } roomURL && dispatch({ type: BEGIN_SHARE_ROOM, - roomURL, - includeDialInfo: getState()['features/base/config'] - .dialInNumbersUrl !== undefined + roomURL }); }; } diff --git a/react/features/share-room/middleware.js b/react/features/share-room/middleware.js index 302463eed..23a5d7e01 100644 --- a/react/features/share-room/middleware.js +++ b/react/features/share-room/middleware.js @@ -21,7 +21,7 @@ const logger = require('jitsi-meet-logger').getLogger(__filename); MiddlewareRegistry.register(store => next => action => { switch (action.type) { case BEGIN_SHARE_ROOM: - _shareRoom(action.roomURL, action.includeDialInfo, store.dispatch); + _shareRoom(action.roomURL, store); break; } @@ -32,36 +32,35 @@ MiddlewareRegistry.register(store => next => action => { * Open the native sheet for sharing a specific conference/room URL. * * @param {string} roomURL - The URL of the conference/room to be shared. - * @param {boolean} includeDialInfo - Whether to include or not the dialing - * information link. - * @param {Dispatch} dispatch - The Redux dispatch function. + * @param {Store} store - Redux store. * @private * @returns {void} */ -function _shareRoom( - roomURL: string, includeDialInfo: boolean, dispatch: Function) { - const message = getShareInfoText(roomURL, includeDialInfo); - const title = `${getName()} Conference`; - const onFulfilled - = (shared: boolean) => dispatch(endShareRoom(roomURL, shared)); +function _shareRoom(roomURL: string, { dispatch, getState }) { + getShareInfoText(getState(), roomURL) + .then(message => { + const title = `${getName()} Conference`; + const onFulfilled + = (shared: boolean) => dispatch(endShareRoom(roomURL, shared)); - Share.share( - /* content */ { - message, - title - }, - /* options */ { - dialogTitle: title, // Android - subject: title // iOS - }) - .then( - /* onFulfilled */ value => { - onFulfilled(value.action === Share.sharedAction); - }, - /* onRejected */ reason => { - logger.error( - `Failed to share conference/room URL ${roomURL}:`, - reason); - onFulfilled(false); - }); + Share.share( + /* content */ { + message, + title + }, + /* options */ { + dialogTitle: title, // Android + subject: title // iOS + }) + .then( + /* onFulfilled */ value => { + onFulfilled(value.action === Share.sharedAction); + }, + /* onRejected */ reason => { + logger.error( + `Failed to share conference/room URL ${roomURL}:`, + reason); + onFulfilled(false); + }); + }); }