2022-11-11 08:20:33 +00:00
|
|
|
import { IStore } from '../app/types';
|
|
|
|
import { getMeetingRegion, getRecordingSharingUrl } from '../base/config/functions';
|
2018-09-10 15:39:33 +00:00
|
|
|
import JitsiMeetJS, { JitsiRecordingConstants } from '../base/lib-jitsi-meet';
|
2022-11-11 08:20:33 +00:00
|
|
|
import { getLocalParticipant, getParticipantDisplayName } from '../base/participants/functions';
|
2022-09-27 14:37:38 +00:00
|
|
|
import { copyText } from '../base/util/copyText';
|
2021-07-20 08:58:42 +00:00
|
|
|
import { getVpaasTenant, isVpaasMeeting } from '../jaas/functions';
|
2018-06-14 09:15:36 +00:00
|
|
|
import {
|
|
|
|
hideNotification,
|
|
|
|
showErrorNotification,
|
2021-11-09 18:58:49 +00:00
|
|
|
showNotification,
|
|
|
|
showWarningNotification
|
2022-11-11 08:20:33 +00:00
|
|
|
} from '../notifications/actions';
|
|
|
|
import { NOTIFICATION_TIMEOUT_TYPE } from '../notifications/constants';
|
|
|
|
import { INotificationProps } from '../notifications/types';
|
2018-06-14 09:15:36 +00:00
|
|
|
|
|
|
|
import {
|
|
|
|
CLEAR_RECORDING_SESSIONS,
|
|
|
|
RECORDING_SESSION_UPDATED,
|
2022-03-14 08:31:08 +00:00
|
|
|
SET_MEETING_HIGHLIGHT_BUTTON_STATE,
|
2018-07-05 11:17:45 +00:00
|
|
|
SET_PENDING_RECORDING_NOTIFICATION_UID,
|
2021-06-23 08:57:11 +00:00
|
|
|
SET_SELECTED_RECORDING_SERVICE,
|
2022-06-03 11:45:27 +00:00
|
|
|
SET_STREAM_KEY,
|
|
|
|
START_LOCAL_RECORDING,
|
|
|
|
STOP_LOCAL_RECORDING
|
2018-06-14 09:15:36 +00:00
|
|
|
} from './actionTypes';
|
2022-04-08 12:24:58 +00:00
|
|
|
import {
|
|
|
|
getRecordingLink,
|
|
|
|
getResourceId,
|
|
|
|
isSavingRecordingOnDropbox,
|
|
|
|
sendMeetingHighlight
|
|
|
|
} from './functions';
|
2021-06-21 08:36:18 +00:00
|
|
|
import logger from './logger';
|
2018-06-14 09:15:36 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Clears the data of every recording sessions.
|
|
|
|
*
|
|
|
|
* @returns {{
|
|
|
|
* type: CLEAR_RECORDING_SESSIONS
|
|
|
|
* }}
|
|
|
|
*/
|
|
|
|
export function clearRecordingSessions() {
|
|
|
|
return {
|
|
|
|
type: CLEAR_RECORDING_SESSIONS
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2022-03-14 08:31:08 +00:00
|
|
|
/**
|
|
|
|
* Sets the meeting highlight button disable state.
|
|
|
|
*
|
|
|
|
* @param {boolean} disabled - The disabled state value.
|
|
|
|
* @returns {{
|
|
|
|
* type: CLEAR_RECORDING_SESSIONS
|
|
|
|
* }}
|
|
|
|
*/
|
|
|
|
export function setHighlightMomentButtonState(disabled: boolean) {
|
|
|
|
return {
|
|
|
|
type: SET_MEETING_HIGHLIGHT_BUTTON_STATE,
|
|
|
|
disabled
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2018-06-14 09:15:36 +00:00
|
|
|
/**
|
|
|
|
* Signals that the pending recording notification should be removed from the
|
|
|
|
* screen.
|
|
|
|
*
|
2018-11-08 12:25:02 +00:00
|
|
|
* @param {string} streamType - The type of the stream ({@code 'file'} or
|
|
|
|
* {@code 'stream'}).
|
2018-06-14 09:15:36 +00:00
|
|
|
* @returns {Function}
|
|
|
|
*/
|
2018-07-05 11:17:45 +00:00
|
|
|
export function hidePendingRecordingNotification(streamType: string) {
|
2022-11-11 08:20:33 +00:00
|
|
|
return (dispatch: IStore['dispatch'], getState: IStore['getState']) => {
|
2018-07-05 11:17:45 +00:00
|
|
|
const { pendingNotificationUids } = getState()['features/recording'];
|
|
|
|
const pendingNotificationUid = pendingNotificationUids[streamType];
|
2018-06-14 09:15:36 +00:00
|
|
|
|
|
|
|
if (pendingNotificationUid) {
|
|
|
|
dispatch(hideNotification(pendingNotificationUid));
|
2018-07-05 11:17:45 +00:00
|
|
|
dispatch(
|
|
|
|
_setPendingRecordingNotificationUid(
|
|
|
|
undefined, streamType));
|
2018-06-14 09:15:36 +00:00
|
|
|
}
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2018-07-05 11:17:45 +00:00
|
|
|
* Sets the stream key last used by the user for later reuse.
|
2018-06-14 09:15:36 +00:00
|
|
|
*
|
2018-07-05 11:17:45 +00:00
|
|
|
* @param {string} streamKey - The stream key to set.
|
2018-06-14 09:15:36 +00:00
|
|
|
* @returns {{
|
2018-07-05 11:17:45 +00:00
|
|
|
* type: SET_STREAM_KEY,
|
|
|
|
* streamKey: string
|
2018-06-14 09:15:36 +00:00
|
|
|
* }}
|
|
|
|
*/
|
2018-07-05 11:17:45 +00:00
|
|
|
export function setLiveStreamKey(streamKey: string) {
|
2018-06-14 09:15:36 +00:00
|
|
|
return {
|
2018-07-05 11:17:45 +00:00
|
|
|
type: SET_STREAM_KEY,
|
|
|
|
streamKey
|
2018-06-14 09:15:36 +00:00
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Signals that the pending recording notification should be shown on the
|
|
|
|
* screen.
|
|
|
|
*
|
2018-11-08 12:25:02 +00:00
|
|
|
* @param {string} streamType - The type of the stream ({@code file} or
|
|
|
|
* {@code stream}).
|
2018-06-14 09:15:36 +00:00
|
|
|
* @returns {Function}
|
|
|
|
*/
|
2018-07-05 11:17:45 +00:00
|
|
|
export function showPendingRecordingNotification(streamType: string) {
|
2022-11-11 08:20:33 +00:00
|
|
|
return async (dispatch: IStore['dispatch']) => {
|
2018-07-05 11:17:45 +00:00
|
|
|
const isLiveStreaming
|
|
|
|
= streamType === JitsiMeetJS.constants.recording.mode.STREAM;
|
|
|
|
const dialogProps = isLiveStreaming ? {
|
|
|
|
descriptionKey: 'liveStreaming.pending',
|
|
|
|
titleKey: 'dialog.liveStreaming'
|
|
|
|
} : {
|
2018-06-14 09:15:36 +00:00
|
|
|
descriptionKey: 'recording.pending',
|
|
|
|
titleKey: 'dialog.recording'
|
2018-07-05 11:17:45 +00:00
|
|
|
};
|
2021-01-11 10:52:52 +00:00
|
|
|
const notification = await dispatch(showNotification({
|
2018-07-05 11:17:45 +00:00
|
|
|
...dialogProps
|
2021-11-24 11:05:27 +00:00
|
|
|
}, NOTIFICATION_TIMEOUT_TYPE.MEDIUM));
|
2018-06-14 09:15:36 +00:00
|
|
|
|
2021-01-11 10:52:52 +00:00
|
|
|
if (notification) {
|
|
|
|
dispatch(_setPendingRecordingNotificationUid(notification.uid, streamType));
|
|
|
|
}
|
2018-06-14 09:15:36 +00:00
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2022-03-14 08:31:08 +00:00
|
|
|
/**
|
|
|
|
* Highlights a meeting moment.
|
|
|
|
*
|
|
|
|
* {@code stream}).
|
|
|
|
*
|
|
|
|
* @returns {Function}
|
|
|
|
*/
|
|
|
|
export function highlightMeetingMoment() {
|
|
|
|
return async (dispatch: Function, getState: Function) => {
|
|
|
|
dispatch(setHighlightMomentButtonState(true));
|
|
|
|
|
2022-03-18 08:19:20 +00:00
|
|
|
const success = await sendMeetingHighlight(getState());
|
|
|
|
|
|
|
|
if (success) {
|
2022-03-14 08:31:08 +00:00
|
|
|
dispatch(showNotification({
|
|
|
|
descriptionKey: 'recording.highlightMomentSucessDescription',
|
|
|
|
titleKey: 'recording.highlightMomentSuccess'
|
2022-03-21 13:14:26 +00:00
|
|
|
}, NOTIFICATION_TIMEOUT_TYPE.SHORT));
|
2022-03-14 08:31:08 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
dispatch(setHighlightMomentButtonState(false));
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2018-06-14 09:15:36 +00:00
|
|
|
/**
|
|
|
|
* Signals that the recording error notification should be shown.
|
|
|
|
*
|
|
|
|
* @param {Object} props - The Props needed to render the notification.
|
|
|
|
* @returns {showErrorNotification}
|
|
|
|
*/
|
|
|
|
export function showRecordingError(props: Object) {
|
2021-11-24 11:05:27 +00:00
|
|
|
return showErrorNotification(props, NOTIFICATION_TIMEOUT_TYPE.LONG);
|
2018-06-14 09:15:36 +00:00
|
|
|
}
|
|
|
|
|
2021-11-09 18:58:49 +00:00
|
|
|
/**
|
|
|
|
* Signals that the recording warning notification should be shown.
|
|
|
|
*
|
|
|
|
* @param {Object} props - The Props needed to render the notification.
|
|
|
|
* @returns {showWarningNotification}
|
|
|
|
*/
|
|
|
|
export function showRecordingWarning(props: Object) {
|
|
|
|
return showWarningNotification(props);
|
|
|
|
}
|
|
|
|
|
2018-06-14 09:15:36 +00:00
|
|
|
/**
|
|
|
|
* Signals that the stopped recording notification should be shown on the
|
|
|
|
* screen for a given period.
|
|
|
|
*
|
2018-11-08 12:25:02 +00:00
|
|
|
* @param {string} streamType - The type of the stream ({@code file} or
|
|
|
|
* {@code stream}).
|
2019-10-03 19:35:21 +00:00
|
|
|
* @param {string?} participantName - The participant name stopping the recording.
|
2018-06-14 09:15:36 +00:00
|
|
|
* @returns {showNotification}
|
|
|
|
*/
|
2019-10-03 19:35:21 +00:00
|
|
|
export function showStoppedRecordingNotification(streamType: string, participantName?: string) {
|
2018-07-05 11:17:45 +00:00
|
|
|
const isLiveStreaming
|
|
|
|
= streamType === JitsiMeetJS.constants.recording.mode.STREAM;
|
2019-10-03 19:35:21 +00:00
|
|
|
const descriptionArguments = { name: participantName };
|
2018-07-05 11:17:45 +00:00
|
|
|
const dialogProps = isLiveStreaming ? {
|
2019-10-03 19:35:21 +00:00
|
|
|
descriptionKey: participantName ? 'liveStreaming.offBy' : 'liveStreaming.off',
|
|
|
|
descriptionArguments,
|
2018-07-05 11:17:45 +00:00
|
|
|
titleKey: 'dialog.liveStreaming'
|
|
|
|
} : {
|
2019-10-03 19:35:21 +00:00
|
|
|
descriptionKey: participantName ? 'recording.offBy' : 'recording.off',
|
|
|
|
descriptionArguments,
|
|
|
|
titleKey: 'dialog.recording'
|
|
|
|
};
|
|
|
|
|
2021-11-24 11:05:27 +00:00
|
|
|
return showNotification(dialogProps, NOTIFICATION_TIMEOUT_TYPE.SHORT);
|
2019-10-03 19:35:21 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Signals that a started recording notification should be shown on the
|
|
|
|
* screen for a given period.
|
|
|
|
*
|
2021-06-21 08:36:18 +00:00
|
|
|
* @param {string} mode - The type of the recording: Stream of File.
|
|
|
|
* @param {string | Object } initiator - The participant who started recording.
|
|
|
|
* @param {string} sessionId - The recording session id.
|
|
|
|
* @returns {Function}
|
2019-10-03 19:35:21 +00:00
|
|
|
*/
|
2021-06-21 08:36:18 +00:00
|
|
|
export function showStartedRecordingNotification(
|
|
|
|
mode: string,
|
2022-11-11 08:20:33 +00:00
|
|
|
initiator: { getId: Function; } | string,
|
2021-06-21 08:36:18 +00:00
|
|
|
sessionId: string) {
|
|
|
|
return async (dispatch: Function, getState: Function) => {
|
|
|
|
const state = getState();
|
|
|
|
const initiatorId = getResourceId(initiator);
|
|
|
|
const participantName = getParticipantDisplayName(state, initiatorId);
|
2022-11-11 08:20:33 +00:00
|
|
|
let dialogProps: INotificationProps = {
|
2021-06-21 08:36:18 +00:00
|
|
|
descriptionKey: participantName ? 'liveStreaming.onBy' : 'liveStreaming.on',
|
|
|
|
descriptionArguments: { name: participantName },
|
|
|
|
titleKey: 'dialog.liveStreaming'
|
|
|
|
};
|
2018-07-05 11:17:45 +00:00
|
|
|
|
2021-06-21 08:36:18 +00:00
|
|
|
if (mode !== JitsiMeetJS.constants.recording.mode.STREAM) {
|
|
|
|
const recordingSharingUrl = getRecordingSharingUrl(state);
|
2022-11-11 08:20:33 +00:00
|
|
|
const iAmRecordingInitiator = getLocalParticipant(state)?.id === initiatorId;
|
2021-06-21 08:36:18 +00:00
|
|
|
|
|
|
|
dialogProps = {
|
|
|
|
customActionHandler: undefined,
|
|
|
|
customActionNameKey: undefined,
|
|
|
|
descriptionKey: participantName ? 'recording.onBy' : 'recording.on',
|
|
|
|
descriptionArguments: { name: participantName },
|
|
|
|
titleKey: 'dialog.recording'
|
|
|
|
};
|
|
|
|
|
|
|
|
// fetch the recording link from the server for recording initiators in jaas meetings
|
|
|
|
if (recordingSharingUrl
|
|
|
|
&& isVpaasMeeting(state)
|
2021-06-23 08:57:11 +00:00
|
|
|
&& iAmRecordingInitiator
|
|
|
|
&& !isSavingRecordingOnDropbox(state)) {
|
2021-06-21 08:36:18 +00:00
|
|
|
const region = getMeetingRegion(state);
|
|
|
|
const tenant = getVpaasTenant(state);
|
|
|
|
|
|
|
|
try {
|
2021-12-06 14:11:16 +00:00
|
|
|
const response = await getRecordingLink(recordingSharingUrl, sessionId, region, tenant);
|
|
|
|
const { url: link, urlExpirationTimeMillis: ttl } = response;
|
2021-06-21 08:36:18 +00:00
|
|
|
|
2021-10-22 08:53:22 +00:00
|
|
|
if (typeof APP === 'object') {
|
2021-12-06 14:11:16 +00:00
|
|
|
APP.API.notifyRecordingLinkAvailable(link, ttl);
|
2021-10-22 08:53:22 +00:00
|
|
|
}
|
|
|
|
|
2021-06-21 08:36:18 +00:00
|
|
|
// add the option to copy recording link
|
2021-11-10 11:19:40 +00:00
|
|
|
dialogProps.customActionNameKey = [ 'recording.copyLink' ];
|
|
|
|
dialogProps.customActionHandler = [ () => copyText(link) ];
|
2021-06-21 08:36:18 +00:00
|
|
|
dialogProps.titleKey = 'recording.on';
|
|
|
|
dialogProps.descriptionKey = 'recording.linkGenerated';
|
|
|
|
} catch (err) {
|
|
|
|
dispatch(showErrorNotification({
|
|
|
|
titleKey: 'recording.errorFetchingLink'
|
2021-11-24 11:05:27 +00:00
|
|
|
}, NOTIFICATION_TIMEOUT_TYPE.MEDIUM));
|
2021-06-21 08:36:18 +00:00
|
|
|
|
|
|
|
return logger.error('Could not fetch recording link', err);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-11-24 11:05:27 +00:00
|
|
|
dispatch(showNotification(dialogProps, NOTIFICATION_TIMEOUT_TYPE.SHORT));
|
2021-06-21 08:36:18 +00:00
|
|
|
};
|
2018-06-14 09:15:36 +00:00
|
|
|
}
|
2017-08-25 16:45:30 +00:00
|
|
|
|
|
|
|
/**
|
2018-05-16 14:00:16 +00:00
|
|
|
* Updates the known state for a given recording session.
|
2017-08-25 16:45:30 +00:00
|
|
|
*
|
2018-05-16 14:00:16 +00:00
|
|
|
* @param {Object} session - The new state to merge with the existing state in
|
|
|
|
* redux.
|
2017-08-25 16:45:30 +00:00
|
|
|
* @returns {{
|
2018-05-16 14:00:16 +00:00
|
|
|
* type: RECORDING_SESSION_UPDATED,
|
|
|
|
* sessionData: Object
|
2017-08-25 16:45:30 +00:00
|
|
|
* }}
|
|
|
|
*/
|
2022-11-11 08:20:33 +00:00
|
|
|
export function updateRecordingSessionData(session: any) {
|
2018-09-10 15:39:33 +00:00
|
|
|
const status = session.getStatus();
|
|
|
|
const timestamp
|
|
|
|
= status === JitsiRecordingConstants.status.ON
|
|
|
|
? Date.now() / 1000
|
|
|
|
: undefined;
|
|
|
|
|
2017-08-25 16:45:30 +00:00
|
|
|
return {
|
2018-05-16 14:00:16 +00:00
|
|
|
type: RECORDING_SESSION_UPDATED,
|
|
|
|
sessionData: {
|
|
|
|
error: session.getError(),
|
|
|
|
id: session.getID(),
|
2019-10-03 19:35:21 +00:00
|
|
|
initiator: session.getInitiator(),
|
2018-05-16 14:00:16 +00:00
|
|
|
liveStreamViewURL: session.getLiveStreamViewURL(),
|
|
|
|
mode: session.getMode(),
|
2018-09-10 15:39:33 +00:00
|
|
|
status,
|
2019-10-03 19:35:21 +00:00
|
|
|
terminator: session.getTerminator(),
|
2018-09-10 15:39:33 +00:00
|
|
|
timestamp
|
2018-05-16 14:00:16 +00:00
|
|
|
}
|
2017-08-25 16:45:30 +00:00
|
|
|
};
|
|
|
|
}
|
2018-07-05 11:17:45 +00:00
|
|
|
|
2021-06-23 08:57:11 +00:00
|
|
|
/**
|
|
|
|
* Sets the selected recording service.
|
|
|
|
*
|
|
|
|
* @param {string} selectedRecordingService - The new selected recording service.
|
|
|
|
* @returns {Object}
|
|
|
|
*/
|
|
|
|
export function setSelectedRecordingService(selectedRecordingService: string) {
|
|
|
|
return {
|
|
|
|
type: SET_SELECTED_RECORDING_SERVICE,
|
|
|
|
selectedRecordingService
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2018-07-05 11:17:45 +00:00
|
|
|
/**
|
|
|
|
* Sets UID of the the pending streaming notification to use it when hinding
|
|
|
|
* the notification is necessary, or unsets it when undefined (or no param) is
|
|
|
|
* passed.
|
|
|
|
*
|
|
|
|
* @param {?number} uid - The UID of the notification.
|
2018-11-08 12:25:02 +00:00
|
|
|
* @param {string} streamType - The type of the stream ({@code file} or
|
|
|
|
* {@code stream}).
|
2018-07-05 11:17:45 +00:00
|
|
|
* @returns {{
|
|
|
|
* type: SET_PENDING_RECORDING_NOTIFICATION_UID,
|
|
|
|
* streamType: string,
|
|
|
|
* uid: number
|
|
|
|
* }}
|
|
|
|
*/
|
2022-11-11 08:20:33 +00:00
|
|
|
function _setPendingRecordingNotificationUid(uid: string | undefined, streamType: string) {
|
2018-07-05 11:17:45 +00:00
|
|
|
return {
|
|
|
|
type: SET_PENDING_RECORDING_NOTIFICATION_UID,
|
|
|
|
streamType,
|
|
|
|
uid
|
|
|
|
};
|
|
|
|
}
|
2022-06-03 11:45:27 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Starts local recording.
|
|
|
|
*
|
2022-06-24 12:07:40 +00:00
|
|
|
* @param {boolean} onlySelf - Whether to only record the local streams.
|
2022-06-03 11:45:27 +00:00
|
|
|
* @returns {Object}
|
|
|
|
*/
|
2022-11-11 08:20:33 +00:00
|
|
|
export function startLocalVideoRecording(onlySelf: boolean) {
|
2022-06-03 11:45:27 +00:00
|
|
|
return {
|
2022-06-24 12:07:40 +00:00
|
|
|
type: START_LOCAL_RECORDING,
|
|
|
|
onlySelf
|
2022-06-03 11:45:27 +00:00
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Stops local recording.
|
|
|
|
*
|
|
|
|
* @returns {Object}
|
|
|
|
*/
|
|
|
|
export function stopLocalVideoRecording() {
|
|
|
|
return {
|
|
|
|
type: STOP_LOCAL_RECORDING
|
|
|
|
};
|
|
|
|
}
|