You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

middleware.js 5.1KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162
  1. /* @flow */
  2. import { NativeModules } from 'react-native';
  3. import {
  4. CONFERENCE_FAILED,
  5. CONFERENCE_JOINED,
  6. CONFERENCE_LEFT,
  7. CONFERENCE_WILL_JOIN,
  8. CONFERENCE_WILL_LEAVE,
  9. JITSI_CONFERENCE_URL_KEY
  10. } from '../../base/conference';
  11. import { LOAD_CONFIG_ERROR } from '../../base/config';
  12. import { MiddlewareRegistry } from '../../base/redux';
  13. import { toURLString } from '../../base/util';
  14. /**
  15. * Middleware that captures Redux actions and uses the ExternalAPI module to
  16. * turn them into native events so the application knows about them.
  17. *
  18. * @param {Store} store - Redux store.
  19. * @returns {Function}
  20. */
  21. MiddlewareRegistry.register(store => next => action => {
  22. const result = next(action);
  23. switch (action.type) {
  24. case CONFERENCE_FAILED: {
  25. const { error, ...data } = action;
  26. // XXX Certain CONFERENCE_FAILED errors are recoverable i.e. they have
  27. // prevented the user from joining a specific conference but the app may
  28. // be able to eventually join the conference. For example, the app will
  29. // ask the user for a password upon
  30. // JitsiConferenceErrors.PASSWORD_REQUIRED and will retry joining the
  31. // conference afterwards. Such errors are to not reach the native
  32. // counterpart of the External API (or at least not in the
  33. // fatality/finality semantics attributed to
  34. // conferenceFailed:/onConferenceFailed).
  35. if (!error.recoverable) {
  36. _sendConferenceEvent(store, /* action */ {
  37. error: _toErrorString(error),
  38. ...data
  39. });
  40. }
  41. break;
  42. }
  43. case CONFERENCE_JOINED:
  44. case CONFERENCE_LEFT:
  45. case CONFERENCE_WILL_JOIN:
  46. case CONFERENCE_WILL_LEAVE:
  47. _sendConferenceEvent(store, action);
  48. break;
  49. case LOAD_CONFIG_ERROR: {
  50. const { error, locationURL, type } = action;
  51. _sendEvent(store, _getSymbolDescription(type), /* data */ {
  52. error: _toErrorString(error),
  53. url: toURLString(locationURL)
  54. });
  55. break;
  56. }
  57. }
  58. return result;
  59. });
  60. /**
  61. * Returns a {@code String} representation of a specific error {@code Object}.
  62. *
  63. * @param {Error|Object|string} error - The error {@code Object} to return a
  64. * {@code String} representation of.
  65. * @returns {string} A {@code String} representation of the specified
  66. * {@code error}.
  67. */
  68. function _toErrorString(
  69. error: Error | { message: ?string, name: ?string } | string) {
  70. // XXX In lib-jitsi-meet and jitsi-meet we utilize errors in the form of
  71. // strings, Error instances, and plain objects which resemble Error.
  72. return (
  73. error
  74. ? typeof error === 'string'
  75. ? error
  76. : Error.prototype.toString(error)
  77. : '');
  78. }
  79. /**
  80. * Gets the description of a specific {@code Symbol}.
  81. *
  82. * @param {Symbol} symbol - The {@code Symbol} to retrieve the description of.
  83. * @private
  84. * @returns {string} The description of {@code symbol}.
  85. */
  86. function _getSymbolDescription(symbol: Symbol) {
  87. let description = symbol.toString();
  88. if (description.startsWith('Symbol(') && description.endsWith(')')) {
  89. description = description.slice(7, -1);
  90. }
  91. // The polyfill es6-symbol that we use does not appear to comply with the
  92. // Symbol standard and, merely, adds @@ at the beginning of the description.
  93. if (description.startsWith('@@')) {
  94. description = description.slice(2);
  95. }
  96. return description;
  97. }
  98. /**
  99. * Sends an event to the native counterpart of the External API for a specific
  100. * conference-related redux action.
  101. *
  102. * @param {Store} store - The redux store.
  103. * @param {Action} action - The redux action.
  104. * @returns {void}
  105. */
  106. function _sendConferenceEvent(
  107. store: Object,
  108. { conference, type, ...data }: { conference: Object, type: Symbol }) {
  109. // For these (redux) actions, conference identifies a JitsiConference
  110. // instance. The external API cannot transport such an object so we have to
  111. // transport an "equivalent".
  112. if (conference) {
  113. data.url = toURLString(conference[JITSI_CONFERENCE_URL_KEY]);
  114. }
  115. _sendEvent(store, _getSymbolDescription(type), data);
  116. }
  117. /**
  118. * Sends a specific event to the native counterpart of the External API. Native
  119. * apps may listen to such events via the mechanisms provided by the (native)
  120. * mobile Jitsi Meet SDK.
  121. *
  122. * @param {Object} store - The redux store.
  123. * @param {string} name - The name of the event to send.
  124. * @param {Object} data - The details/specifics of the event to send determined
  125. * by/associated with the specified {@code name}.
  126. * @private
  127. * @returns {void}
  128. */
  129. function _sendEvent(
  130. { getState }: { getState: Function },
  131. name: string,
  132. data: Object) {
  133. // The JavaScript App needs to provide uniquely identifying information
  134. // to the native ExternalAPI module so that the latter may match the former
  135. // to the native JitsiMeetView which hosts it.
  136. const { app } = getState()['features/app'];
  137. if (app) {
  138. const { externalAPIScope } = app.props;
  139. if (externalAPIScope) {
  140. NativeModules.ExternalAPI.sendEvent(name, data, externalAPIScope);
  141. }
  142. }
  143. }