Вы не можете выбрать более 25 тем Темы должны начинаться с буквы или цифры, могут содержать дефисы(-) и должны содержать не более 35 символов.

actions.web.js 8.3KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293
  1. // @flow
  2. import { generateRoomWithoutSeparator } from 'js-utils/random';
  3. import type { Dispatch } from 'redux';
  4. import { loadGoogleAPI } from '../google-api';
  5. import { refreshCalendar, setCalendarEvents } from './actions';
  6. import { createCalendarConnectedEvent, sendAnalytics } from '../analytics';
  7. import {
  8. CLEAR_CALENDAR_INTEGRATION,
  9. SET_CALENDAR_AUTH_STATE,
  10. SET_CALENDAR_ERROR,
  11. SET_CALENDAR_INTEGRATION,
  12. SET_CALENDAR_PROFILE_EMAIL,
  13. SET_LOADING_CALENDAR_EVENTS
  14. } from './actionTypes';
  15. import { _getCalendarIntegration, isCalendarEnabled } from './functions';
  16. import logger from './logger';
  17. export * from './actions.any';
  18. /**
  19. * Sets the initial state of calendar integration by loading third party APIs
  20. * and filling out any data that needs to be fetched.
  21. *
  22. * @returns {Function}
  23. */
  24. export function bootstrapCalendarIntegration(): Function {
  25. return (dispatch, getState) => {
  26. const state = getState();
  27. if (!isCalendarEnabled(state)) {
  28. return Promise.reject();
  29. }
  30. const {
  31. googleApiApplicationClientID
  32. } = state['features/base/config'];
  33. const {
  34. integrationReady,
  35. integrationType
  36. } = state['features/calendar-sync'];
  37. return Promise.resolve()
  38. .then(() => {
  39. if (googleApiApplicationClientID) {
  40. return dispatch(
  41. loadGoogleAPI(googleApiApplicationClientID));
  42. }
  43. })
  44. .then(() => {
  45. if (!integrationType || integrationReady) {
  46. return;
  47. }
  48. const integrationToLoad
  49. = _getCalendarIntegration(integrationType);
  50. if (!integrationToLoad) {
  51. dispatch(clearCalendarIntegration());
  52. return;
  53. }
  54. return dispatch(integrationToLoad._isSignedIn())
  55. .then(signedIn => {
  56. if (signedIn) {
  57. dispatch(setIntegrationReady(integrationType));
  58. dispatch(updateProfile(integrationType));
  59. } else {
  60. dispatch(clearCalendarIntegration());
  61. }
  62. });
  63. });
  64. };
  65. }
  66. /**
  67. * Resets the state of calendar integration so stored events and selected
  68. * calendar type are cleared.
  69. *
  70. * @returns {{
  71. * type: CLEAR_CALENDAR_INTEGRATION
  72. * }}
  73. */
  74. export function clearCalendarIntegration() {
  75. return {
  76. type: CLEAR_CALENDAR_INTEGRATION
  77. };
  78. }
  79. /**
  80. * Asks confirmation from the user to add a Jitsi link to the calendar event.
  81. *
  82. * NOTE: Currently there is no confirmation prompted on web, so this is just
  83. * a relaying method to avoid flow problems.
  84. *
  85. * @param {string} eventId - The event id.
  86. * @param {string} calendarId - The calendar id.
  87. * @returns {Function}
  88. */
  89. export function openUpdateCalendarEventDialog(
  90. eventId: string, calendarId: string) {
  91. return updateCalendarEvent(eventId, calendarId);
  92. }
  93. /**
  94. * Sends an action to update the current calendar api auth state in redux.
  95. * This is used only for microsoft implementation to store it auth state.
  96. *
  97. * @param {number} newState - The new state.
  98. * @returns {{
  99. * type: SET_CALENDAR_AUTH_STATE,
  100. * msAuthState: Object
  101. * }}
  102. */
  103. export function setCalendarAPIAuthState(newState: ?Object) {
  104. return {
  105. type: SET_CALENDAR_AUTH_STATE,
  106. msAuthState: newState
  107. };
  108. }
  109. /**
  110. * Sends an action to update the calendar error state in redux.
  111. *
  112. * @param {Object} error - An object with error details.
  113. * @returns {{
  114. * type: SET_CALENDAR_ERROR,
  115. * error: Object
  116. * }}
  117. */
  118. export function setCalendarError(error: ?Object) {
  119. return {
  120. type: SET_CALENDAR_ERROR,
  121. error
  122. };
  123. }
  124. /**
  125. * Sends an action to update the current calendar profile email state in redux.
  126. *
  127. * @param {number} newEmail - The new email.
  128. * @returns {{
  129. * type: SET_CALENDAR_PROFILE_EMAIL,
  130. * email: string
  131. * }}
  132. */
  133. export function setCalendarProfileEmail(newEmail: ?string) {
  134. return {
  135. type: SET_CALENDAR_PROFILE_EMAIL,
  136. email: newEmail
  137. };
  138. }
  139. /**
  140. * Sends an to denote a request in is flight to get calendar events.
  141. *
  142. * @param {boolean} isLoadingEvents - Whether or not calendar events are being
  143. * fetched.
  144. * @returns {{
  145. * type: SET_LOADING_CALENDAR_EVENTS,
  146. * isLoadingEvents: boolean
  147. * }}
  148. */
  149. export function setLoadingCalendarEvents(isLoadingEvents: boolean) {
  150. return {
  151. type: SET_LOADING_CALENDAR_EVENTS,
  152. isLoadingEvents
  153. };
  154. }
  155. /**
  156. * Sets the calendar integration type to be used by web and signals that the
  157. * integration is ready to be used.
  158. *
  159. * @param {string|undefined} integrationType - The calendar type.
  160. * @returns {{
  161. * type: SET_CALENDAR_INTEGRATION,
  162. * integrationReady: boolean,
  163. * integrationType: string
  164. * }}
  165. */
  166. export function setIntegrationReady(integrationType: string) {
  167. return {
  168. type: SET_CALENDAR_INTEGRATION,
  169. integrationReady: true,
  170. integrationType
  171. };
  172. }
  173. /**
  174. * Signals signing in to the specified calendar integration.
  175. *
  176. * @param {string} calendarType - The calendar integration which should be
  177. * signed into.
  178. * @returns {Function}
  179. */
  180. export function signIn(calendarType: string): Function {
  181. return (dispatch: Dispatch<any>) => {
  182. const integration = _getCalendarIntegration(calendarType);
  183. if (!integration) {
  184. return Promise.reject('No supported integration found');
  185. }
  186. return dispatch(integration.load())
  187. .then(() => dispatch(integration.signIn()))
  188. .then(() => dispatch(setIntegrationReady(calendarType)))
  189. .then(() => dispatch(updateProfile(calendarType)))
  190. .then(() => dispatch(refreshCalendar()))
  191. .then(() => sendAnalytics(createCalendarConnectedEvent()))
  192. .catch(error => {
  193. logger.error(
  194. 'Error occurred while signing into calendar integration',
  195. error);
  196. return Promise.reject(error);
  197. });
  198. };
  199. }
  200. /**
  201. * Updates calendar event by generating new invite URL and editing the event
  202. * adding some descriptive text and location.
  203. *
  204. * @param {string} id - The event id.
  205. * @param {string} calendarId - The id of the calendar to use.
  206. * @returns {Function}
  207. */
  208. export function updateCalendarEvent(id: string, calendarId: string): Function {
  209. return (dispatch: Dispatch<any>, getState: Function) => {
  210. const { integrationType } = getState()['features/calendar-sync'];
  211. const integration = _getCalendarIntegration(integrationType);
  212. if (!integration) {
  213. return Promise.reject('No integration found');
  214. }
  215. const { locationURL } = getState()['features/base/connection'];
  216. const newRoomName = generateRoomWithoutSeparator();
  217. let href = locationURL.href;
  218. href.endsWith('/') || (href += '/');
  219. const roomURL = `${href}${newRoomName}`;
  220. return dispatch(integration.updateCalendarEvent(
  221. id, calendarId, roomURL))
  222. .then(() => {
  223. // make a copy of the array
  224. const events
  225. = getState()['features/calendar-sync'].events.slice(0);
  226. const eventIx = events.findIndex(
  227. e => e.id === id && e.calendarId === calendarId);
  228. // clone the event we will modify
  229. const newEvent = Object.assign({}, events[eventIx]);
  230. newEvent.url = roomURL;
  231. events[eventIx] = newEvent;
  232. return dispatch(setCalendarEvents(events));
  233. });
  234. };
  235. }
  236. /**
  237. * Signals to get current profile data linked to the current calendar
  238. * integration that is in use.
  239. *
  240. * @param {string} calendarType - The calendar integration to which the profile
  241. * should be updated.
  242. * @returns {Function}
  243. */
  244. export function updateProfile(calendarType: string): Function {
  245. return (dispatch: Dispatch<any>) => {
  246. const integration = _getCalendarIntegration(calendarType);
  247. if (!integration) {
  248. return Promise.reject('No integration found');
  249. }
  250. return dispatch(integration.getCurrentEmail())
  251. .then(email => {
  252. dispatch(setCalendarProfileEmail(email));
  253. });
  254. };
  255. }