Implements calendar entries edit. (#3382)
* Implements calendar entries edit. Share text generation between calendar-sync and the share-room feature. * Fixing comments. * Clone the event element we modify on update.
This commit is contained in:
parent
dba7f2d429
commit
7267f386dc
|
@ -424,6 +424,11 @@
|
||||||
],
|
],
|
||||||
"and": "and"
|
"and": "and"
|
||||||
},
|
},
|
||||||
|
"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__"
|
||||||
|
},
|
||||||
"connection":
|
"connection":
|
||||||
{
|
{
|
||||||
"ERROR": "Error",
|
"ERROR": "Error",
|
||||||
|
|
|
@ -300,7 +300,15 @@ export function parseStandardURIString(str: string) {
|
||||||
* references a Jitsi Meet resource (location).
|
* references a Jitsi Meet resource (location).
|
||||||
* @public
|
* @public
|
||||||
* @returns {{
|
* @returns {{
|
||||||
* room: (string|undefined)
|
* contextRoot: string,
|
||||||
|
* hash: string,
|
||||||
|
* host: string,
|
||||||
|
* hostname: string,
|
||||||
|
* pathname: string,
|
||||||
|
* port: string,
|
||||||
|
* protocol: string,
|
||||||
|
* room: (string|undefined),
|
||||||
|
* search: string
|
||||||
* }}
|
* }}
|
||||||
*/
|
*/
|
||||||
export function parseURIString(uri: ?string) {
|
export function parseURIString(uri: ?string) {
|
||||||
|
|
|
@ -12,6 +12,7 @@ import {
|
||||||
SET_CALENDAR_PROFILE_EMAIL
|
SET_CALENDAR_PROFILE_EMAIL
|
||||||
} from './actionTypes';
|
} from './actionTypes';
|
||||||
import { _getCalendarIntegration, isCalendarEnabled } from './functions';
|
import { _getCalendarIntegration, isCalendarEnabled } from './functions';
|
||||||
|
import { generateRoomWithoutSeparator } from '../welcome';
|
||||||
|
|
||||||
const logger = require('jitsi-meet-logger').getLogger(__filename);
|
const logger = require('jitsi-meet-logger').getLogger(__filename);
|
||||||
|
|
||||||
|
@ -242,3 +243,50 @@ export function updateProfile(calendarType: string): Function {
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates calendar event by generating new invite URL and editing the event
|
||||||
|
* adding some descriptive text and location.
|
||||||
|
*
|
||||||
|
* @param {string} id - The event id.
|
||||||
|
* @param {string} calendarId - The id of the calendar to use.
|
||||||
|
* @returns {Function}
|
||||||
|
*/
|
||||||
|
export function updateCalendarEvent(id: string, calendarId: string): Function {
|
||||||
|
return (dispatch: Dispatch<*>, getState: Function) => {
|
||||||
|
|
||||||
|
const { integrationType } = getState()['features/calendar-sync'];
|
||||||
|
const integration = _getCalendarIntegration(integrationType);
|
||||||
|
|
||||||
|
if (!integration) {
|
||||||
|
return Promise.reject('No integration found');
|
||||||
|
}
|
||||||
|
|
||||||
|
const { locationURL } = getState()['features/base/connection'];
|
||||||
|
const newRoomName = generateRoomWithoutSeparator();
|
||||||
|
let href = locationURL.href;
|
||||||
|
|
||||||
|
href.endsWith('/') || (href += '/');
|
||||||
|
|
||||||
|
const roomURL = `${href}${newRoomName}`;
|
||||||
|
|
||||||
|
return dispatch(integration.updateCalendarEvent(
|
||||||
|
id, calendarId, roomURL))
|
||||||
|
.then(() => {
|
||||||
|
// make a copy of the array
|
||||||
|
const events
|
||||||
|
= getState()['features/calendar-sync'].events.slice(0);
|
||||||
|
|
||||||
|
const eventIx = events.findIndex(
|
||||||
|
e => e.id === id && e.calendarId === calendarId);
|
||||||
|
|
||||||
|
// clone the event we will modify
|
||||||
|
const newEvent = Object.assign({}, events[eventIx]);
|
||||||
|
|
||||||
|
newEvent.url = roomURL;
|
||||||
|
events[eventIx] = newEvent;
|
||||||
|
|
||||||
|
return dispatch(setCalendarEvents(events));
|
||||||
|
});
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
|
@ -90,19 +90,32 @@ function _parseCalendarEntry(event, knownDomains) {
|
||||||
if (event) {
|
if (event) {
|
||||||
const url = _getURLFromEvent(event, knownDomains);
|
const url = _getURLFromEvent(event, knownDomains);
|
||||||
|
|
||||||
if (url) {
|
// we only filter events without url on mobile, this is temporary
|
||||||
|
// till we implement event edit on mobile
|
||||||
|
if (url || navigator.product !== 'ReactNative') {
|
||||||
const startDate = Date.parse(event.startDate);
|
const startDate = Date.parse(event.startDate);
|
||||||
const endDate = Date.parse(event.endDate);
|
const endDate = Date.parse(event.endDate);
|
||||||
|
|
||||||
if (isNaN(startDate) || isNaN(endDate)) {
|
// we want to hide all events that
|
||||||
logger.warn(
|
// - has no start or end date
|
||||||
|
// - for web, if there is no url and we cannot edit the event (has
|
||||||
|
// no calendarId)
|
||||||
|
if (isNaN(startDate)
|
||||||
|
|| isNaN(endDate)
|
||||||
|
|| (navigator.product !== 'ReactNative'
|
||||||
|
&& !url
|
||||||
|
&& !event.calendarId)) {
|
||||||
|
logger.debug(
|
||||||
'Skipping invalid calendar event',
|
'Skipping invalid calendar event',
|
||||||
event.title,
|
event.title,
|
||||||
event.startDate,
|
event.startDate,
|
||||||
event.endDate
|
event.endDate,
|
||||||
|
url,
|
||||||
|
event.calendarId
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
return {
|
return {
|
||||||
|
calendarId: event.calendarId,
|
||||||
endDate,
|
endDate,
|
||||||
id: event.id,
|
id: event.id,
|
||||||
startDate,
|
startDate,
|
||||||
|
|
|
@ -5,6 +5,7 @@ import {
|
||||||
googleApi,
|
googleApi,
|
||||||
loadGoogleAPI,
|
loadGoogleAPI,
|
||||||
signIn,
|
signIn,
|
||||||
|
updateCalendarEvent,
|
||||||
updateProfile
|
updateProfile
|
||||||
} from '../../google-api';
|
} from '../../google-api';
|
||||||
|
|
||||||
|
@ -62,5 +63,16 @@ export const googleCalendarApi = {
|
||||||
*/
|
*/
|
||||||
_isSignedIn() {
|
_isSignedIn() {
|
||||||
return () => googleApi.isSignedIn();
|
return () => googleApi.isSignedIn();
|
||||||
}
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates calendar event by generating new invite URL and editing the event
|
||||||
|
* adding some descriptive text and location.
|
||||||
|
*
|
||||||
|
* @param {string} id - The event id.
|
||||||
|
* @param {string} calendarId - The id of the calendar to use.
|
||||||
|
* @param {string} location - The location to save to the event.
|
||||||
|
* @returns {function(Dispatch<*>): Promise<string|never>}
|
||||||
|
*/
|
||||||
|
updateCalendarEvent
|
||||||
};
|
};
|
||||||
|
|
|
@ -7,6 +7,7 @@ import { createDeferred } from '../../../../modules/util/helpers';
|
||||||
|
|
||||||
import parseURLParams from '../../base/config/parseURLParams';
|
import parseURLParams from '../../base/config/parseURLParams';
|
||||||
import { parseStandardURIString } from '../../base/util';
|
import { parseStandardURIString } from '../../base/util';
|
||||||
|
import { getShareInfoText } from '../../invite';
|
||||||
|
|
||||||
import { setCalendarAPIAuthState } from '../actions';
|
import { setCalendarAPIAuthState } from '../actions';
|
||||||
|
|
||||||
|
@ -31,7 +32,7 @@ const MS_API_CONFIGURATION = {
|
||||||
*
|
*
|
||||||
* @type {string}
|
* @type {string}
|
||||||
*/
|
*/
|
||||||
MS_API_SCOPES: 'openid profile Calendars.Read',
|
MS_API_SCOPES: 'openid profile Calendars.ReadWrite',
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* See https://docs.microsoft.com/en-us/azure/active-directory/develop/
|
* See https://docs.microsoft.com/en-us/azure/active-directory/develop/
|
||||||
|
@ -106,7 +107,7 @@ export const microsoftCalendarApi = {
|
||||||
// get .value of every element from the array of results,
|
// get .value of every element from the array of results,
|
||||||
// which is an array of events and flatten it to one array
|
// which is an array of events and flatten it to one array
|
||||||
// of events
|
// of events
|
||||||
.then(result => [].concat(...result.map(en => en.value)))
|
.then(result => [].concat(...result))
|
||||||
.then(entries => entries.map(e => formatCalendarEntry(e)));
|
.then(entries => entries.map(e => formatCalendarEntry(e)));
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
@ -308,6 +309,59 @@ export const microsoftCalendarApi = {
|
||||||
}));
|
}));
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates calendar event by generating new invite URL and editing the event
|
||||||
|
* adding some descriptive text and location.
|
||||||
|
*
|
||||||
|
* @param {string} id - The event id.
|
||||||
|
* @param {string} calendarId - The id of the calendar to use.
|
||||||
|
* @param {string} location - The location to save to the event.
|
||||||
|
* @returns {function(Dispatch<*>): Promise<string|never>}
|
||||||
|
*/
|
||||||
|
updateCalendarEvent(id: string, calendarId: string, location: string) {
|
||||||
|
return (dispatch: Dispatch<*>, getState: Function): Promise<*> => {
|
||||||
|
const state = getState()['features/calendar-sync'] || {};
|
||||||
|
const token = state.msAuthState && state.msAuthState.accessToken;
|
||||||
|
|
||||||
|
if (!token) {
|
||||||
|
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}<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
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -317,6 +371,7 @@ export const microsoftCalendarApi = {
|
||||||
* @param {Object} entry - The Microsoft calendar entry.
|
* @param {Object} entry - The Microsoft calendar entry.
|
||||||
* @private
|
* @private
|
||||||
* @returns {{
|
* @returns {{
|
||||||
|
* calendarId: string,
|
||||||
* description: string,
|
* description: string,
|
||||||
* endDate: string,
|
* endDate: string,
|
||||||
* id: string,
|
* id: string,
|
||||||
|
@ -327,6 +382,7 @@ export const microsoftCalendarApi = {
|
||||||
*/
|
*/
|
||||||
function formatCalendarEntry(entry) {
|
function formatCalendarEntry(entry) {
|
||||||
return {
|
return {
|
||||||
|
calendarId: entry.calendarId,
|
||||||
description: entry.body.content,
|
description: entry.body.content,
|
||||||
endDate: entry.end.dateTime,
|
endDate: entry.end.dateTime,
|
||||||
id: entry.id,
|
id: entry.id,
|
||||||
|
@ -509,7 +565,13 @@ function requestCalendarEvents( // eslint-disable-line max-params
|
||||||
.filter(filter)
|
.filter(filter)
|
||||||
.select('id,subject,start,end,location,body')
|
.select('id,subject,start,end,location,body')
|
||||||
.orderby('createdDateTime DESC')
|
.orderby('createdDateTime DESC')
|
||||||
.get();
|
.get()
|
||||||
|
.then(result => result.value.map(item => {
|
||||||
|
return {
|
||||||
|
...item,
|
||||||
|
calendarId
|
||||||
|
};
|
||||||
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
/* @flow */
|
/* @flow */
|
||||||
|
import { getShareInfoText } from '../invite';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
SET_GOOGLE_API_PROFILE,
|
SET_GOOGLE_API_PROFILE,
|
||||||
|
@ -184,3 +185,24 @@ export function updateProfile() {
|
||||||
return profile.getEmail();
|
return profile.getEmail();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates the calendar event and adds a location and text.
|
||||||
|
*
|
||||||
|
* @param {string} id - The event id to update.
|
||||||
|
* @param {string} calendarId - The calendar id to use.
|
||||||
|
* @param {string} location - The location to add to the event.
|
||||||
|
* @returns {function(Dispatch<*>): Promise<string | never>}
|
||||||
|
*/
|
||||||
|
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(() =>
|
||||||
|
googleApi._updateCalendarEntry(id, calendarId, location, text));
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
|
@ -204,22 +204,24 @@ const googleApi = {
|
||||||
*
|
*
|
||||||
* @param {Object} entry - The google calendar entry.
|
* @param {Object} entry - The google calendar entry.
|
||||||
* @returns {{
|
* @returns {{
|
||||||
* id: string,
|
* calendarId: string,
|
||||||
* startDate: string,
|
* description: string,
|
||||||
* endDate: string,
|
* endDate: string,
|
||||||
* title: string,
|
* id: string,
|
||||||
* location: string,
|
* location: string,
|
||||||
* description: string}}
|
* startDate: string,
|
||||||
|
* title: string}}
|
||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
_convertCalendarEntry(entry) {
|
_convertCalendarEntry(entry) {
|
||||||
return {
|
return {
|
||||||
id: entry.id,
|
calendarId: entry.calendarId,
|
||||||
startDate: entry.start.dateTime,
|
description: entry.description,
|
||||||
endDate: entry.end.dateTime,
|
endDate: entry.end.dateTime,
|
||||||
title: entry.summary,
|
id: entry.id,
|
||||||
location: entry.location,
|
location: entry.location,
|
||||||
description: entry.description
|
startDate: entry.start.dateTime,
|
||||||
|
title: entry.summary
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -240,6 +242,8 @@ const googleApi = {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// user can edit the events, so we want only those that
|
||||||
|
// can be edited
|
||||||
return this._getGoogleApiClient()
|
return this._getGoogleApiClient()
|
||||||
.client.calendar.calendarList.list();
|
.client.calendar.calendarList.list();
|
||||||
})
|
})
|
||||||
|
@ -251,14 +255,20 @@ const googleApi = {
|
||||||
}
|
}
|
||||||
|
|
||||||
const calendarIds
|
const calendarIds
|
||||||
= calendarList.result.items.map(en => en.id);
|
= calendarList.result.items.map(en => {
|
||||||
const promises = calendarIds.map(id => {
|
return {
|
||||||
|
id: en.id,
|
||||||
|
accessRole: en.accessRole
|
||||||
|
};
|
||||||
|
});
|
||||||
|
const promises = calendarIds.map(({ id, accessRole }) => {
|
||||||
const startDate = new Date();
|
const startDate = new Date();
|
||||||
const endDate = new Date();
|
const endDate = new Date();
|
||||||
|
|
||||||
startDate.setDate(startDate.getDate() + fetchStartDays);
|
startDate.setDate(startDate.getDate() + fetchStartDays);
|
||||||
endDate.setDate(endDate.getDate() + fetchEndDays);
|
endDate.setDate(endDate.getDate() + fetchEndDays);
|
||||||
|
|
||||||
|
// retrieve the events and adds to the result the calendarId
|
||||||
return this._getGoogleApiClient()
|
return this._getGoogleApiClient()
|
||||||
.client.calendar.events.list({
|
.client.calendar.events.list({
|
||||||
'calendarId': id,
|
'calendarId': id,
|
||||||
|
@ -267,17 +277,73 @@ const googleApi = {
|
||||||
'showDeleted': false,
|
'showDeleted': false,
|
||||||
'singleEvents': true,
|
'singleEvents': true,
|
||||||
'orderBy': 'startTime'
|
'orderBy': 'startTime'
|
||||||
});
|
})
|
||||||
|
.then(result => result.result.items
|
||||||
|
.map(item => {
|
||||||
|
const resultItem = { ...item };
|
||||||
|
|
||||||
|
// add the calendarId only for the events
|
||||||
|
// we can edit
|
||||||
|
if (accessRole === 'writer'
|
||||||
|
|| accessRole === 'owner') {
|
||||||
|
resultItem.calendarId = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
return resultItem;
|
||||||
|
}));
|
||||||
});
|
});
|
||||||
|
|
||||||
return Promise.all(promises)
|
return Promise.all(promises)
|
||||||
.then(results =>
|
.then(results => [].concat(...results))
|
||||||
[].concat(...results.map(rItem => rItem.result.items)))
|
|
||||||
.then(entries =>
|
.then(entries =>
|
||||||
entries.map(e => this._convertCalendarEntry(e)));
|
entries.map(e => this._convertCalendarEntry(e)));
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/* eslint-disable max-params */
|
||||||
|
/**
|
||||||
|
* Updates the calendar event and adds a location and text.
|
||||||
|
*
|
||||||
|
* @param {string} id - The event id to update.
|
||||||
|
* @param {string} calendarId - The calendar id to use.
|
||||||
|
* @param {string} location - The location to add to the event.
|
||||||
|
* @param {string} text - The description text to set/append.
|
||||||
|
* @returns {Promise<T | never>}
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
_updateCalendarEntry(id, calendarId, location, text) {
|
||||||
|
return this.get()
|
||||||
|
.then(() => this.isSignedIn())
|
||||||
|
.then(isSignedIn => {
|
||||||
|
if (!isSignedIn) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return this._getGoogleApiClient()
|
||||||
|
.client.calendar.events.get({
|
||||||
|
'calendarId': calendarId,
|
||||||
|
'eventId': id
|
||||||
|
}).then(event => {
|
||||||
|
let newDescription = text;
|
||||||
|
|
||||||
|
if (event.result.description) {
|
||||||
|
newDescription = `${event.result.description}\n\n${
|
||||||
|
text}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
return this._getGoogleApiClient()
|
||||||
|
.client.calendar.events.patch({
|
||||||
|
'calendarId': calendarId,
|
||||||
|
'eventId': id,
|
||||||
|
'description': newDescription,
|
||||||
|
'location': location
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
},
|
||||||
|
/* eslint-enable max-params */
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the global Google API Client Library object. Direct use of this
|
* Returns the global Google API Client Library object. Direct use of this
|
||||||
* method is discouraged; instead use the {@link get} method.
|
* method is discouraged; instead use the {@link get} method.
|
||||||
|
|
|
@ -7,6 +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 DialInNumber from './DialInNumber';
|
import DialInNumber from './DialInNumber';
|
||||||
import PasswordForm from './PasswordForm';
|
import PasswordForm from './PasswordForm';
|
||||||
|
|
||||||
|
@ -266,23 +267,8 @@ class InfoDialog extends Component {
|
||||||
* @returns {string}
|
* @returns {string}
|
||||||
*/
|
*/
|
||||||
_getDialInfoPageURL() {
|
_getDialInfoPageURL() {
|
||||||
const origin = window.location.origin;
|
return getDialInfoPageURL(
|
||||||
const encodedConferenceName
|
encodeURIComponent(this.props._conferenceName));
|
||||||
= encodeURIComponent(this.props._conferenceName);
|
|
||||||
const pathParts = window.location.pathname.split('/');
|
|
||||||
|
|
||||||
pathParts.length = pathParts.length - 1;
|
|
||||||
|
|
||||||
const newPath = pathParts.reduce((accumulator, currentValue) => {
|
|
||||||
if (currentValue) {
|
|
||||||
return `${accumulator}/${currentValue}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
return accumulator;
|
|
||||||
}, '');
|
|
||||||
|
|
||||||
return `${origin}${newPath}/static/dialInInfo.html?room=${
|
|
||||||
encodedConferenceName}`;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -1,8 +1,9 @@
|
||||||
// @flow
|
// @flow
|
||||||
|
|
||||||
import { getAppProp } from '../base/app';
|
import { getAppProp } from '../base/app';
|
||||||
|
import { i18next } from '../base/i18n';
|
||||||
import { isLocalParticipantModerator } from '../base/participants';
|
import { isLocalParticipantModerator } from '../base/participants';
|
||||||
import { doGetJSON } from '../base/util';
|
import { doGetJSON, parseURIString } from '../base/util';
|
||||||
|
|
||||||
declare var $: Function;
|
declare var $: Function;
|
||||||
declare var interfaceConfig: Object;
|
declare var interfaceConfig: Object;
|
||||||
|
@ -397,3 +398,62 @@ export function searchDirectory( // eslint-disable-line max-params
|
||||||
return Promise.reject(error);
|
return Promise.reject(error);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns descriptive text that can be used to invite participants to a meeting
|
||||||
|
* (share via mobile or use it for calendar event description).
|
||||||
|
*
|
||||||
|
* @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}
|
||||||
|
*/
|
||||||
|
export function getShareInfoText(
|
||||||
|
inviteUrl: string, includeDialInfo: boolean, useHtml: ?boolean) {
|
||||||
|
let roomUrl = inviteUrl;
|
||||||
|
|
||||||
|
if (useHtml) {
|
||||||
|
roomUrl = `<a href="${roomUrl}">${roomUrl}</a>`;
|
||||||
|
}
|
||||||
|
|
||||||
|
let infoText = i18next.t('share.mainText', { roomUrl });
|
||||||
|
|
||||||
|
if (includeDialInfo) {
|
||||||
|
const { room } = parseURIString(inviteUrl);
|
||||||
|
let dialInfoPageUrl = getDialInfoPageURL(room);
|
||||||
|
|
||||||
|
if (useHtml) {
|
||||||
|
dialInfoPageUrl
|
||||||
|
= `<a href="${dialInfoPageUrl}">${dialInfoPageUrl}</a>`;
|
||||||
|
}
|
||||||
|
|
||||||
|
infoText += i18next.t('share.dialInfoText', { dialInfoPageUrl });
|
||||||
|
}
|
||||||
|
|
||||||
|
return infoText;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generates the URL for the static dial in info page.
|
||||||
|
*
|
||||||
|
* @param {string} conferenceName - The conference name.
|
||||||
|
* @private
|
||||||
|
* @returns {string}
|
||||||
|
*/
|
||||||
|
export function getDialInfoPageURL(conferenceName: string) {
|
||||||
|
const origin = window.location.origin;
|
||||||
|
const pathParts = window.location.pathname.split('/');
|
||||||
|
|
||||||
|
pathParts.length = pathParts.length - 1;
|
||||||
|
|
||||||
|
const newPath = pathParts.reduce((accumulator, currentValue) => {
|
||||||
|
if (currentValue) {
|
||||||
|
return `${accumulator}/${currentValue}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
return accumulator;
|
||||||
|
}, '');
|
||||||
|
|
||||||
|
return `${origin}${newPath}/static/dialInInfo.html?room=${conferenceName}`;
|
||||||
|
}
|
||||||
|
|
|
@ -4,7 +4,8 @@
|
||||||
*
|
*
|
||||||
* {
|
* {
|
||||||
* type: BEGIN_SHARE_ROOM,
|
* type: BEGIN_SHARE_ROOM,
|
||||||
* roomURL: string
|
* roomURL: string,
|
||||||
|
* includeDialInfo: boolean
|
||||||
* }
|
* }
|
||||||
*/
|
*/
|
||||||
export const BEGIN_SHARE_ROOM = Symbol('BEGIN_SHARE_ROOM');
|
export const BEGIN_SHARE_ROOM = Symbol('BEGIN_SHARE_ROOM');
|
||||||
|
|
|
@ -19,7 +19,9 @@ 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
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,6 +4,7 @@ import { Share } from 'react-native';
|
||||||
|
|
||||||
import { getName } from '../app';
|
import { getName } from '../app';
|
||||||
import { MiddlewareRegistry } from '../base/redux';
|
import { MiddlewareRegistry } from '../base/redux';
|
||||||
|
import { getShareInfoText } from '../invite';
|
||||||
|
|
||||||
import { endShareRoom } from './actions';
|
import { endShareRoom } from './actions';
|
||||||
import { BEGIN_SHARE_ROOM } from './actionTypes';
|
import { BEGIN_SHARE_ROOM } from './actionTypes';
|
||||||
|
@ -20,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, store.dispatch);
|
_shareRoom(action.roomURL, action.includeDialInfo, store.dispatch);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -31,15 +32,15 @@ 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
|
||||||
|
* information link.
|
||||||
* @param {Dispatch} dispatch - The Redux dispatch function.
|
* @param {Dispatch} dispatch - The Redux dispatch function.
|
||||||
* @private
|
* @private
|
||||||
* @returns {void}
|
* @returns {void}
|
||||||
*/
|
*/
|
||||||
function _shareRoom(roomURL: string, dispatch: Function) {
|
function _shareRoom(
|
||||||
// TODO The following display/human-readable strings were submitted for
|
roomURL: string, includeDialInfo: boolean, dispatch: Function) {
|
||||||
// review before i18n was introduces in react/. However, I reviewed it
|
const message = getShareInfoText(roomURL, includeDialInfo);
|
||||||
// afterwards. Translate the display/human-readable strings.
|
|
||||||
const message = `Click the following link to join the meeting: ${roomURL}`;
|
|
||||||
const title = `${getName()} Conference`;
|
const title = `${getName()} Conference`;
|
||||||
const onFulfilled
|
const onFulfilled
|
||||||
= (shared: boolean) => dispatch(endShareRoom(roomURL, shared));
|
= (shared: boolean) => dispatch(endShareRoom(roomURL, shared));
|
||||||
|
|
Loading…
Reference in New Issue