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.any.js 12KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413
  1. // @flow
  2. import { type Dispatch } from 'redux';
  3. import {
  4. conferenceWillJoin,
  5. getCurrentConference,
  6. sendLocalParticipant,
  7. setPassword
  8. } from '../base/conference';
  9. import { getLocalParticipant } from '../base/participants';
  10. import { onLobbyChatInitialized, removeLobbyChatParticipant, sendMessage } from '../chat/actions.any';
  11. import { LOBBY_CHAT_MESSAGE } from '../chat/constants';
  12. import { handleLobbyMessageReceived } from '../chat/middleware';
  13. import { hideNotification, LOBBY_NOTIFICATION_ID } from '../notifications';
  14. import { showNotification } from '../notifications/actions';
  15. import {
  16. KNOCKING_PARTICIPANT_ARRIVED_OR_UPDATED,
  17. KNOCKING_PARTICIPANT_LEFT,
  18. SET_KNOCKING_STATE,
  19. SET_LOBBY_MODE_ENABLED,
  20. SET_PASSWORD_JOIN_FAILED,
  21. SET_LOBBY_VISIBILITY,
  22. SET_LOBBY_PARTICIPANT_CHAT_STATE,
  23. REMOVE_LOBBY_CHAT_WITH_MODERATOR
  24. } from './actionTypes';
  25. import { LOBBY_CHAT_INITIALIZED, MODERATOR_IN_CHAT_WITH_LEFT } from './constants';
  26. import { getKnockingParticipants, getLobbyEnabled } from './functions';
  27. /**
  28. * Tries to join with a preset password.
  29. *
  30. * @param {string} password - The password to join with.
  31. * @returns {Function}
  32. */
  33. export function joinWithPassword(password: string) {
  34. return async (dispatch: Dispatch<any>, getState: Function) => {
  35. const conference = getCurrentConference(getState);
  36. dispatch(setPassword(conference, conference.join, password));
  37. };
  38. }
  39. /**
  40. * Action to be dispatched when a knocking poarticipant leaves before any response.
  41. *
  42. * @param {string} id - The ID of the participant.
  43. * @returns {{
  44. * id: string,
  45. * type: KNOCKING_PARTICIPANT_LEFT
  46. * }}
  47. */
  48. export function knockingParticipantLeft(id: string) {
  49. return {
  50. id,
  51. type: KNOCKING_PARTICIPANT_LEFT
  52. };
  53. }
  54. /**
  55. * Action to be executed when a participant starts knocking or an already knocking participant gets updated.
  56. *
  57. * @param {Object} participant - The knocking participant.
  58. * @returns {{
  59. * participant: Object,
  60. * type: KNOCKING_PARTICIPANT_ARRIVED_OR_UPDATED
  61. * }}
  62. */
  63. export function participantIsKnockingOrUpdated(participant: Object) {
  64. return {
  65. participant,
  66. type: KNOCKING_PARTICIPANT_ARRIVED_OR_UPDATED
  67. };
  68. }
  69. /**
  70. * Handles a knocking participant and dismisses the notification.
  71. *
  72. * @param {string} id - The id of the knocking participant.
  73. * @param {boolean} approved - True if the participant is approved, false otherwise.
  74. * @returns {Function}
  75. */
  76. export function answerKnockingParticipant(id: string, approved: boolean) {
  77. return async (dispatch: Dispatch<any>) => {
  78. dispatch(setKnockingParticipantApproval(id, approved));
  79. dispatch(hideNotification(LOBBY_NOTIFICATION_ID));
  80. };
  81. }
  82. /**
  83. * Approves (lets in) or rejects a knocking participant.
  84. *
  85. * @param {string} id - The id of the knocking participant.
  86. * @param {boolean} approved - True if the participant is approved, false otherwise.
  87. * @returns {Function}
  88. */
  89. export function setKnockingParticipantApproval(id: string, approved: boolean) {
  90. return async (dispatch: Dispatch<any>, getState: Function) => {
  91. const conference = getCurrentConference(getState);
  92. if (conference) {
  93. if (approved) {
  94. conference.lobbyApproveAccess(id);
  95. } else {
  96. conference.lobbyDenyAccess(id);
  97. }
  98. }
  99. };
  100. }
  101. /**
  102. * Action used to admit multiple participants in the conference.
  103. *
  104. * @param {Array<Object>} participants - A list of knocking participants.
  105. * @returns {void}
  106. */
  107. export function admitMultiple(participants: Array<Object>) {
  108. return (dispatch: Function, getState: Function) => {
  109. const conference = getCurrentConference(getState);
  110. participants.forEach(p => {
  111. conference.lobbyApproveAccess(p.id);
  112. });
  113. };
  114. }
  115. /**
  116. * Approves the request of a knocking participant to join the meeting.
  117. *
  118. * @param {string} id - The id of the knocking participant.
  119. * @returns {Function}
  120. */
  121. export function approveKnockingParticipant(id: string) {
  122. return (dispatch: Dispatch<any>, getState: Function) => {
  123. const conference = getCurrentConference(getState);
  124. conference && conference.lobbyApproveAccess(id);
  125. };
  126. }
  127. /**
  128. * Denies the request of a knocking participant to join the meeting.
  129. *
  130. * @param {string} id - The id of the knocking participant.
  131. * @returns {Function}
  132. */
  133. export function rejectKnockingParticipant(id: string) {
  134. return (dispatch: Dispatch<any>, getState: Function) => {
  135. const conference = getCurrentConference(getState);
  136. conference && conference.lobbyDenyAccess(id);
  137. };
  138. }
  139. /**
  140. * Action to set the knocking state of the participant.
  141. *
  142. * @param {boolean} knocking - The new state.
  143. * @returns {{
  144. * state: boolean,
  145. * type: SET_KNOCKING_STATE
  146. * }}
  147. */
  148. export function setKnockingState(knocking: boolean) {
  149. return {
  150. knocking,
  151. type: SET_KNOCKING_STATE
  152. };
  153. }
  154. /**
  155. * Action to set the new state of the lobby mode.
  156. *
  157. * @param {boolean} enabled - The new state to set.
  158. * @returns {{
  159. * enabled: boolean,
  160. * type: SET_LOBBY_MODE_ENABLED
  161. * }}
  162. */
  163. export function setLobbyModeEnabled(enabled: boolean) {
  164. return {
  165. enabled,
  166. type: SET_LOBBY_MODE_ENABLED
  167. };
  168. }
  169. /**
  170. * Action to be dispatched when we failed to join with a password.
  171. *
  172. * @param {boolean} failed - True of recent password join failed.
  173. * @returns {{
  174. * failed: boolean,
  175. * type: SET_PASSWORD_JOIN_FAILED
  176. * }}
  177. */
  178. export function setPasswordJoinFailed(failed: boolean) {
  179. return {
  180. failed,
  181. type: SET_PASSWORD_JOIN_FAILED
  182. };
  183. }
  184. /**
  185. * Starts knocking and waiting for approval.
  186. *
  187. * @returns {Function}
  188. */
  189. export function startKnocking() {
  190. return async (dispatch: Dispatch<any>, getState: Function) => {
  191. const state = getState();
  192. const { membersOnly } = state['features/base/conference'];
  193. const localParticipant = getLocalParticipant(state);
  194. dispatch(conferenceWillJoin(membersOnly));
  195. // We need to update the conference object with the current display name, if approved
  196. // we want to send that display name, it was not updated in case when pre-join is disabled
  197. sendLocalParticipant(state, membersOnly);
  198. membersOnly.joinLobby(localParticipant.name, localParticipant.email);
  199. dispatch(setLobbyMessageListener());
  200. dispatch(setKnockingState(true));
  201. };
  202. }
  203. /**
  204. * Action to toggle lobby mode on or off.
  205. *
  206. * @param {boolean} enabled - The desired (new) state of the lobby mode.
  207. * @returns {Function}
  208. */
  209. export function toggleLobbyMode(enabled: boolean) {
  210. return async (dispatch: Dispatch<any>, getState: Function) => {
  211. const conference = getCurrentConference(getState);
  212. if (enabled) {
  213. conference.enableLobby();
  214. } else {
  215. conference.disableLobby();
  216. }
  217. };
  218. }
  219. /**
  220. * Action to open the lobby screen.
  221. *
  222. * @returns {openDialog}
  223. */
  224. export function openLobbyScreen() {
  225. return {
  226. type: SET_LOBBY_VISIBILITY,
  227. visible: true
  228. };
  229. }
  230. /**
  231. * Action to hide the lobby screen.
  232. *
  233. * @returns {hideDialog}
  234. */
  235. export function hideLobbyScreen() {
  236. return {
  237. type: SET_LOBBY_VISIBILITY,
  238. visible: false
  239. };
  240. }
  241. /**
  242. * Action to handle chat initialized in the lobby room.
  243. *
  244. * @param {Object} payload - The payload received,
  245. * contains the information about the two participants
  246. * that will chat with each other in the lobby room.
  247. *
  248. * @returns {Promise<void>}
  249. */
  250. export function handleLobbyChatInitialized(payload: Object) {
  251. return async (dispatch: Dispatch<any>, getState: Function) => {
  252. const state = getState();
  253. const conference = getCurrentConference(state);
  254. const id = conference.myLobbyUserId();
  255. dispatch({
  256. type: SET_LOBBY_PARTICIPANT_CHAT_STATE,
  257. participant: payload.attendee,
  258. moderator: payload.moderator
  259. });
  260. dispatch(onLobbyChatInitialized(payload));
  261. const attendeeIsKnocking = getKnockingParticipants(state).some(p => p.id === payload.attendee.id);
  262. if (attendeeIsKnocking && conference.getRole() === 'moderator' && payload.moderator.id !== id) {
  263. dispatch(showNotification({
  264. titleKey: 'lobby.lobbyChatStartedNotification',
  265. titleArguments: {
  266. moderator: payload.moderator.name,
  267. attendee: payload.attendee.name
  268. }
  269. }));
  270. }
  271. };
  272. }
  273. /**
  274. * Action to send message to the moderator.
  275. *
  276. * @param {string} message - The message to be sent.
  277. *
  278. * @returns {Promise<void>}
  279. */
  280. export function onSendMessage(message: string) {
  281. return async (dispatch: Dispatch<any>) => {
  282. dispatch(sendMessage(message));
  283. };
  284. }
  285. /**
  286. * Action to send lobby message to every participant. Only allowed for moderators.
  287. *
  288. * @param {Object} message - The message to be sent.
  289. *
  290. * @returns {Promise<void>}
  291. */
  292. export function sendLobbyChatMessage(message: Object) {
  293. return async (dispatch: Dispatch<any>, getState: Function) => {
  294. const conference = getCurrentConference(getState);
  295. conference.sendLobbyMessage(message);
  296. };
  297. }
  298. /**
  299. * Sets lobby listeners if lobby has been enabled.
  300. *
  301. * @returns {Function}
  302. */
  303. export function maybeSetLobbyChatMessageListener() {
  304. return async (dispatch: Dispatch<any>, getState: Function) => {
  305. const state = getState();
  306. const lobbyEnabled = getLobbyEnabled(state);
  307. if (lobbyEnabled) {
  308. dispatch(setLobbyMessageListener());
  309. }
  310. };
  311. }
  312. /**
  313. * Action to handle the event when a moderator leaves during lobby chat.
  314. *
  315. * @param {string} participantId - The participant id of the moderator who left.
  316. * @returns {Function}
  317. */
  318. export function updateLobbyParticipantOnLeave(participantId: string) {
  319. return async (dispatch: Dispatch<any>, getState: Function) => {
  320. const state = getState();
  321. const { knocking, knockingParticipants } = state['features/lobby'];
  322. const { lobbyMessageRecipient } = state['features/chat'];
  323. const { conference } = state['features/base/conference'];
  324. if (knocking && lobbyMessageRecipient && lobbyMessageRecipient.id === participantId) {
  325. return dispatch(removeLobbyChatParticipant(true));
  326. }
  327. if (!knocking) {
  328. // inform knocking participant when their moderator leaves
  329. const participantToNotify = knockingParticipants.find(p => p.chattingWithModerator === participantId);
  330. if (participantToNotify) {
  331. conference.sendLobbyMessage({
  332. type: MODERATOR_IN_CHAT_WITH_LEFT,
  333. moderatorId: participantToNotify.chattingWithModerator
  334. }, participantToNotify.id);
  335. }
  336. dispatch({
  337. type: REMOVE_LOBBY_CHAT_WITH_MODERATOR,
  338. moderatorId: participantId
  339. });
  340. }
  341. };
  342. }
  343. /**
  344. * Handles all messages received in the lobby room.
  345. *
  346. * @returns {Function}
  347. */
  348. export function setLobbyMessageListener() {
  349. return async (dispatch: Dispatch<any>, getState: Function) => {
  350. const state = getState();
  351. const conference = getCurrentConference(state);
  352. const { enableLobbyChat = true } = state['features/base/config'];
  353. if (!enableLobbyChat) {
  354. return;
  355. }
  356. conference.addLobbyMessageListener((message: Object, participantId: string) => {
  357. if (message.type === LOBBY_CHAT_MESSAGE) {
  358. return dispatch(handleLobbyMessageReceived(message.message, participantId));
  359. }
  360. if (message.type === LOBBY_CHAT_INITIALIZED) {
  361. return dispatch(handleLobbyChatInitialized(message));
  362. }
  363. if (message.type === MODERATOR_IN_CHAT_WITH_LEFT) {
  364. return dispatch(updateLobbyParticipantOnLeave(message.moderatorId));
  365. }
  366. });
  367. };
  368. }