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

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330
  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. try {
  124. await sendMeetingHighlight(getState());
  125. dispatch(showNotification({
  126. descriptionKey: 'recording.highlightMomentSucessDescription',
  127. titleKey: 'recording.highlightMomentSuccess'
  128. }));
  129. } catch (err) {
  130. logger.error('Could not highlight meeting moment', err);
  131. }
  132. dispatch(setHighlightMomentButtonState(false));
  133. };
  134. }
  135. /**
  136. * Signals that the recording error notification should be shown.
  137. *
  138. * @param {Object} props - The Props needed to render the notification.
  139. * @returns {showErrorNotification}
  140. */
  141. export function showRecordingError(props: Object) {
  142. return showErrorNotification(props, NOTIFICATION_TIMEOUT_TYPE.LONG);
  143. }
  144. /**
  145. * Signals that the recording warning notification should be shown.
  146. *
  147. * @param {Object} props - The Props needed to render the notification.
  148. * @returns {showWarningNotification}
  149. */
  150. export function showRecordingWarning(props: Object) {
  151. return showWarningNotification(props);
  152. }
  153. /**
  154. * Signals that the stopped recording notification should be shown on the
  155. * screen for a given period.
  156. *
  157. * @param {string} streamType - The type of the stream ({@code file} or
  158. * {@code stream}).
  159. * @param {string?} participantName - The participant name stopping the recording.
  160. * @returns {showNotification}
  161. */
  162. export function showStoppedRecordingNotification(streamType: string, participantName?: string) {
  163. const isLiveStreaming
  164. = streamType === JitsiMeetJS.constants.recording.mode.STREAM;
  165. const descriptionArguments = { name: participantName };
  166. const dialogProps = isLiveStreaming ? {
  167. descriptionKey: participantName ? 'liveStreaming.offBy' : 'liveStreaming.off',
  168. descriptionArguments,
  169. titleKey: 'dialog.liveStreaming'
  170. } : {
  171. descriptionKey: participantName ? 'recording.offBy' : 'recording.off',
  172. descriptionArguments,
  173. titleKey: 'dialog.recording'
  174. };
  175. return showNotification(dialogProps, NOTIFICATION_TIMEOUT_TYPE.SHORT);
  176. }
  177. /**
  178. * Signals that a started recording notification should be shown on the
  179. * screen for a given period.
  180. *
  181. * @param {string} mode - The type of the recording: Stream of File.
  182. * @param {string | Object } initiator - The participant who started recording.
  183. * @param {string} sessionId - The recording session id.
  184. * @returns {Function}
  185. */
  186. export function showStartedRecordingNotification(
  187. mode: string,
  188. initiator: Object | string,
  189. sessionId: string) {
  190. return async (dispatch: Function, getState: Function) => {
  191. const state = getState();
  192. const initiatorId = getResourceId(initiator);
  193. const participantName = getParticipantDisplayName(state, initiatorId);
  194. let dialogProps = {
  195. descriptionKey: participantName ? 'liveStreaming.onBy' : 'liveStreaming.on',
  196. descriptionArguments: { name: participantName },
  197. titleKey: 'dialog.liveStreaming'
  198. };
  199. if (mode !== JitsiMeetJS.constants.recording.mode.STREAM) {
  200. const recordingSharingUrl = getRecordingSharingUrl(state);
  201. const iAmRecordingInitiator = getLocalParticipant(state).id === initiatorId;
  202. dialogProps = {
  203. customActionHandler: undefined,
  204. customActionNameKey: undefined,
  205. descriptionKey: participantName ? 'recording.onBy' : 'recording.on',
  206. descriptionArguments: { name: participantName },
  207. titleKey: 'dialog.recording'
  208. };
  209. // fetch the recording link from the server for recording initiators in jaas meetings
  210. if (recordingSharingUrl
  211. && isVpaasMeeting(state)
  212. && iAmRecordingInitiator
  213. && !isSavingRecordingOnDropbox(state)) {
  214. const region = getMeetingRegion(state);
  215. const tenant = getVpaasTenant(state);
  216. try {
  217. const response = await getRecordingLink(recordingSharingUrl, sessionId, region, tenant);
  218. const { url: link, urlExpirationTimeMillis: ttl } = response;
  219. if (typeof APP === 'object') {
  220. APP.API.notifyRecordingLinkAvailable(link, ttl);
  221. }
  222. // add the option to copy recording link
  223. dialogProps.customActionNameKey = [ 'recording.copyLink' ];
  224. dialogProps.customActionHandler = [ () => copyText(link) ];
  225. dialogProps.titleKey = 'recording.on';
  226. dialogProps.descriptionKey = 'recording.linkGenerated';
  227. } catch (err) {
  228. dispatch(showErrorNotification({
  229. titleKey: 'recording.errorFetchingLink'
  230. }, NOTIFICATION_TIMEOUT_TYPE.MEDIUM));
  231. return logger.error('Could not fetch recording link', err);
  232. }
  233. }
  234. }
  235. dispatch(showNotification(dialogProps, NOTIFICATION_TIMEOUT_TYPE.SHORT));
  236. };
  237. }
  238. /**
  239. * Updates the known state for a given recording session.
  240. *
  241. * @param {Object} session - The new state to merge with the existing state in
  242. * redux.
  243. * @returns {{
  244. * type: RECORDING_SESSION_UPDATED,
  245. * sessionData: Object
  246. * }}
  247. */
  248. export function updateRecordingSessionData(session: Object) {
  249. const status = session.getStatus();
  250. const timestamp
  251. = status === JitsiRecordingConstants.status.ON
  252. ? Date.now() / 1000
  253. : undefined;
  254. return {
  255. type: RECORDING_SESSION_UPDATED,
  256. sessionData: {
  257. error: session.getError(),
  258. id: session.getID(),
  259. initiator: session.getInitiator(),
  260. liveStreamViewURL: session.getLiveStreamViewURL(),
  261. mode: session.getMode(),
  262. status,
  263. terminator: session.getTerminator(),
  264. timestamp
  265. }
  266. };
  267. }
  268. /**
  269. * Sets the selected recording service.
  270. *
  271. * @param {string} selectedRecordingService - The new selected recording service.
  272. * @returns {Object}
  273. */
  274. export function setSelectedRecordingService(selectedRecordingService: string) {
  275. return {
  276. type: SET_SELECTED_RECORDING_SERVICE,
  277. selectedRecordingService
  278. };
  279. }
  280. /**
  281. * Sets UID of the the pending streaming notification to use it when hinding
  282. * the notification is necessary, or unsets it when undefined (or no param) is
  283. * passed.
  284. *
  285. * @param {?number} uid - The UID of the notification.
  286. * @param {string} streamType - The type of the stream ({@code file} or
  287. * {@code stream}).
  288. * @returns {{
  289. * type: SET_PENDING_RECORDING_NOTIFICATION_UID,
  290. * streamType: string,
  291. * uid: number
  292. * }}
  293. */
  294. function _setPendingRecordingNotificationUid(uid: ?number, streamType: string) {
  295. return {
  296. type: SET_PENDING_RECORDING_NOTIFICATION_UID,
  297. streamType,
  298. uid
  299. };
  300. }