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.

actions.js 11KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394
  1. import { JitsiConferenceEvents } from '../lib-jitsi-meet';
  2. import {
  3. dominantSpeakerChanged,
  4. getLocalParticipant,
  5. participantJoined,
  6. participantLeft,
  7. participantRoleChanged,
  8. participantUpdated
  9. } from '../participants';
  10. import { trackAdded, trackRemoved } from '../tracks';
  11. import {
  12. CONFERENCE_FAILED,
  13. CONFERENCE_JOINED,
  14. CONFERENCE_LEFT,
  15. CONFERENCE_WILL_JOIN,
  16. CONFERENCE_WILL_LEAVE,
  17. LOCK_STATE_CHANGED,
  18. SET_LASTN,
  19. SET_PASSWORD,
  20. SET_ROOM
  21. } from './actionTypes';
  22. import {
  23. AVATAR_ID_COMMAND,
  24. AVATAR_URL_COMMAND,
  25. EMAIL_COMMAND
  26. } from './constants';
  27. import { _addLocalTracksToConference } from './functions';
  28. import type { Dispatch } from 'redux';
  29. /**
  30. * Adds conference (event) listeners.
  31. *
  32. * @param {JitsiConference} conference - The JitsiConference instance.
  33. * @param {Dispatch} dispatch - The Redux dispatch function.
  34. * @private
  35. * @returns {void}
  36. */
  37. function _addConferenceListeners(conference, dispatch) {
  38. conference.on(
  39. JitsiConferenceEvents.CONFERENCE_FAILED,
  40. (...args) => dispatch(conferenceFailed(conference, ...args)));
  41. conference.on(
  42. JitsiConferenceEvents.CONFERENCE_JOINED,
  43. (...args) => dispatch(conferenceJoined(conference, ...args)));
  44. conference.on(
  45. JitsiConferenceEvents.CONFERENCE_LEFT,
  46. (...args) => dispatch(conferenceLeft(conference, ...args)));
  47. conference.on(
  48. JitsiConferenceEvents.DOMINANT_SPEAKER_CHANGED,
  49. (...args) => dispatch(dominantSpeakerChanged(...args)));
  50. conference.on(
  51. JitsiConferenceEvents.LOCK_STATE_CHANGED,
  52. (...args) => dispatch(_lockStateChanged(conference, ...args)));
  53. conference.on(
  54. JitsiConferenceEvents.TRACK_ADDED,
  55. t => t && !t.isLocal() && dispatch(trackAdded(t)));
  56. conference.on(
  57. JitsiConferenceEvents.TRACK_REMOVED,
  58. t => t && !t.isLocal() && dispatch(trackRemoved(t)));
  59. conference.on(
  60. JitsiConferenceEvents.USER_JOINED,
  61. (id, user) => dispatch(participantJoined({
  62. id,
  63. name: user.getDisplayName(),
  64. role: user.getRole()
  65. })));
  66. conference.on(
  67. JitsiConferenceEvents.USER_LEFT,
  68. (...args) => dispatch(participantLeft(...args)));
  69. conference.on(
  70. JitsiConferenceEvents.USER_ROLE_CHANGED,
  71. (...args) => dispatch(participantRoleChanged(...args)));
  72. conference.addCommandListener(
  73. AVATAR_ID_COMMAND,
  74. (data, id) => dispatch(participantUpdated({
  75. id,
  76. avatarID: data.value
  77. })));
  78. conference.addCommandListener(
  79. AVATAR_URL_COMMAND,
  80. (data, id) => dispatch(participantUpdated({
  81. id,
  82. avatarURL: data.value
  83. })));
  84. conference.addCommandListener(
  85. EMAIL_COMMAND,
  86. (data, id) => dispatch(participantUpdated({
  87. id,
  88. email: data.value
  89. })));
  90. }
  91. /**
  92. * Sets the data for the local participant to the conference.
  93. *
  94. * @param {JitsiConference} conference - The JitsiConference instance.
  95. * @param {Object} state - The Redux state.
  96. * @returns {void}
  97. */
  98. function _setLocalParticipantData(conference, state) {
  99. const localParticipant
  100. = getLocalParticipant(state['features/base/participants']);
  101. conference.removeCommand(AVATAR_ID_COMMAND);
  102. conference.sendCommand(AVATAR_ID_COMMAND, {
  103. value: localParticipant.avatarID
  104. });
  105. }
  106. /**
  107. * Signals that a specific conference has failed.
  108. *
  109. * @param {JitsiConference} conference - The JitsiConference that has failed.
  110. * @param {string} error - The error describing/detailing the cause of the
  111. * failure.
  112. * @returns {{
  113. * type: CONFERENCE_FAILED,
  114. * conference: JitsiConference,
  115. * error: string
  116. * }}
  117. * @public
  118. */
  119. export function conferenceFailed(conference, error) {
  120. return {
  121. type: CONFERENCE_FAILED,
  122. conference,
  123. error
  124. };
  125. }
  126. /**
  127. * Attach any pre-existing local media to the conference once the conference has
  128. * been joined.
  129. *
  130. * @param {JitsiConference} conference - The JitsiConference instance which was
  131. * joined by the local participant.
  132. * @returns {Function}
  133. */
  134. export function conferenceJoined(conference) {
  135. return (dispatch, getState) => {
  136. const localTracks
  137. = getState()['features/base/tracks']
  138. .filter(t => t.local)
  139. .map(t => t.jitsiTrack);
  140. if (localTracks.length) {
  141. _addLocalTracksToConference(conference, localTracks);
  142. }
  143. dispatch({
  144. type: CONFERENCE_JOINED,
  145. conference
  146. });
  147. };
  148. }
  149. /**
  150. * Signals that a specific conference has been left.
  151. *
  152. * @param {JitsiConference} conference - The JitsiConference instance which was
  153. * left by the local participant.
  154. * @returns {{
  155. * type: CONFERENCE_LEFT,
  156. * conference: JitsiConference
  157. * }}
  158. */
  159. export function conferenceLeft(conference) {
  160. return {
  161. type: CONFERENCE_LEFT,
  162. conference
  163. };
  164. }
  165. /**
  166. * Signals the intention of the application to have the local participant join a
  167. * conference with a specific room (name). Similar in fashion
  168. * to CONFERENCE_JOINED.
  169. *
  170. * @param {string} room - The room (name) which identifies the conference the
  171. * local participant will (try to) join.
  172. * @returns {{
  173. * type: CONFERENCE_WILL_JOIN,
  174. * room: string
  175. * }}
  176. */
  177. function _conferenceWillJoin(room) {
  178. return {
  179. type: CONFERENCE_WILL_JOIN,
  180. room
  181. };
  182. }
  183. /**
  184. * Signals the intention of the application to have the local participant leave
  185. * a specific conference. Similar in fashion to CONFERENCE_LEFT. Contrary to it
  186. * though, it's not guaranteed because CONFERENCE_LEFT may be triggered by
  187. * lib-jitsi-meet and not the application.
  188. *
  189. * @param {JitsiConference} conference - The JitsiConference instance which will
  190. * be left by the local participant.
  191. * @returns {{
  192. * type: CONFERENCE_LEFT,
  193. * conference: JitsiConference
  194. * }}
  195. */
  196. export function conferenceWillLeave(conference) {
  197. return {
  198. type: CONFERENCE_WILL_LEAVE,
  199. conference
  200. };
  201. }
  202. /**
  203. * Initializes a new conference.
  204. *
  205. * @returns {Function}
  206. */
  207. export function createConference() {
  208. return (dispatch, getState) => {
  209. const state = getState();
  210. const connection = state['features/base/connection'].connection;
  211. if (!connection) {
  212. throw new Error('Cannot create conference without connection');
  213. }
  214. const { password, room } = state['features/base/conference'];
  215. if (typeof room === 'undefined' || room === '') {
  216. throw new Error('Cannot join conference without room name');
  217. }
  218. dispatch(_conferenceWillJoin(room));
  219. // TODO Take options from config.
  220. const conference
  221. = connection.initJitsiConference(
  222. // XXX Lib-jitsi-meet does not accept uppercase letters.
  223. room.toLowerCase(),
  224. {
  225. openSctp: true
  226. // FIXME I tested H.264 from iPhone 6S during a morning
  227. // standup but, unfortunately, the other participants who
  228. // happened to be running the Web app saw only black.
  229. //
  230. // preferH264: true
  231. });
  232. _addConferenceListeners(conference, dispatch);
  233. _setLocalParticipantData(conference, state);
  234. conference.join(password);
  235. };
  236. }
  237. /**
  238. * Signals that the lock state of a specific JitsiConference changed.
  239. *
  240. * @param {JitsiConference} conference - The JitsiConference which had its lock
  241. * state changed.
  242. * @param {boolean} locked - If the specified conference became locked, true;
  243. * otherwise, false.
  244. * @returns {{
  245. * type: LOCK_STATE_CHANGED,
  246. * conference: JitsiConference,
  247. * locked: boolean
  248. * }}
  249. */
  250. function _lockStateChanged(conference, locked) {
  251. return {
  252. type: LOCK_STATE_CHANGED,
  253. conference,
  254. locked
  255. };
  256. }
  257. /**
  258. * Sets the video channel's last N (value) of the current conference. A value of
  259. * undefined shall be used to reset it to the default value.
  260. *
  261. * @param {(number|undefined)} lastN - The last N value to be set.
  262. * @returns {Function}
  263. */
  264. export function setLastN(lastN: ?number) {
  265. return (dispatch: Dispatch<*>, getState: Function) => {
  266. if (typeof lastN === 'undefined') {
  267. const { config } = getState()['features/base/lib-jitsi-meet'];
  268. /* eslint-disable no-param-reassign */
  269. lastN = config.channelLastN;
  270. if (typeof lastN === 'undefined') {
  271. lastN = -1;
  272. }
  273. /* eslint-enable no-param-reassign */
  274. }
  275. dispatch({
  276. type: SET_LASTN,
  277. lastN
  278. });
  279. };
  280. }
  281. /**
  282. * Sets the password to join or lock a specific JitsiConference.
  283. *
  284. * @param {JitsiConference} conference - The JitsiConference which requires a
  285. * password to join or is to be locked with the specified password.
  286. * @param {Function} method - The JitsiConference method of password protection
  287. * such as join or lock.
  288. * @param {string} password - The password with which the specified conference
  289. * is to be joined or locked.
  290. * @returns {Function}
  291. */
  292. export function setPassword(conference, method, password) {
  293. return (dispatch, getState) => {
  294. switch (method) {
  295. case conference.join: {
  296. let state = getState()['features/base/conference'];
  297. // Make sure that the action will set a password for a conference
  298. // that the application wants joined.
  299. if (state.passwordRequired === conference) {
  300. dispatch({
  301. type: SET_PASSWORD,
  302. conference,
  303. method,
  304. password
  305. });
  306. // Join the conference with the newly-set password.
  307. // Make sure that the action did set the password.
  308. state = getState()['features/base/conference'];
  309. if (state.password === password
  310. && !state.passwordRequired
  311. // Make sure that the application still wants the
  312. // conference joined.
  313. && !state.conference) {
  314. method.call(conference, password);
  315. }
  316. }
  317. break;
  318. }
  319. case conference.lock: {
  320. const state = getState()['features/base/conference'];
  321. if (state.conference === conference) {
  322. return (
  323. method.call(conference, password)
  324. .then(() => dispatch({
  325. type: SET_PASSWORD,
  326. conference,
  327. method,
  328. password
  329. })));
  330. }
  331. return Promise.reject();
  332. }
  333. }
  334. };
  335. }
  336. /**
  337. * Sets (the name of) the room of the conference to be joined.
  338. *
  339. * @param {(string|undefined)} room - The name of the room of the conference to
  340. * be joined.
  341. * @returns {{
  342. * type: SET_ROOM,
  343. * room: string
  344. * }}
  345. */
  346. export function setRoom(room) {
  347. return {
  348. type: SET_ROOM,
  349. room
  350. };
  351. }