Du kannst nicht mehr als 25 Themen auswählen Themen müssen mit entweder einem Buchstaben oder einer Ziffer beginnen. Sie können Bindestriche („-“) enthalten und bis zu 35 Zeichen lang sein.

actions.any.ts 13KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429
  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 { joinConference } from '../prejoin/actions';
  12. import {
  13. KNOCKING_PARTICIPANT_ARRIVED_OR_UPDATED,
  14. KNOCKING_PARTICIPANT_LEFT,
  15. REMOVE_LOBBY_CHAT_WITH_MODERATOR,
  16. SET_KNOCKING_STATE,
  17. SET_LOBBY_MODE_ENABLED,
  18. SET_LOBBY_PARTICIPANT_CHAT_STATE,
  19. SET_LOBBY_VISIBILITY,
  20. SET_PASSWORD_JOIN_FAILED
  21. } from './actionTypes';
  22. import { LOBBY_CHAT_INITIALIZED, MODERATOR_IN_CHAT_WITH_LEFT } from './constants';
  23. import { getKnockingParticipants, getLobbyConfig, getLobbyEnabled, isEnablingLobbyAllowed } from './functions';
  24. import logger from './logger';
  25. import { IKnockingParticipant } from './types';
  26. /**
  27. * Tries to join with a preset password.
  28. *
  29. * @param {string} password - The password to join with.
  30. * @returns {Function}
  31. */
  32. export function joinWithPassword(password: string) {
  33. return (dispatch: IStore['dispatch'], getState: IStore['getState']) => {
  34. const conference = getCurrentConference(getState);
  35. dispatch(setPassword(conference, conference?.join, password));
  36. };
  37. }
  38. /**
  39. * Action to be dispatched when a knocking poarticipant leaves before any response.
  40. *
  41. * @param {string} id - The ID of the participant.
  42. * @returns {{
  43. * id: string,
  44. * type: KNOCKING_PARTICIPANT_LEFT
  45. * }}
  46. */
  47. export function knockingParticipantLeft(id: string) {
  48. return {
  49. id,
  50. type: KNOCKING_PARTICIPANT_LEFT
  51. };
  52. }
  53. /**
  54. * Action to be executed when a participant starts knocking or an already knocking participant gets updated.
  55. *
  56. * @param {Object} participant - The knocking participant.
  57. * @returns {{
  58. * participant: Object,
  59. * type: KNOCKING_PARTICIPANT_ARRIVED_OR_UPDATED
  60. * }}
  61. */
  62. export function participantIsKnockingOrUpdated(participant: IKnockingParticipant | Object) {
  63. return {
  64. participant,
  65. type: KNOCKING_PARTICIPANT_ARRIVED_OR_UPDATED
  66. };
  67. }
  68. /**
  69. * Handles a knocking participant and dismisses the notification.
  70. *
  71. * @param {string} id - The id of the knocking participant.
  72. * @param {boolean} approved - True if the participant is approved, false otherwise.
  73. * @returns {Function}
  74. */
  75. export function answerKnockingParticipant(id: string, approved: boolean) {
  76. return (dispatch: IStore['dispatch']) => {
  77. dispatch(setKnockingParticipantApproval(id, approved));
  78. dispatch(hideNotification(LOBBY_NOTIFICATION_ID));
  79. };
  80. }
  81. /**
  82. * Approves (lets in) or rejects a knocking participant.
  83. *
  84. * @param {string} id - The id of the knocking participant.
  85. * @param {boolean} approved - True if the participant is approved, false otherwise.
  86. * @returns {Function}
  87. */
  88. export function setKnockingParticipantApproval(id: string, approved: boolean) {
  89. return async (dispatch: IStore['dispatch'], getState: IStore['getState']) => {
  90. const conference = getCurrentConference(getState);
  91. if (conference) {
  92. if (approved) {
  93. conference.lobbyApproveAccess(id);
  94. } else {
  95. conference.lobbyDenyAccess(id);
  96. }
  97. }
  98. };
  99. }
  100. /**
  101. * Action used to admit multiple participants in the conference.
  102. *
  103. * @param {Array<Object>} participants - A list of knocking participants.
  104. * @returns {void}
  105. */
  106. export function admitMultiple(participants: Array<IKnockingParticipant>) {
  107. return (dispatch: IStore['dispatch'], getState: IStore['getState']) => {
  108. const conference = getCurrentConference(getState);
  109. conference?.lobbyApproveAccess(participants.map(p => p.id));
  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 (dispatch: IStore['dispatch'], getState: IStore['getState']) => {
  188. const state = getState();
  189. const { membersOnly } = state['features/base/conference'];
  190. logger.info(`Lobby starting knocking (membersOnly = ${membersOnly})`);
  191. if (!membersOnly) {
  192. // let's hide the notification (the case with denied access and retrying)
  193. dispatch(hideNotification(LOBBY_NOTIFICATION_ID));
  194. // no membersOnly, this means we got lobby screen shown as someone
  195. // tried to join a conference that has lobby enabled without setting display name
  196. // join conference should trigger the lobby/member_only path after setting the display name
  197. // this is possible only for web, where we can join without a prejoin screen
  198. dispatch(joinConference());
  199. return;
  200. }
  201. const localParticipant = getLocalParticipant(state);
  202. dispatch(conferenceWillJoin(membersOnly));
  203. // We need to update the conference object with the current display name, if approved
  204. // we want to send that display name, it was not updated in case when pre-join is disabled
  205. sendLocalParticipant(state, membersOnly);
  206. membersOnly?.joinLobby(localParticipant?.name, localParticipant?.email);
  207. dispatch(setLobbyMessageListener());
  208. dispatch(setKnockingState(true));
  209. };
  210. }
  211. /**
  212. * Action to toggle lobby mode on or off.
  213. *
  214. * @param {boolean} enabled - The desired (new) state of the lobby mode.
  215. * @returns {Function}
  216. */
  217. export function toggleLobbyMode(enabled: boolean) {
  218. return async (dispatch: IStore['dispatch'], getState: IStore['getState']) => {
  219. const conference = getCurrentConference(getState);
  220. if (enabled) {
  221. if (isEnablingLobbyAllowed(getState())) {
  222. conference?.enableLobby();
  223. } else {
  224. logger.info('Ignoring enable lobby request because there are visitors in the call already.');
  225. }
  226. } else {
  227. conference?.disableLobby();
  228. }
  229. };
  230. }
  231. /**
  232. * Action to open the lobby screen.
  233. *
  234. * @returns {openDialog}
  235. */
  236. export function openLobbyScreen() {
  237. return {
  238. type: SET_LOBBY_VISIBILITY,
  239. visible: true
  240. };
  241. }
  242. /**
  243. * Action to hide the lobby screen.
  244. *
  245. * @returns {hideDialog}
  246. */
  247. export function hideLobbyScreen() {
  248. return {
  249. type: SET_LOBBY_VISIBILITY,
  250. visible: false
  251. };
  252. }
  253. /**
  254. * Action to handle chat initialized in the lobby room.
  255. *
  256. * @param {Object} payload - The payload received,
  257. * contains the information about the two participants
  258. * that will chat with each other in the lobby room.
  259. *
  260. * @returns {Promise<void>}
  261. */
  262. export function handleLobbyChatInitialized(payload: { attendee: IParticipant; moderator: IParticipant; }) {
  263. return (dispatch: IStore['dispatch'], getState: IStore['getState']) => {
  264. const state = getState();
  265. const conference = getCurrentConference(state);
  266. const id = conference?.myLobbyUserId();
  267. dispatch({
  268. type: SET_LOBBY_PARTICIPANT_CHAT_STATE,
  269. participant: payload.attendee,
  270. moderator: payload.moderator
  271. });
  272. dispatch(onLobbyChatInitialized(payload));
  273. const attendeeIsKnocking = getKnockingParticipants(state).some(p => p.id === payload.attendee.id);
  274. if (attendeeIsKnocking && conference?.getRole() === 'moderator' && payload.moderator.id !== id) {
  275. dispatch(showNotification({
  276. titleKey: 'lobby.lobbyChatStartedNotification',
  277. titleArguments: {
  278. moderator: payload.moderator.name ?? '',
  279. attendee: payload.attendee.name ?? ''
  280. }
  281. }));
  282. }
  283. };
  284. }
  285. /**
  286. * Action to send message to the moderator.
  287. *
  288. * @param {string} message - The message to be sent.
  289. *
  290. * @returns {Promise<void>}
  291. */
  292. export function onSendMessage(message: string) {
  293. return (dispatch: IStore['dispatch']) => {
  294. dispatch(sendMessage(message));
  295. };
  296. }
  297. /**
  298. * Action to send lobby message to every participant. Only allowed for moderators.
  299. *
  300. * @param {Object} message - The message to be sent.
  301. *
  302. * @returns {Promise<void>}
  303. */
  304. export function sendLobbyChatMessage(message: Object) {
  305. return async (dispatch: IStore['dispatch'], getState: IStore['getState']) => {
  306. const conference = getCurrentConference(getState);
  307. conference?.sendLobbyMessage(message);
  308. };
  309. }
  310. /**
  311. * Sets lobby listeners if lobby has been enabled.
  312. *
  313. * @returns {Function}
  314. */
  315. export function maybeSetLobbyChatMessageListener() {
  316. return (dispatch: IStore['dispatch'], getState: IStore['getState']) => {
  317. const state = getState();
  318. const lobbyEnabled = getLobbyEnabled(state);
  319. if (lobbyEnabled) {
  320. dispatch(setLobbyMessageListener());
  321. }
  322. };
  323. }
  324. /**
  325. * Action to handle the event when a moderator leaves during lobby chat.
  326. *
  327. * @param {string} participantId - The participant id of the moderator who left.
  328. * @returns {Function}
  329. */
  330. export function updateLobbyParticipantOnLeave(participantId: string) {
  331. return (dispatch: IStore['dispatch'], getState: IStore['getState']) => {
  332. const state = getState();
  333. const { knocking, knockingParticipants } = state['features/lobby'];
  334. const { lobbyMessageRecipient } = state['features/chat'];
  335. const { conference } = state['features/base/conference'];
  336. if (knocking && lobbyMessageRecipient && lobbyMessageRecipient.id === participantId) {
  337. return dispatch(removeLobbyChatParticipant(true));
  338. }
  339. if (!knocking) {
  340. // inform knocking participant when their moderator leaves
  341. const participantToNotify = knockingParticipants.find(p => p.chattingWithModerator === participantId);
  342. if (participantToNotify) {
  343. conference?.sendLobbyMessage({
  344. type: MODERATOR_IN_CHAT_WITH_LEFT,
  345. moderatorId: participantToNotify.chattingWithModerator
  346. }, participantToNotify.id);
  347. }
  348. dispatch({
  349. type: REMOVE_LOBBY_CHAT_WITH_MODERATOR,
  350. moderatorId: participantId
  351. });
  352. }
  353. };
  354. }
  355. /**
  356. * Handles all messages received in the lobby room.
  357. *
  358. * @returns {Function}
  359. */
  360. export function setLobbyMessageListener() {
  361. return (dispatch: IStore['dispatch'], getState: IStore['getState']) => {
  362. const state = getState();
  363. const conference = getCurrentConference(state);
  364. const { enableChat = true } = getLobbyConfig(state);
  365. if (!enableChat) {
  366. return;
  367. }
  368. conference?.addLobbyMessageListener((message: any, participantId: string) => {
  369. if (message.type === LOBBY_CHAT_MESSAGE) {
  370. return dispatch(handleLobbyMessageReceived(message.message, participantId));
  371. }
  372. if (message.type === LOBBY_CHAT_INITIALIZED) {
  373. return dispatch(handleLobbyChatInitialized(message));
  374. }
  375. if (message.type === MODERATOR_IN_CHAT_WITH_LEFT) {
  376. return dispatch(updateLobbyParticipantOnLeave(message.moderatorId));
  377. }
  378. });
  379. };
  380. }