Du kan inte välja fler än 25 ämnen Ämnen måste starta med en bokstav eller siffra, kan innehålla bindestreck ('-') och vara max 35 tecken långa.

actions.js 21KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747
  1. // @flow
  2. import type { Dispatch } from 'redux';
  3. import {
  4. createStartMutedConfigurationEvent,
  5. sendAnalytics
  6. } from '../../analytics';
  7. import { getName } from '../../app';
  8. import { endpointMessageReceived } from '../../subtitles';
  9. import { JITSI_CONNECTION_CONFERENCE_KEY } from '../connection';
  10. import { JitsiConferenceEvents } from '../lib-jitsi-meet';
  11. import { setAudioMuted, setVideoMuted } from '../media';
  12. import {
  13. dominantSpeakerChanged,
  14. getNormalizedDisplayName,
  15. participantConnectionStatusChanged,
  16. participantKicked,
  17. participantMutedUs,
  18. participantPresenceChanged,
  19. participantRoleChanged,
  20. participantUpdated
  21. } from '../participants';
  22. import { getLocalTracks, trackAdded, trackRemoved } from '../tracks';
  23. import {
  24. getBackendSafeRoomName,
  25. getJitsiMeetGlobalNS
  26. } from '../util';
  27. import {
  28. AUTH_STATUS_CHANGED,
  29. CONFERENCE_FAILED,
  30. CONFERENCE_JOINED,
  31. CONFERENCE_LEFT,
  32. CONFERENCE_SUBJECT_CHANGED,
  33. CONFERENCE_WILL_JOIN,
  34. CONFERENCE_WILL_LEAVE,
  35. DATA_CHANNEL_OPENED,
  36. KICKED_OUT,
  37. LOCK_STATE_CHANGED,
  38. P2P_STATUS_CHANGED,
  39. SEND_TONES,
  40. SET_DESKTOP_SHARING_ENABLED,
  41. SET_FOLLOW_ME,
  42. SET_MAX_RECEIVER_VIDEO_QUALITY,
  43. SET_PASSWORD,
  44. SET_PASSWORD_FAILED,
  45. SET_PREFERRED_RECEIVER_VIDEO_QUALITY,
  46. SET_ROOM,
  47. SET_PENDING_SUBJECT_CHANGE,
  48. SET_START_MUTED_POLICY
  49. } from './actionTypes';
  50. import {
  51. AVATAR_ID_COMMAND,
  52. AVATAR_URL_COMMAND,
  53. EMAIL_COMMAND,
  54. JITSI_CONFERENCE_URL_KEY
  55. } from './constants';
  56. import {
  57. _addLocalTracksToConference,
  58. commonUserJoinedHandling,
  59. commonUserLeftHandling,
  60. getCurrentConference,
  61. sendLocalParticipant
  62. } from './functions';
  63. import logger from './logger';
  64. declare var APP: Object;
  65. /**
  66. * Adds conference (event) listeners.
  67. *
  68. * @param {JitsiConference} conference - The JitsiConference instance.
  69. * @param {Dispatch} dispatch - The Redux dispatch function.
  70. * @private
  71. * @returns {void}
  72. */
  73. function _addConferenceListeners(conference, dispatch) {
  74. // Dispatches into features/base/conference follow:
  75. conference.on(
  76. JitsiConferenceEvents.CONFERENCE_FAILED,
  77. (...args) => dispatch(conferenceFailed(conference, ...args)));
  78. conference.on(
  79. JitsiConferenceEvents.CONFERENCE_JOINED,
  80. (...args) => dispatch(conferenceJoined(conference, ...args)));
  81. conference.on(
  82. JitsiConferenceEvents.CONFERENCE_LEFT,
  83. (...args) => dispatch(conferenceLeft(conference, ...args)));
  84. conference.on(JitsiConferenceEvents.SUBJECT_CHANGED,
  85. (...args) => dispatch(conferenceSubjectChanged(...args)));
  86. conference.on(
  87. JitsiConferenceEvents.KICKED,
  88. (...args) => dispatch(kickedOut(conference, ...args)));
  89. conference.on(
  90. JitsiConferenceEvents.PARTICIPANT_KICKED,
  91. (kicker, kicked) => dispatch(participantKicked(kicker, kicked)));
  92. conference.on(
  93. JitsiConferenceEvents.LOCK_STATE_CHANGED,
  94. (...args) => dispatch(lockStateChanged(conference, ...args)));
  95. // Dispatches into features/base/media follow:
  96. conference.on(
  97. JitsiConferenceEvents.STARTED_MUTED,
  98. () => {
  99. const audioMuted = Boolean(conference.startAudioMuted);
  100. const videoMuted = Boolean(conference.startVideoMuted);
  101. sendAnalytics(createStartMutedConfigurationEvent(
  102. 'remote', audioMuted, videoMuted));
  103. logger.log(`Start muted: ${audioMuted ? 'audio, ' : ''}${
  104. videoMuted ? 'video' : ''}`);
  105. // XXX Jicofo tells lib-jitsi-meet to start with audio and/or video
  106. // muted i.e. Jicofo expresses an intent. Lib-jitsi-meet has turned
  107. // Jicofo's intent into reality by actually muting the respective
  108. // tracks. The reality is expressed in base/tracks already so what
  109. // is left is to express Jicofo's intent in base/media.
  110. // TODO Maybe the app needs to learn about Jicofo's intent and
  111. // transfer that intent to lib-jitsi-meet instead of lib-jitsi-meet
  112. // acting on Jicofo's intent without the app's knowledge.
  113. dispatch(setAudioMuted(audioMuted));
  114. dispatch(setVideoMuted(videoMuted));
  115. });
  116. // Dispatches into features/base/tracks follow:
  117. conference.on(
  118. JitsiConferenceEvents.TRACK_ADDED,
  119. t => t && !t.isLocal() && dispatch(trackAdded(t)));
  120. conference.on(
  121. JitsiConferenceEvents.TRACK_REMOVED,
  122. t => t && !t.isLocal() && dispatch(trackRemoved(t)));
  123. conference.on(
  124. JitsiConferenceEvents.TRACK_MUTE_CHANGED,
  125. (_, participantThatMutedUs) => {
  126. if (participantThatMutedUs) {
  127. dispatch(participantMutedUs(participantThatMutedUs));
  128. }
  129. });
  130. // Dispatches into features/base/participants follow:
  131. conference.on(
  132. JitsiConferenceEvents.DISPLAY_NAME_CHANGED,
  133. (id, displayName) => dispatch(participantUpdated({
  134. conference,
  135. id,
  136. name: getNormalizedDisplayName(displayName)
  137. })));
  138. conference.on(
  139. JitsiConferenceEvents.DOMINANT_SPEAKER_CHANGED,
  140. id => dispatch(dominantSpeakerChanged(id, conference)));
  141. conference.on(
  142. JitsiConferenceEvents.ENDPOINT_MESSAGE_RECEIVED,
  143. (...args) => dispatch(endpointMessageReceived(...args)));
  144. conference.on(
  145. JitsiConferenceEvents.PARTICIPANT_CONN_STATUS_CHANGED,
  146. (...args) => dispatch(participantConnectionStatusChanged(...args)));
  147. conference.on(
  148. JitsiConferenceEvents.USER_JOINED,
  149. (id, user) => commonUserJoinedHandling({ dispatch }, conference, user));
  150. conference.on(
  151. JitsiConferenceEvents.USER_LEFT,
  152. (id, user) => commonUserLeftHandling({ dispatch }, conference, user));
  153. conference.on(
  154. JitsiConferenceEvents.USER_ROLE_CHANGED,
  155. (...args) => dispatch(participantRoleChanged(...args)));
  156. conference.on(
  157. JitsiConferenceEvents.USER_STATUS_CHANGED,
  158. (...args) => dispatch(participantPresenceChanged(...args)));
  159. conference.on(
  160. JitsiConferenceEvents.BOT_TYPE_CHANGED,
  161. (id, botType) => dispatch(participantUpdated({
  162. conference,
  163. id,
  164. botType
  165. })));
  166. conference.addCommandListener(
  167. AVATAR_ID_COMMAND,
  168. (data, id) => dispatch(participantUpdated({
  169. conference,
  170. id,
  171. avatarID: data.value
  172. })));
  173. conference.addCommandListener(
  174. AVATAR_URL_COMMAND,
  175. (data, id) => dispatch(participantUpdated({
  176. conference,
  177. id,
  178. avatarURL: data.value
  179. })));
  180. conference.addCommandListener(
  181. EMAIL_COMMAND,
  182. (data, id) => dispatch(participantUpdated({
  183. conference,
  184. id,
  185. email: data.value
  186. })));
  187. }
  188. /**
  189. * Updates the current known state of server-side authentication.
  190. *
  191. * @param {boolean} authEnabled - Whether or not server authentication is
  192. * enabled.
  193. * @param {string} authLogin - The current name of the logged in user, if any.
  194. * @returns {{
  195. * type: AUTH_STATUS_CHANGED,
  196. * authEnabled: boolean,
  197. * authLogin: string
  198. * }}
  199. */
  200. export function authStatusChanged(authEnabled: boolean, authLogin: string) {
  201. return {
  202. type: AUTH_STATUS_CHANGED,
  203. authEnabled,
  204. authLogin
  205. };
  206. }
  207. /**
  208. * Signals that a specific conference has failed.
  209. *
  210. * @param {JitsiConference} conference - The JitsiConference that has failed.
  211. * @param {string} error - The error describing/detailing the cause of the
  212. * failure.
  213. * @returns {{
  214. * type: CONFERENCE_FAILED,
  215. * conference: JitsiConference,
  216. * error: Error
  217. * }}
  218. * @public
  219. */
  220. export function conferenceFailed(conference: Object, error: string) {
  221. return {
  222. type: CONFERENCE_FAILED,
  223. conference,
  224. // Make the error resemble an Error instance (to the extent that
  225. // jitsi-meet needs it).
  226. error: {
  227. name: error,
  228. recoverable: undefined
  229. }
  230. };
  231. }
  232. /**
  233. * Signals that a specific conference has been joined.
  234. *
  235. * @param {JitsiConference} conference - The JitsiConference instance which was
  236. * joined by the local participant.
  237. * @returns {{
  238. * type: CONFERENCE_JOINED,
  239. * conference: JitsiConference
  240. * }}
  241. */
  242. export function conferenceJoined(conference: Object) {
  243. return {
  244. type: CONFERENCE_JOINED,
  245. conference
  246. };
  247. }
  248. /**
  249. * Signals that a specific conference has been left.
  250. *
  251. * @param {JitsiConference} conference - The JitsiConference instance which was
  252. * left by the local participant.
  253. * @returns {{
  254. * type: CONFERENCE_LEFT,
  255. * conference: JitsiConference
  256. * }}
  257. */
  258. export function conferenceLeft(conference: Object) {
  259. return {
  260. type: CONFERENCE_LEFT,
  261. conference
  262. };
  263. }
  264. /**
  265. * Signals that the conference subject has been changed.
  266. *
  267. * @param {string} subject - The new subject.
  268. * @returns {{
  269. * type: CONFERENCE_SUBJECT_CHANGED,
  270. * subject: string
  271. * }}
  272. */
  273. export function conferenceSubjectChanged(subject: string) {
  274. return {
  275. type: CONFERENCE_SUBJECT_CHANGED,
  276. subject
  277. };
  278. }
  279. /**
  280. * Adds any existing local tracks to a specific conference before the conference
  281. * is joined. Then signals the intention of the application to have the local
  282. * participant join the specified conference.
  283. *
  284. * @param {JitsiConference} conference - The {@code JitsiConference} instance
  285. * the local participant will (try to) join.
  286. * @returns {Function}
  287. */
  288. function _conferenceWillJoin(conference: Object) {
  289. return (dispatch: Dispatch<any>, getState: Function) => {
  290. const localTracks
  291. = getLocalTracks(getState()['features/base/tracks'])
  292. .map(t => t.jitsiTrack);
  293. if (localTracks.length) {
  294. _addLocalTracksToConference(conference, localTracks);
  295. }
  296. dispatch(conferenceWillJoin(conference));
  297. };
  298. }
  299. /**
  300. * Signals the intention of the application to have the local participant
  301. * join the specified conference.
  302. *
  303. * @param {JitsiConference} conference - The {@code JitsiConference} instance
  304. * the local participant will (try to) join.
  305. * @returns {{
  306. * type: CONFERENCE_WILL_JOIN,
  307. * conference: JitsiConference
  308. * }}
  309. */
  310. export function conferenceWillJoin(conference: Object) {
  311. return {
  312. type: CONFERENCE_WILL_JOIN,
  313. conference
  314. };
  315. }
  316. /**
  317. * Signals the intention of the application to have the local participant leave
  318. * a specific conference. Similar in fashion to CONFERENCE_LEFT. Contrary to it
  319. * though, it's not guaranteed because CONFERENCE_LEFT may be triggered by
  320. * lib-jitsi-meet and not the application.
  321. *
  322. * @param {JitsiConference} conference - The JitsiConference instance which will
  323. * be left by the local participant.
  324. * @returns {{
  325. * type: CONFERENCE_LEFT,
  326. * conference: JitsiConference
  327. * }}
  328. */
  329. export function conferenceWillLeave(conference: Object) {
  330. return {
  331. type: CONFERENCE_WILL_LEAVE,
  332. conference
  333. };
  334. }
  335. /**
  336. * Initializes a new conference.
  337. *
  338. * @returns {Function}
  339. */
  340. export function createConference() {
  341. return (dispatch: Function, getState: Function) => {
  342. const state = getState();
  343. const { connection, locationURL } = state['features/base/connection'];
  344. if (!connection) {
  345. throw new Error('Cannot create a conference without a connection!');
  346. }
  347. const { password, room } = state['features/base/conference'];
  348. if (!room) {
  349. throw new Error('Cannot join a conference without a room name!');
  350. }
  351. const conference
  352. = connection.initJitsiConference(
  353. getBackendSafeRoomName(room), {
  354. ...state['features/base/config'],
  355. applicationName: getName(),
  356. getWiFiStatsMethod: getJitsiMeetGlobalNS().getWiFiStats,
  357. confID: `${locationURL.host}${locationURL.pathname}`
  358. });
  359. connection[JITSI_CONNECTION_CONFERENCE_KEY] = conference;
  360. conference[JITSI_CONFERENCE_URL_KEY] = locationURL;
  361. dispatch(_conferenceWillJoin(conference));
  362. _addConferenceListeners(conference, dispatch);
  363. sendLocalParticipant(state, conference);
  364. conference.join(password);
  365. };
  366. }
  367. /**
  368. * Will try to join the conference again in case it failed earlier with
  369. * {@link JitsiConferenceErrors.AUTHENTICATION_REQUIRED}. It means that Jicofo
  370. * did not allow to create new room from anonymous domain, but it can be tried
  371. * again later in case authenticated user created it in the meantime.
  372. *
  373. * @returns {Function}
  374. */
  375. export function checkIfCanJoin() {
  376. return (dispatch: Function, getState: Function) => {
  377. const { authRequired, password }
  378. = getState()['features/base/conference'];
  379. authRequired && dispatch(_conferenceWillJoin(authRequired));
  380. authRequired && authRequired.join(password);
  381. };
  382. }
  383. /**
  384. * Signals the data channel with the bridge has successfully opened.
  385. *
  386. * @returns {{
  387. * type: DATA_CHANNEL_OPENED
  388. * }}
  389. */
  390. export function dataChannelOpened() {
  391. return {
  392. type: DATA_CHANNEL_OPENED
  393. };
  394. }
  395. /**
  396. * Signals that we've been kicked out of the conference.
  397. *
  398. * @param {JitsiConference} conference - The {@link JitsiConference} instance
  399. * for which the event is being signaled.
  400. * @param {JitsiParticipant} participant - The {@link JitsiParticipant}
  401. * instance which initiated the kick event.
  402. * @returns {{
  403. * type: KICKED_OUT,
  404. * conference: JitsiConference,
  405. * participant: JitsiParticipant
  406. * }}
  407. */
  408. export function kickedOut(conference: Object, participant: Object) {
  409. return {
  410. type: KICKED_OUT,
  411. conference,
  412. participant
  413. };
  414. }
  415. /**
  416. * Signals that the lock state of a specific JitsiConference changed.
  417. *
  418. * @param {JitsiConference} conference - The JitsiConference which had its lock
  419. * state changed.
  420. * @param {boolean} locked - If the specified conference became locked, true;
  421. * otherwise, false.
  422. * @returns {{
  423. * type: LOCK_STATE_CHANGED,
  424. * conference: JitsiConference,
  425. * locked: boolean
  426. * }}
  427. */
  428. export function lockStateChanged(conference: Object, locked: boolean) {
  429. return {
  430. type: LOCK_STATE_CHANGED,
  431. conference,
  432. locked
  433. };
  434. }
  435. /**
  436. * Updates the known state of start muted policies.
  437. *
  438. * @param {boolean} audioMuted - Whether or not members will join the conference
  439. * as audio muted.
  440. * @param {boolean} videoMuted - Whether or not members will join the conference
  441. * as video muted.
  442. * @returns {{
  443. * type: SET_START_MUTED_POLICY,
  444. * startAudioMutedPolicy: boolean,
  445. * startVideoMutedPolicy: boolean
  446. * }}
  447. */
  448. export function onStartMutedPolicyChanged(
  449. audioMuted: boolean, videoMuted: boolean) {
  450. return {
  451. type: SET_START_MUTED_POLICY,
  452. startAudioMutedPolicy: audioMuted,
  453. startVideoMutedPolicy: videoMuted
  454. };
  455. }
  456. /**
  457. * Sets whether or not peer2peer is currently enabled.
  458. *
  459. * @param {boolean} p2p - Whether or not peer2peer is currently active.
  460. * @returns {{
  461. * type: P2P_STATUS_CHANGED,
  462. * p2p: boolean
  463. * }}
  464. */
  465. export function p2pStatusChanged(p2p: boolean) {
  466. return {
  467. type: P2P_STATUS_CHANGED,
  468. p2p
  469. };
  470. }
  471. /**
  472. * Signals to play touch tones.
  473. *
  474. * @param {string} tones - The tones to play.
  475. * @param {number} [duration] - How long to play each tone.
  476. * @param {number} [pause] - How long to pause between each tone.
  477. * @returns {{
  478. * type: SEND_TONES,
  479. * tones: string,
  480. * duration: number,
  481. * pause: number
  482. * }}
  483. */
  484. export function sendTones(tones: string, duration: number, pause: number) {
  485. return {
  486. type: SEND_TONES,
  487. tones,
  488. duration,
  489. pause
  490. };
  491. }
  492. /**
  493. * Sets the flag for indicating if desktop sharing is enabled.
  494. *
  495. * @param {boolean} desktopSharingEnabled - True if desktop sharing is enabled.
  496. * @returns {{
  497. * type: SET_DESKTOP_SHARING_ENABLED,
  498. * desktopSharingEnabled: boolean
  499. * }}
  500. */
  501. export function setDesktopSharingEnabled(desktopSharingEnabled: boolean) {
  502. return {
  503. type: SET_DESKTOP_SHARING_ENABLED,
  504. desktopSharingEnabled
  505. };
  506. }
  507. /**
  508. * Enables or disables the Follow Me feature.
  509. *
  510. * @param {boolean} enabled - Whether or not Follow Me should be enabled.
  511. * @returns {{
  512. * type: SET_FOLLOW_ME,
  513. * enabled: boolean
  514. * }}
  515. */
  516. export function setFollowMe(enabled: boolean) {
  517. return {
  518. type: SET_FOLLOW_ME,
  519. enabled
  520. };
  521. }
  522. /**
  523. * Sets the max frame height that should be received from remote videos.
  524. *
  525. * @param {number} maxReceiverVideoQuality - The max video frame height to
  526. * receive.
  527. * @returns {{
  528. * type: SET_MAX_RECEIVER_VIDEO_QUALITY,
  529. * maxReceiverVideoQuality: number
  530. * }}
  531. */
  532. export function setMaxReceiverVideoQuality(maxReceiverVideoQuality: number) {
  533. return {
  534. type: SET_MAX_RECEIVER_VIDEO_QUALITY,
  535. maxReceiverVideoQuality
  536. };
  537. }
  538. /**
  539. * Sets the password to join or lock a specific JitsiConference.
  540. *
  541. * @param {JitsiConference} conference - The JitsiConference which requires a
  542. * password to join or is to be locked with the specified password.
  543. * @param {Function} method - The JitsiConference method of password protection
  544. * such as join or lock.
  545. * @param {string} password - The password with which the specified conference
  546. * is to be joined or locked.
  547. * @returns {Function}
  548. */
  549. export function setPassword(
  550. conference: Object,
  551. method: Function,
  552. password: string) {
  553. return (dispatch: Dispatch<any>, getState: Function): ?Promise<void> => {
  554. switch (method) {
  555. case conference.join: {
  556. let state = getState()['features/base/conference'];
  557. // Make sure that the action will set a password for a conference
  558. // that the application wants joined.
  559. if (state.passwordRequired === conference) {
  560. dispatch({
  561. type: SET_PASSWORD,
  562. conference,
  563. method,
  564. password
  565. });
  566. // Join the conference with the newly-set password.
  567. // Make sure that the action did set the password.
  568. state = getState()['features/base/conference'];
  569. if (state.password === password
  570. && !state.passwordRequired
  571. // Make sure that the application still wants the
  572. // conference joined.
  573. && !state.conference) {
  574. method.call(conference, password);
  575. }
  576. }
  577. break;
  578. }
  579. case conference.lock: {
  580. const state = getState()['features/base/conference'];
  581. if (state.conference === conference) {
  582. return (
  583. method.call(conference, password)
  584. .then(() => dispatch({
  585. type: SET_PASSWORD,
  586. conference,
  587. method,
  588. password
  589. }))
  590. .catch(error => dispatch({
  591. type: SET_PASSWORD_FAILED,
  592. error
  593. }))
  594. );
  595. }
  596. return Promise.reject();
  597. }
  598. }
  599. };
  600. }
  601. /**
  602. * Sets the max frame height the user prefers to receive from remote participant
  603. * videos.
  604. *
  605. * @param {number} preferredReceiverVideoQuality - The max video resolution to
  606. * receive.
  607. * @returns {{
  608. * type: SET_PREFERRED_RECEIVER_VIDEO_QUALITY,
  609. * preferredReceiverVideoQuality: number
  610. * }}
  611. */
  612. export function setPreferredReceiverVideoQuality(
  613. preferredReceiverVideoQuality: number) {
  614. return {
  615. type: SET_PREFERRED_RECEIVER_VIDEO_QUALITY,
  616. preferredReceiverVideoQuality
  617. };
  618. }
  619. /**
  620. * Sets (the name of) the room of the conference to be joined.
  621. *
  622. * @param {(string|undefined)} room - The name of the room of the conference to
  623. * be joined.
  624. * @returns {{
  625. * type: SET_ROOM,
  626. * room: string
  627. * }}
  628. */
  629. export function setRoom(room: ?string) {
  630. return {
  631. type: SET_ROOM,
  632. room
  633. };
  634. }
  635. /**
  636. * Sets whether or not members should join audio and/or video muted.
  637. *
  638. * @param {boolean} startAudioMuted - Whether or not members will join the
  639. * conference as audio muted.
  640. * @param {boolean} startVideoMuted - Whether or not members will join the
  641. * conference as video muted.
  642. * @returns {Function}
  643. */
  644. export function setStartMutedPolicy(
  645. startAudioMuted: boolean, startVideoMuted: boolean) {
  646. return (dispatch: Dispatch<any>, getState: Function) => {
  647. const conference = getCurrentConference(getState());
  648. conference && conference.setStartMutedPolicy({
  649. audio: startAudioMuted,
  650. video: startVideoMuted
  651. });
  652. return dispatch(
  653. onStartMutedPolicyChanged(startAudioMuted, startVideoMuted));
  654. };
  655. }
  656. /**
  657. * Changing conference subject.
  658. *
  659. * @param {string} subject - The new subject.
  660. * @returns {void}
  661. */
  662. export function setSubject(subject: string = '') {
  663. return (dispatch: Dispatch<any>, getState: Function) => {
  664. const { conference } = getState()['features/base/conference'];
  665. if (conference) {
  666. conference.setSubject(subject);
  667. } else {
  668. dispatch({
  669. type: SET_PENDING_SUBJECT_CHANGE,
  670. subject
  671. });
  672. }
  673. };
  674. }