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

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329
  1. // @flow
  2. import { getMeetingRegion, getRecordingSharingUrl } from '../base/config';
  3. import JitsiMeetJS, { JitsiRecordingConstants } from '../base/lib-jitsi-meet';
  4. import { getLocalParticipant, getParticipantDisplayName } from '../base/participants';
  5. import { copyText } from '../base/util/helpers';
  6. import { getVpaasTenant, isVpaasMeeting } from '../jaas/functions';
  7. import {
  8. NOTIFICATION_TIMEOUT_TYPE,
  9. hideNotification,
  10. showErrorNotification,
  11. showNotification,
  12. showWarningNotification
  13. } from '../notifications';
  14. import {
  15. CLEAR_RECORDING_SESSIONS,
  16. RECORDING_SESSION_UPDATED,
  17. SET_MEETING_HIGHLIGHT_BUTTON_STATE,
  18. SET_PENDING_RECORDING_NOTIFICATION_UID,
  19. SET_SELECTED_RECORDING_SERVICE,
  20. SET_STREAM_KEY
  21. } from './actionTypes';
  22. import { getRecordingLink, getResourceId, isSavingRecordingOnDropbox, sendMeetingHighlight } from './functions';
  23. import logger from './logger';
  24. declare var APP: Object;
  25. /**
  26. * Clears the data of every recording sessions.
  27. *
  28. * @returns {{
  29. * type: CLEAR_RECORDING_SESSIONS
  30. * }}
  31. */
  32. export function clearRecordingSessions() {
  33. return {
  34. type: CLEAR_RECORDING_SESSIONS
  35. };
  36. }
  37. /**
  38. * Sets the meeting highlight button disable state.
  39. *
  40. * @param {boolean} disabled - The disabled state value.
  41. * @returns {{
  42. * type: CLEAR_RECORDING_SESSIONS
  43. * }}
  44. */
  45. export function setHighlightMomentButtonState(disabled: boolean) {
  46. return {
  47. type: SET_MEETING_HIGHLIGHT_BUTTON_STATE,
  48. disabled
  49. };
  50. }
  51. /**
  52. * Signals that the pending recording notification should be removed from the
  53. * screen.
  54. *
  55. * @param {string} streamType - The type of the stream ({@code 'file'} or
  56. * {@code 'stream'}).
  57. * @returns {Function}
  58. */
  59. export function hidePendingRecordingNotification(streamType: string) {
  60. return (dispatch: Function, getState: Function) => {
  61. const { pendingNotificationUids } = getState()['features/recording'];
  62. const pendingNotificationUid = pendingNotificationUids[streamType];
  63. if (pendingNotificationUid) {
  64. dispatch(hideNotification(pendingNotificationUid));
  65. dispatch(
  66. _setPendingRecordingNotificationUid(
  67. undefined, streamType));
  68. }
  69. };
  70. }
  71. /**
  72. * Sets the stream key last used by the user for later reuse.
  73. *
  74. * @param {string} streamKey - The stream key to set.
  75. * @returns {{
  76. * type: SET_STREAM_KEY,
  77. * streamKey: string
  78. * }}
  79. */
  80. export function setLiveStreamKey(streamKey: string) {
  81. return {
  82. type: SET_STREAM_KEY,
  83. streamKey
  84. };
  85. }
  86. /**
  87. * Signals that the pending recording notification should be shown on the
  88. * screen.
  89. *
  90. * @param {string} streamType - The type of the stream ({@code file} or
  91. * {@code stream}).
  92. * @returns {Function}
  93. */
  94. export function showPendingRecordingNotification(streamType: string) {
  95. return async (dispatch: Function) => {
  96. const isLiveStreaming
  97. = streamType === JitsiMeetJS.constants.recording.mode.STREAM;
  98. const dialogProps = isLiveStreaming ? {
  99. descriptionKey: 'liveStreaming.pending',
  100. titleKey: 'dialog.liveStreaming'
  101. } : {
  102. descriptionKey: 'recording.pending',
  103. titleKey: 'dialog.recording'
  104. };
  105. const notification = await dispatch(showNotification({
  106. ...dialogProps
  107. }, NOTIFICATION_TIMEOUT_TYPE.MEDIUM));
  108. if (notification) {
  109. dispatch(_setPendingRecordingNotificationUid(notification.uid, streamType));
  110. }
  111. };
  112. }
  113. /**
  114. * Highlights a meeting moment.
  115. *
  116. * {@code stream}).
  117. *
  118. * @returns {Function}
  119. */
  120. export function highlightMeetingMoment() {
  121. return async (dispatch: Function, getState: Function) => {
  122. dispatch(setHighlightMomentButtonState(true));
  123. const success = await sendMeetingHighlight(getState());
  124. if (success) {
  125. dispatch(showNotification({
  126. descriptionKey: 'recording.highlightMomentSucessDescription',
  127. titleKey: 'recording.highlightMomentSuccess'
  128. }));
  129. }
  130. dispatch(setHighlightMomentButtonState(false));
  131. };
  132. }
  133. /**
  134. * Signals that the recording error notification should be shown.
  135. *
  136. * @param {Object} props - The Props needed to render the notification.
  137. * @returns {showErrorNotification}
  138. */
  139. export function showRecordingError(props: Object) {
  140. return showErrorNotification(props, NOTIFICATION_TIMEOUT_TYPE.LONG);
  141. }
  142. /**
  143. * Signals that the recording warning notification should be shown.
  144. *
  145. * @param {Object} props - The Props needed to render the notification.
  146. * @returns {showWarningNotification}
  147. */
  148. export function showRecordingWarning(props: Object) {
  149. return showWarningNotification(props);
  150. }
  151. /**
  152. * Signals that the stopped recording notification should be shown on the
  153. * screen for a given period.
  154. *
  155. * @param {string} streamType - The type of the stream ({@code file} or
  156. * {@code stream}).
  157. * @param {string?} participantName - The participant name stopping the recording.
  158. * @returns {showNotification}
  159. */
  160. export function showStoppedRecordingNotification(streamType: string, participantName?: string) {
  161. const isLiveStreaming
  162. = streamType === JitsiMeetJS.constants.recording.mode.STREAM;
  163. const descriptionArguments = { name: participantName };
  164. const dialogProps = isLiveStreaming ? {
  165. descriptionKey: participantName ? 'liveStreaming.offBy' : 'liveStreaming.off',
  166. descriptionArguments,
  167. titleKey: 'dialog.liveStreaming'
  168. } : {
  169. descriptionKey: participantName ? 'recording.offBy' : 'recording.off',
  170. descriptionArguments,
  171. titleKey: 'dialog.recording'
  172. };
  173. return showNotification(dialogProps, NOTIFICATION_TIMEOUT_TYPE.SHORT);
  174. }
  175. /**
  176. * Signals that a started recording notification should be shown on the
  177. * screen for a given period.
  178. *
  179. * @param {string} mode - The type of the recording: Stream of File.
  180. * @param {string | Object } initiator - The participant who started recording.
  181. * @param {string} sessionId - The recording session id.
  182. * @returns {Function}
  183. */
  184. export function showStartedRecordingNotification(
  185. mode: string,
  186. initiator: Object | string,
  187. sessionId: string) {
  188. return async (dispatch: Function, getState: Function) => {
  189. const state = getState();
  190. const initiatorId = getResourceId(initiator);
  191. const participantName = getParticipantDisplayName(state, initiatorId);
  192. let dialogProps = {
  193. descriptionKey: participantName ? 'liveStreaming.onBy' : 'liveStreaming.on',
  194. descriptionArguments: { name: participantName },
  195. titleKey: 'dialog.liveStreaming'
  196. };
  197. if (mode !== JitsiMeetJS.constants.recording.mode.STREAM) {
  198. const recordingSharingUrl = getRecordingSharingUrl(state);
  199. const iAmRecordingInitiator = getLocalParticipant(state).id === initiatorId;
  200. dialogProps = {
  201. customActionHandler: undefined,
  202. customActionNameKey: undefined,
  203. descriptionKey: participantName ? 'recording.onBy' : 'recording.on',
  204. descriptionArguments: { name: participantName },
  205. titleKey: 'dialog.recording'
  206. };
  207. // fetch the recording link from the server for recording initiators in jaas meetings
  208. if (recordingSharingUrl
  209. && isVpaasMeeting(state)
  210. && iAmRecordingInitiator
  211. && !isSavingRecordingOnDropbox(state)) {
  212. const region = getMeetingRegion(state);
  213. const tenant = getVpaasTenant(state);
  214. try {
  215. const response = await getRecordingLink(recordingSharingUrl, sessionId, region, tenant);
  216. const { url: link, urlExpirationTimeMillis: ttl } = response;
  217. if (typeof APP === 'object') {
  218. APP.API.notifyRecordingLinkAvailable(link, ttl);
  219. }
  220. // add the option to copy recording link
  221. dialogProps.customActionNameKey = [ 'recording.copyLink' ];
  222. dialogProps.customActionHandler = [ () => copyText(link) ];
  223. dialogProps.titleKey = 'recording.on';
  224. dialogProps.descriptionKey = 'recording.linkGenerated';
  225. } catch (err) {
  226. dispatch(showErrorNotification({
  227. titleKey: 'recording.errorFetchingLink'
  228. }, NOTIFICATION_TIMEOUT_TYPE.MEDIUM));
  229. return logger.error('Could not fetch recording link', err);
  230. }
  231. }
  232. }
  233. dispatch(showNotification(dialogProps, NOTIFICATION_TIMEOUT_TYPE.SHORT));
  234. };
  235. }
  236. /**
  237. * Updates the known state for a given recording session.
  238. *
  239. * @param {Object} session - The new state to merge with the existing state in
  240. * redux.
  241. * @returns {{
  242. * type: RECORDING_SESSION_UPDATED,
  243. * sessionData: Object
  244. * }}
  245. */
  246. export function updateRecordingSessionData(session: Object) {
  247. const status = session.getStatus();
  248. const timestamp
  249. = status === JitsiRecordingConstants.status.ON
  250. ? Date.now() / 1000
  251. : undefined;
  252. return {
  253. type: RECORDING_SESSION_UPDATED,
  254. sessionData: {
  255. error: session.getError(),
  256. id: session.getID(),
  257. initiator: session.getInitiator(),
  258. liveStreamViewURL: session.getLiveStreamViewURL(),
  259. mode: session.getMode(),
  260. status,
  261. terminator: session.getTerminator(),
  262. timestamp
  263. }
  264. };
  265. }
  266. /**
  267. * Sets the selected recording service.
  268. *
  269. * @param {string} selectedRecordingService - The new selected recording service.
  270. * @returns {Object}
  271. */
  272. export function setSelectedRecordingService(selectedRecordingService: string) {
  273. return {
  274. type: SET_SELECTED_RECORDING_SERVICE,
  275. selectedRecordingService
  276. };
  277. }
  278. /**
  279. * Sets UID of the the pending streaming notification to use it when hinding
  280. * the notification is necessary, or unsets it when undefined (or no param) is
  281. * passed.
  282. *
  283. * @param {?number} uid - The UID of the notification.
  284. * @param {string} streamType - The type of the stream ({@code file} or
  285. * {@code stream}).
  286. * @returns {{
  287. * type: SET_PENDING_RECORDING_NOTIFICATION_UID,
  288. * streamType: string,
  289. * uid: number
  290. * }}
  291. */
  292. function _setPendingRecordingNotificationUid(uid: ?number, streamType: string) {
  293. return {
  294. type: SET_PENDING_RECORDING_NOTIFICATION_UID,
  295. streamType,
  296. uid
  297. };
  298. }