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 13KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497
  1. import { NOTIFICATION_TIMEOUT, showNotification } from '../../notifications';
  2. import { set } from '../redux';
  3. import {
  4. DOMINANT_SPEAKER_CHANGED,
  5. HIDDEN_PARTICIPANT_JOINED,
  6. HIDDEN_PARTICIPANT_LEFT,
  7. GRANT_MODERATOR,
  8. KICK_PARTICIPANT,
  9. MUTE_REMOTE_PARTICIPANT,
  10. PARTICIPANT_ID_CHANGED,
  11. PARTICIPANT_JOINED,
  12. PARTICIPANT_KICKED,
  13. PARTICIPANT_LEFT,
  14. PARTICIPANT_UPDATED,
  15. PIN_PARTICIPANT,
  16. SET_LOADABLE_AVATAR_URL
  17. } from './actionTypes';
  18. import {
  19. getLocalParticipant,
  20. getNormalizedDisplayName,
  21. getParticipantDisplayName
  22. } from './functions';
  23. /**
  24. * Create an action for when dominant speaker changes.
  25. *
  26. * @param {string} id - Participant's ID.
  27. * @param {JitsiConference} conference - The {@code JitsiConference} associated
  28. * with the participant identified by the specified {@code id}. Only the local
  29. * participant is allowed to not specify an associated {@code JitsiConference}
  30. * instance.
  31. * @returns {{
  32. * type: DOMINANT_SPEAKER_CHANGED,
  33. * participant: {
  34. * conference: JitsiConference,
  35. * id: string
  36. * }
  37. * }}
  38. */
  39. export function dominantSpeakerChanged(id, conference) {
  40. return {
  41. type: DOMINANT_SPEAKER_CHANGED,
  42. participant: {
  43. conference,
  44. id
  45. }
  46. };
  47. }
  48. /**
  49. * Create an action for granting moderator to a participant.
  50. *
  51. * @param {string} id - Participant's ID.
  52. * @returns {{
  53. * type: GRANT_MODERATOR,
  54. * id: string
  55. * }}
  56. */
  57. export function grantModerator(id) {
  58. return {
  59. type: GRANT_MODERATOR,
  60. id
  61. };
  62. }
  63. /**
  64. * Create an action for removing a participant from the conference.
  65. *
  66. * @param {string} id - Participant's ID.
  67. * @returns {{
  68. * type: KICK_PARTICIPANT,
  69. * id: string
  70. * }}
  71. */
  72. export function kickParticipant(id) {
  73. return {
  74. type: KICK_PARTICIPANT,
  75. id
  76. };
  77. }
  78. /**
  79. * Creates an action to signal the connection status of the local participant
  80. * has changed.
  81. *
  82. * @param {string} connectionStatus - The current connection status of the local
  83. * participant, as enumerated by the library's participantConnectionStatus
  84. * constants.
  85. * @returns {Function}
  86. */
  87. export function localParticipantConnectionStatusChanged(connectionStatus) {
  88. return (dispatch, getState) => {
  89. const participant = getLocalParticipant(getState);
  90. if (participant) {
  91. return dispatch(participantConnectionStatusChanged(
  92. participant.id,
  93. connectionStatus));
  94. }
  95. };
  96. }
  97. /**
  98. * Action to signal that the ID of local participant has changed. It happens
  99. * when the local participant joins a new conference or leaves an existing
  100. * conference.
  101. *
  102. * @param {string} id - New ID for local participant.
  103. * @returns {Function}
  104. */
  105. export function localParticipantIdChanged(id) {
  106. return (dispatch, getState) => {
  107. const participant = getLocalParticipant(getState);
  108. if (participant) {
  109. return dispatch({
  110. type: PARTICIPANT_ID_CHANGED,
  111. // XXX A participant is identified by an id-conference pair.
  112. // Only the local participant is with an undefined conference.
  113. conference: undefined,
  114. newValue: id,
  115. oldValue: participant.id
  116. });
  117. }
  118. };
  119. }
  120. /**
  121. * Action to signal that a local participant has joined.
  122. *
  123. * @param {Participant} participant={} - Information about participant.
  124. * @returns {{
  125. * type: PARTICIPANT_JOINED,
  126. * participant: Participant
  127. * }}
  128. */
  129. export function localParticipantJoined(participant = {}) {
  130. return participantJoined(set(participant, 'local', true));
  131. }
  132. /**
  133. * Action to remove a local participant.
  134. *
  135. * @returns {Function}
  136. */
  137. export function localParticipantLeft() {
  138. return (dispatch, getState) => {
  139. const participant = getLocalParticipant(getState);
  140. if (participant) {
  141. return (
  142. dispatch(
  143. participantLeft(
  144. participant.id,
  145. // XXX Only the local participant is allowed to leave
  146. // without stating the JitsiConference instance because
  147. // the local participant is uniquely identified by the
  148. // very fact that there is only one local participant
  149. // (and the fact that the local participant "joins" at
  150. // the beginning of the app and "leaves" at the end of
  151. // the app).
  152. undefined)));
  153. }
  154. };
  155. }
  156. /**
  157. * Action to signal the role of the local participant has changed. It can happen
  158. * when the participant has joined a conference, even before a non-default local
  159. * id has been set, or after a moderator leaves.
  160. *
  161. * @param {string} role - The new role of the local participant.
  162. * @returns {Function}
  163. */
  164. export function localParticipantRoleChanged(role) {
  165. return (dispatch, getState) => {
  166. const participant = getLocalParticipant(getState);
  167. if (participant) {
  168. return dispatch(participantRoleChanged(participant.id, role));
  169. }
  170. };
  171. }
  172. /**
  173. * Create an action for muting another participant in the conference.
  174. *
  175. * @param {string} id - Participant's ID.
  176. * @returns {{
  177. * type: MUTE_REMOTE_PARTICIPANT,
  178. * id: string
  179. * }}
  180. */
  181. export function muteRemoteParticipant(id) {
  182. return {
  183. type: MUTE_REMOTE_PARTICIPANT,
  184. id
  185. };
  186. }
  187. /**
  188. * Action to update a participant's connection status.
  189. *
  190. * @param {string} id - Participant's ID.
  191. * @param {string} connectionStatus - The new connection status of the
  192. * participant.
  193. * @returns {{
  194. * type: PARTICIPANT_UPDATED,
  195. * participant: {
  196. * connectionStatus: string,
  197. * id: string
  198. * }
  199. * }}
  200. */
  201. export function participantConnectionStatusChanged(id, connectionStatus) {
  202. return {
  203. type: PARTICIPANT_UPDATED,
  204. participant: {
  205. connectionStatus,
  206. id
  207. }
  208. };
  209. }
  210. /**
  211. * Action to signal that a participant has joined.
  212. *
  213. * @param {Participant} participant - Information about participant.
  214. * @returns {{
  215. * type: PARTICIPANT_JOINED,
  216. * participant: Participant
  217. * }}
  218. */
  219. export function participantJoined(participant) {
  220. // Only the local participant is not identified with an id-conference pair.
  221. if (participant.local) {
  222. return {
  223. type: PARTICIPANT_JOINED,
  224. participant
  225. };
  226. }
  227. // In other words, a remote participant is identified with an id-conference
  228. // pair.
  229. const { conference } = participant;
  230. if (!conference) {
  231. throw Error(
  232. 'A remote participant must be associated with a JitsiConference!');
  233. }
  234. return (dispatch, getState) => {
  235. // A remote participant is only expected to join in a joined or joining
  236. // conference. The following check is really necessary because a
  237. // JitsiConference may have moved into leaving but may still manage to
  238. // sneak a PARTICIPANT_JOINED in if its leave is delayed for any purpose
  239. // (which is not outragous given that leaving involves network
  240. // requests.)
  241. const stateFeaturesBaseConference
  242. = getState()['features/base/conference'];
  243. if (conference === stateFeaturesBaseConference.conference
  244. || conference === stateFeaturesBaseConference.joining) {
  245. return dispatch({
  246. type: PARTICIPANT_JOINED,
  247. participant
  248. });
  249. }
  250. };
  251. }
  252. /**
  253. * Action to signal that a hidden participant has joined the conference.
  254. *
  255. * @param {string} id - The id of the participant.
  256. * @param {string} displayName - The display name, or undefined when
  257. * unknown.
  258. * @returns {{
  259. * type: HIDDEN_PARTICIPANT_JOINED,
  260. * displayName: string,
  261. * id: string
  262. * }}
  263. */
  264. export function hiddenParticipantJoined(id, displayName) {
  265. return {
  266. type: HIDDEN_PARTICIPANT_JOINED,
  267. id,
  268. displayName
  269. };
  270. }
  271. /**
  272. * Action to signal that a hidden participant has left the conference.
  273. *
  274. * @param {string} id - The id of the participant.
  275. * @returns {{
  276. * type: HIDDEN_PARTICIPANT_LEFT,
  277. * id: string
  278. * }}
  279. */
  280. export function hiddenParticipantLeft(id) {
  281. return {
  282. type: HIDDEN_PARTICIPANT_LEFT,
  283. id
  284. };
  285. }
  286. /**
  287. * Action to signal that a participant has left.
  288. *
  289. * @param {string} id - Participant's ID.
  290. * @param {JitsiConference} conference - The {@code JitsiConference} associated
  291. * with the participant identified by the specified {@code id}. Only the local
  292. * participant is allowed to not specify an associated {@code JitsiConference}
  293. * instance.
  294. * @returns {{
  295. * type: PARTICIPANT_LEFT,
  296. * participant: {
  297. * conference: JitsiConference,
  298. * id: string
  299. * }
  300. * }}
  301. */
  302. export function participantLeft(id, conference) {
  303. return {
  304. type: PARTICIPANT_LEFT,
  305. participant: {
  306. conference,
  307. id
  308. }
  309. };
  310. }
  311. /**
  312. * Action to signal that a participant's presence status has changed.
  313. *
  314. * @param {string} id - Participant's ID.
  315. * @param {string} presence - Participant's new presence status.
  316. * @returns {{
  317. * type: PARTICIPANT_UPDATED,
  318. * participant: {
  319. * id: string,
  320. * presence: string
  321. * }
  322. * }}
  323. */
  324. export function participantPresenceChanged(id, presence) {
  325. return participantUpdated({
  326. id,
  327. presence
  328. });
  329. }
  330. /**
  331. * Action to signal that a participant's role has changed.
  332. *
  333. * @param {string} id - Participant's ID.
  334. * @param {PARTICIPANT_ROLE} role - Participant's new role.
  335. * @returns {{
  336. * type: PARTICIPANT_UPDATED,
  337. * participant: {
  338. * id: string,
  339. * role: PARTICIPANT_ROLE
  340. * }
  341. * }}
  342. */
  343. export function participantRoleChanged(id, role) {
  344. return participantUpdated({
  345. id,
  346. role
  347. });
  348. }
  349. /**
  350. * Action to signal that some of participant properties has been changed.
  351. *
  352. * @param {Participant} participant={} - Information about participant. To
  353. * identify the participant the object should contain either property id with
  354. * value the id of the participant or property local with value true (if the
  355. * local participant hasn't joined the conference yet).
  356. * @returns {{
  357. * type: PARTICIPANT_UPDATED,
  358. * participant: Participant
  359. * }}
  360. */
  361. export function participantUpdated(participant = {}) {
  362. const participantToUpdate = {
  363. ...participant
  364. };
  365. if (participant.name) {
  366. participantToUpdate.name = getNormalizedDisplayName(participant.name);
  367. }
  368. return {
  369. type: PARTICIPANT_UPDATED,
  370. participant: participantToUpdate
  371. };
  372. }
  373. /**
  374. * Action to signal that a participant has muted us.
  375. *
  376. * @param {JitsiParticipant} participant - Information about participant.
  377. * @returns {Promise}
  378. */
  379. export function participantMutedUs(participant) {
  380. return (dispatch, getState) => {
  381. if (!participant) {
  382. return;
  383. }
  384. dispatch(showNotification({
  385. descriptionKey: 'notify.mutedRemotelyDescription',
  386. titleKey: 'notify.mutedRemotelyTitle',
  387. titleArguments: {
  388. participantDisplayName:
  389. getParticipantDisplayName(getState, participant.getId())
  390. }
  391. }));
  392. };
  393. }
  394. /**
  395. * Action to signal that a participant had been kicked.
  396. *
  397. * @param {JitsiParticipant} kicker - Information about participant performing the kick.
  398. * @param {JitsiParticipant} kicked - Information about participant that was kicked.
  399. * @returns {Promise}
  400. */
  401. export function participantKicked(kicker, kicked) {
  402. return (dispatch, getState) => {
  403. dispatch({
  404. type: PARTICIPANT_KICKED,
  405. kicked: kicked.getId(),
  406. kicker: kicker.getId()
  407. });
  408. dispatch(showNotification({
  409. titleArguments: {
  410. kicked:
  411. getParticipantDisplayName(getState, kicked.getId()),
  412. kicker:
  413. getParticipantDisplayName(getState, kicker.getId())
  414. },
  415. titleKey: 'notify.kickParticipant'
  416. }, NOTIFICATION_TIMEOUT * 2)); // leave more time for this
  417. };
  418. }
  419. /**
  420. * Create an action which pins a conference participant.
  421. *
  422. * @param {string|null} id - The ID of the conference participant to pin or null
  423. * if none of the conference's participants are to be pinned.
  424. * @returns {{
  425. * type: PIN_PARTICIPANT,
  426. * participant: {
  427. * id: string
  428. * }
  429. * }}
  430. */
  431. export function pinParticipant(id) {
  432. return {
  433. type: PIN_PARTICIPANT,
  434. participant: {
  435. id
  436. }
  437. };
  438. }
  439. /**
  440. * Creates an action which notifies the app that the loadable URL of the avatar of a participant got updated.
  441. *
  442. * @param {string} participantId - The ID of the participant.
  443. * @param {string} url - The new URL.
  444. * @returns {{
  445. * type: SET_LOADABLE_AVATAR_URL,
  446. * participant: {
  447. * id: string,
  448. * loadableAvatarUrl: string
  449. * }
  450. * }}
  451. */
  452. export function setLoadableAvatarUrl(participantId, url) {
  453. return {
  454. type: SET_LOADABLE_AVATAR_URL,
  455. participant: {
  456. id: participantId,
  457. loadableAvatarUrl: url
  458. }
  459. };
  460. }