Nelze vybrat více než 25 témat Téma musí začínat písmenem nebo číslem, může obsahovat pomlčky („-“) a může být dlouhé až 35 znaků.

actions.native.js 11KB

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