您最多选择25个主题 主题必须以字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符

actions.native.js 11KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384
  1. // @flow
  2. import _ from 'lodash';
  3. import type { Dispatch } from 'redux';
  4. import { conferenceLeft, conferenceWillLeave } from '../conference/actions';
  5. import { getCurrentConference } from '../conference/functions';
  6. import JitsiMeetJS, { JitsiConnectionEvents } from '../lib-jitsi-meet';
  7. import {
  8. getBackendSafeRoomName,
  9. parseURIString
  10. } from '../util';
  11. import {
  12. CONNECTION_DISCONNECTED,
  13. CONNECTION_ESTABLISHED,
  14. CONNECTION_FAILED,
  15. CONNECTION_WILL_CONNECT,
  16. SET_LOCATION_URL
  17. } from './actionTypes';
  18. import { JITSI_CONNECTION_URL_KEY } from './constants';
  19. import logger from './logger';
  20. /**
  21. * The error structure passed to the {@link connectionFailed} action.
  22. *
  23. * Note there was an intention to make the error resemble an Error instance (to
  24. * the extent that jitsi-meet needs it).
  25. */
  26. export type ConnectionFailedError = {
  27. /**
  28. * The invalid credentials that were used to authenticate and the
  29. * authentication failed.
  30. */
  31. credentials?: {
  32. /**
  33. * The XMPP user's ID.
  34. */
  35. jid: string,
  36. /**
  37. * The XMPP user's password.
  38. */
  39. password: string
  40. },
  41. /**
  42. * The details about the connection failed event.
  43. */
  44. details?: Object,
  45. /**
  46. * Error message.
  47. */
  48. message?: string,
  49. /**
  50. * One of {@link JitsiConnectionError} constants (defined in
  51. * lib-jitsi-meet).
  52. */
  53. name: string,
  54. /**
  55. * Indicates whether this event is recoverable or not.
  56. */
  57. recoverable?: boolean
  58. };
  59. /**
  60. * Opens new connection.
  61. *
  62. * @param {string} [id] - The XMPP user's ID (e.g. {@code user@server.com}).
  63. * @param {string} [password] - The XMPP user's password.
  64. * @returns {Function}
  65. */
  66. export function connect(id: ?string, password: ?string) {
  67. return (dispatch: Dispatch<any>, getState: Function) => {
  68. const state = getState();
  69. const options = _constructOptions(state);
  70. const { locationURL } = state['features/base/connection'];
  71. const { jwt } = state['features/base/jwt'];
  72. const connection = new JitsiMeetJS.JitsiConnection(options.appId, jwt, options);
  73. connection[JITSI_CONNECTION_URL_KEY] = locationURL;
  74. dispatch(_connectionWillConnect(connection));
  75. connection.addEventListener(
  76. JitsiConnectionEvents.CONNECTION_DISCONNECTED,
  77. _onConnectionDisconnected);
  78. connection.addEventListener(
  79. JitsiConnectionEvents.CONNECTION_ESTABLISHED,
  80. _onConnectionEstablished);
  81. connection.addEventListener(
  82. JitsiConnectionEvents.CONNECTION_FAILED,
  83. _onConnectionFailed);
  84. connection.connect({
  85. id,
  86. password
  87. });
  88. /**
  89. * Dispatches {@code CONNECTION_DISCONNECTED} action when connection is
  90. * disconnected.
  91. *
  92. * @private
  93. * @returns {void}
  94. */
  95. function _onConnectionDisconnected() {
  96. unsubscribe();
  97. dispatch(connectionDisconnected(connection));
  98. }
  99. /**
  100. * Resolves external promise when connection is established.
  101. *
  102. * @private
  103. * @returns {void}
  104. */
  105. function _onConnectionEstablished() {
  106. connection.removeEventListener(
  107. JitsiConnectionEvents.CONNECTION_ESTABLISHED,
  108. _onConnectionEstablished);
  109. dispatch(connectionEstablished(connection, Date.now()));
  110. }
  111. /**
  112. * Rejects external promise when connection fails.
  113. *
  114. * @param {JitsiConnectionErrors} err - Connection error.
  115. * @param {string} [msg] - Error message supplied by lib-jitsi-meet.
  116. * @param {Object} [credentials] - The invalid credentials that were
  117. * used to authenticate and the authentication failed.
  118. * @param {string} [credentials.jid] - The XMPP user's ID.
  119. * @param {string} [credentials.password] - The XMPP user's password.
  120. * @param {Object} details - Additional information about the error.
  121. * @private
  122. * @returns {void}
  123. */
  124. function _onConnectionFailed( // eslint-disable-line max-params
  125. err: string,
  126. msg: string,
  127. credentials: Object,
  128. details: Object) {
  129. unsubscribe();
  130. dispatch(
  131. connectionFailed(
  132. connection, {
  133. credentials,
  134. details,
  135. name: err,
  136. message: msg
  137. }
  138. ));
  139. }
  140. /**
  141. * Unsubscribe the connection instance from
  142. * {@code CONNECTION_DISCONNECTED} and {@code CONNECTION_FAILED} events.
  143. *
  144. * @returns {void}
  145. */
  146. function unsubscribe() {
  147. connection.removeEventListener(
  148. JitsiConnectionEvents.CONNECTION_DISCONNECTED,
  149. _onConnectionDisconnected);
  150. connection.removeEventListener(
  151. JitsiConnectionEvents.CONNECTION_FAILED,
  152. _onConnectionFailed);
  153. }
  154. };
  155. }
  156. /**
  157. * Create an action for when the signaling connection has been lost.
  158. *
  159. * @param {JitsiConnection} connection - The {@code JitsiConnection} which
  160. * disconnected.
  161. * @private
  162. * @returns {{
  163. * type: CONNECTION_DISCONNECTED,
  164. * connection: JitsiConnection
  165. * }}
  166. */
  167. export function connectionDisconnected(connection: Object) {
  168. return {
  169. type: CONNECTION_DISCONNECTED,
  170. connection
  171. };
  172. }
  173. /**
  174. * Create an action for when the signaling connection has been established.
  175. *
  176. * @param {JitsiConnection} connection - The {@code JitsiConnection} which was
  177. * established.
  178. * @param {number} timeEstablished - The time at which the
  179. * {@code JitsiConnection} which was established.
  180. * @public
  181. * @returns {{
  182. * type: CONNECTION_ESTABLISHED,
  183. * connection: JitsiConnection,
  184. * timeEstablished: number
  185. * }}
  186. */
  187. export function connectionEstablished(
  188. connection: Object, timeEstablished: number) {
  189. return {
  190. type: CONNECTION_ESTABLISHED,
  191. connection,
  192. timeEstablished
  193. };
  194. }
  195. /**
  196. * Create an action for when the signaling connection could not be created.
  197. *
  198. * @param {JitsiConnection} connection - The {@code JitsiConnection} which
  199. * failed.
  200. * @param {ConnectionFailedError} error - Error.
  201. * @public
  202. * @returns {{
  203. * type: CONNECTION_FAILED,
  204. * connection: JitsiConnection,
  205. * error: ConnectionFailedError
  206. * }}
  207. */
  208. export function connectionFailed(
  209. connection: Object,
  210. error: ConnectionFailedError) {
  211. const { credentials } = error;
  212. if (credentials && !Object.keys(credentials).length) {
  213. error.credentials = undefined;
  214. }
  215. return {
  216. type: CONNECTION_FAILED,
  217. connection,
  218. error
  219. };
  220. }
  221. /**
  222. * Create an action for when a connection will connect.
  223. *
  224. * @param {JitsiConnection} connection - The {@code JitsiConnection} which will
  225. * connect.
  226. * @private
  227. * @returns {{
  228. * type: CONNECTION_WILL_CONNECT,
  229. * connection: JitsiConnection
  230. * }}
  231. */
  232. function _connectionWillConnect(connection) {
  233. return {
  234. type: CONNECTION_WILL_CONNECT,
  235. connection
  236. };
  237. }
  238. /**
  239. * Constructs options to be passed to the constructor of {@code JitsiConnection}
  240. * based on the redux state.
  241. *
  242. * @param {Object} state - The redux state.
  243. * @returns {Object} The options to be passed to the constructor of
  244. * {@code JitsiConnection}.
  245. */
  246. function _constructOptions(state) {
  247. // Deep clone the options to make sure we don't modify the object in the
  248. // redux store.
  249. const options = _.cloneDeep(state['features/base/config']);
  250. // Normalize the BOSH URL.
  251. let { bosh } = options;
  252. if (bosh) {
  253. const { locationURL } = state['features/base/connection'];
  254. if (bosh.startsWith('//')) {
  255. // By default our config.js doesn't include the protocol.
  256. bosh = `${locationURL.protocol}${bosh}`;
  257. } else if (bosh.startsWith('/')) {
  258. // Handle relative URLs, which won't work on mobile.
  259. const {
  260. protocol,
  261. host,
  262. contextRoot
  263. } = parseURIString(locationURL.href);
  264. // eslint-disable-next-line max-len
  265. bosh = `${protocol}//${host}${contextRoot || '/'}${bosh.substr(1)}`;
  266. }
  267. // Append room to the URL's search.
  268. const { room } = state['features/base/conference'];
  269. room && (bosh += `?room=${getBackendSafeRoomName(room)}`);
  270. // FIXME Remove deprecated 'bosh' option assignment at some point.
  271. options.serviceUrl = options.bosh = bosh;
  272. }
  273. return options;
  274. }
  275. /**
  276. * Closes connection.
  277. *
  278. * @returns {Function}
  279. */
  280. export function disconnect() {
  281. return (dispatch: Dispatch<any>, getState: Function): Promise<void> => {
  282. const state = getState();
  283. // The conference we have already joined or are joining.
  284. const conference_ = getCurrentConference(state);
  285. // Promise which completes when the conference has been left and the
  286. // connection has been disconnected.
  287. let promise;
  288. // Leave the conference.
  289. if (conference_) {
  290. // In a fashion similar to JitsiConference's CONFERENCE_LEFT event
  291. // (and the respective Redux action) which is fired after the
  292. // conference has been left, notify the application about the
  293. // intention to leave the conference.
  294. dispatch(conferenceWillLeave(conference_));
  295. promise
  296. = conference_.leave()
  297. .catch(error => {
  298. logger.warn(
  299. 'JitsiConference.leave() rejected with:',
  300. error);
  301. // The library lib-jitsi-meet failed to make the
  302. // JitsiConference leave. Which may be because
  303. // JitsiConference thinks it has already left.
  304. // Regardless of the failure reason, continue in
  305. // jitsi-meet as if the leave has succeeded.
  306. dispatch(conferenceLeft(conference_));
  307. });
  308. } else {
  309. promise = Promise.resolve();
  310. }
  311. // Disconnect the connection.
  312. const { connecting, connection } = state['features/base/connection'];
  313. // The connection we have already connected or are connecting.
  314. const connection_ = connection || connecting;
  315. if (connection_) {
  316. promise = promise.then(() => connection_.disconnect());
  317. } else {
  318. logger.info('No connection found while disconnecting.');
  319. }
  320. return promise;
  321. };
  322. }
  323. /**
  324. * Sets the location URL of the application, connecton, conference, etc.
  325. *
  326. * @param {URL} [locationURL] - The location URL of the application,
  327. * connection, conference, etc.
  328. * @returns {{
  329. * type: SET_LOCATION_URL,
  330. * locationURL: URL
  331. * }}
  332. */
  333. export function setLocationURL(locationURL: ?URL) {
  334. return {
  335. type: SET_LOCATION_URL,
  336. locationURL
  337. };
  338. }