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.ts 12KB

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