feat(recording): Limit notification

This commit is contained in:
Hristo Terezov 2020-05-28 17:42:02 -05:00
parent d740752522
commit 6773aed67f
11 changed files with 201 additions and 10 deletions

View File

@ -217,6 +217,21 @@ var config = {
// Default value for the channel "last N" attribute. -1 for unlimited.
channelLastN: -1,
// // Options for the recording limit notification.
// recordingLimit: {
//
// // The recording limit in minutes. Note: This number appears in the notification text
// // but doesn't enforce the actual recording time limit. This should be configured in
// // jibri!
// limit: 60,
//
// // The name of the app with unlimited recordings.
// appName: 'Unlimited recordings APP',
//
// // The URL of the app with unlimited recordings.
// appURL: 'https://unlimited.recordings.app.com/'
// },
// Disables or enables RTX (RFC 4588) (defaults to false).
// disableRtx: false,

View File

@ -395,6 +395,8 @@
"videoQuality": "Manage call quality"
},
"liveStreaming": {
"limitNotificationDescriptionWeb": "Due to high demand your streaming will be limited to {{limit}} min. For unlimited streaming try <a href={{url}} rel='noopener noreferrer' target='_blank'>{{app}}</a>.",
"limitNotificationDescriptionNative": "Your streaming will be limited to {{limit}} min. For unlimited streaming try {{app}}.",
"busy": "We're working on freeing streaming resources. Please try again in a few minutes.",
"busyTitle": "All streamers are currently busy",
"changeSignIn": "Switch accounts.",
@ -552,6 +554,8 @@
},
"raisedHand": "Would like to speak",
"recording": {
"limitNotificationDescriptionWeb": "Due to high demand your recording will be limited to {{limit}} min. For unlimited recordings try <a href={{url}} rel='noopener noreferrer' target='_blank'>{{app}}</a>.",
"limitNotificationDescriptionNative": "Due to high demand your recording will be limited to {{limit}} min. For unlimited recordings try <3>{{app}}</3>.",
"authDropboxText": "Upload to Dropbox",
"availableSpace": "Available space: {{spaceLeft}} MB (approximately {{duration}} minutes of recording)",
"beta": "BETA",

View File

@ -55,6 +55,11 @@ export type Props = {
*/
isDismissAllowed: boolean,
/**
* Maximum lines of the description.
*/
maxLines: ?number,
/**
* Callback invoked when the user clicks to dismiss the notification.
*/

View File

@ -11,6 +11,13 @@ import AbstractNotification, {
import styles from './styles';
/**
* Default value for the maxLines prop.
*
* @type {number}
*/
const DEFAULT_MAX_LINES = 1;
/**
* Implements a React {@link Component} to display a notification.
*
@ -24,9 +31,7 @@ class Notification extends AbstractNotification<Props> {
* @returns {ReactElement}
*/
render() {
const {
isDismissAllowed
} = this.props;
const { isDismissAllowed } = this.props;
return (
<View
@ -61,7 +66,7 @@ class Notification extends AbstractNotification<Props> {
* @private
*/
_renderContent() {
const { t, title, titleArguments, titleKey } = this.props;
const { maxLines = DEFAULT_MAX_LINES, t, title, titleArguments, titleKey } = this.props;
const titleText = title || (titleKey && t(titleKey, titleArguments));
const description = this._getDescription();
@ -69,7 +74,7 @@ class Notification extends AbstractNotification<Props> {
return description.map((line, index) => (
<Text
key = { index }
numberOfLines = { 1 }
numberOfLines = { maxLines }
style = { styles.contentText }>
{ line }
</Text>
@ -78,7 +83,7 @@ class Notification extends AbstractNotification<Props> {
return (
<Text
numberOfLines = { 1 }
numberOfLines = { maxLines }
style = { styles.contentText } >
{ titleText }
</Text>

View File

@ -40,7 +40,7 @@ export default {
notification: {
backgroundColor: '#768898',
flexDirection: 'row',
height: 48,
minHeight: 48,
marginTop: 0.5 * BoxModel.margin
},

View File

@ -0,0 +1,42 @@
// @flow
import JitsiMeetJS from '../base/lib-jitsi-meet';
import { showNotification } from '../notifications';
export * from './actions.any';
/**
* Signals that a started recording notification should be shown on the
* screen for a given period.
*
* @param {string} streamType - The type of the stream ({@code file} or
* {@code stream}).
* @returns {showNotification}
*/
export function showRecordingLimitNotification(streamType: string) {
return (dispatch: Function, getState: Function) => {
const isLiveStreaming = streamType === JitsiMeetJS.constants.recording.mode.STREAM;
let descriptionKey, titleKey;
if (isLiveStreaming) {
descriptionKey = 'liveStreaming.limitNotificationDescriptionNative';
titleKey = 'dialog.liveStreaming';
} else {
descriptionKey = 'recording.limitNotificationDescriptionNative';
titleKey = 'dialog.recording';
}
const { recordingLimit = {} } = getState()['features/base/config'];
const { limit, appName } = recordingLimit;
return dispatch(showNotification({
descriptionArguments: {
limit,
app: appName
},
descriptionKey,
titleKey,
maxLines: 2
}, 10000));
};
}

View File

@ -0,0 +1,27 @@
// @flow
import React from 'react';
import JitsiMeetJS from '../base/lib-jitsi-meet';
import { showNotification } from '../notifications';
import { RecordingLimitNotificationDescription } from './components';
export * from './actions.any';
/**
* Signals that a started recording notification should be shown on the
* screen for a given period.
*
* @param {string} streamType - The type of the stream ({@code file} or
* {@code stream}).
* @returns {showNotification}
*/
export function showRecordingLimitNotification(streamType: string) {
const isLiveStreaming = streamType === JitsiMeetJS.constants.recording.mode.STREAM;
return showNotification({
description: <RecordingLimitNotificationDescription isLiveStreaming = { isLiveStreaming } />,
titleKey: isLiveStreaming ? 'dialog.liveStreaming' : 'dialog.recording'
}, 10000);
}

View File

@ -0,0 +1,81 @@
// @flow
import React from 'react';
import { translate, translateToHTML } from '../../../base/i18n';
import { connect } from '../../../base/redux';
/**
* The type of the React {@code Component} props of {@link RecordingLimitNotificationDescription}.
*/
type Props = {
/**
* The limit of time in minutes for the recording.
*/
_limit: number,
/**
* The name of the app with unlimited recordings.
*/
_appName: string,
/**
* The URL to the app with unlimited recordings.
*/
_appURL: string,
/**
* True if the notification is related to the livestreaming and false if not.
*/
isLiveStreaming: Boolean,
/**
* Invoked to obtain translated strings.
*/
t: Function
};
/**
* A component that renders the description of the notification for the recording initiator.
*
* @param {Props} props - The props of the component.
* @returns {Component}
*/
function RecordingLimitNotificationDescription(props: Props) {
const { _limit, _appName, _appURL, isLiveStreaming, t } = props;
return (
<span>
{
translateToHTML(
t,
`${isLiveStreaming ? 'liveStreaming' : 'recording'}.limitNotificationDescriptionWeb`, {
limit: _limit,
app: _appName,
url: _appURL
})
}
</span>
);
}
/**
* Maps part of the Redix state to the props of this component.
*
* @param {Object} state - The Redux state.
* @returns {Props}
*/
function _mapStateToProps(state): $Shape<Props> {
const { recordingLimit = {} } = state['features/base/config'];
const { limit: _limit, appName: _appName, appURL: _appURL } = recordingLimit;
return {
_limit,
_appName,
_appURL
};
}
export default translate(connect(_mapStateToProps)(RecordingLimitNotificationDescription));

View File

@ -1,3 +1,4 @@
// @flow
export { default as RecordingLabel } from './RecordingLabel';
export { default as RecordingLimitNotificationDescription } from './RecordingLimitNotificationDescription';

View File

@ -26,6 +26,7 @@ import {
hidePendingRecordingNotification,
showPendingRecordingNotification,
showRecordingError,
showRecordingLimitNotification,
showStartedRecordingNotification,
showStoppedRecordingNotification,
updateRecordingSessionData
@ -44,6 +45,8 @@ import {
RECORDING_ON_SOUND_FILE
} from './sounds';
declare var interfaceConfig: Object;
/**
* StateListenerRegistry provides a reliable way to detect the leaving of a
* conference, where we need to clean up the recording sessions.
@ -131,7 +134,8 @@ MiddlewareRegistry.register(({ dispatch, getState }) => next => action => {
const {
iAmRecorder,
iAmSipGateway,
disableRecordAudioNotification
disableRecordAudioNotification,
recordingLimit
} = getState()['features/base/config'];
if (iAmRecorder && !iAmSipGateway) {
@ -151,9 +155,16 @@ MiddlewareRegistry.register(({ dispatch, getState }) => next => action => {
if (updatedSessionData.status === ON
&& (!oldSessionData || oldSessionData.status !== ON)) {
const initiatorName = initiator && getParticipantDisplayName(getState, initiator.getId());
if (initiator) {
const initiatorName = initiator && getParticipantDisplayName(getState, initiator.getId());
initiatorName && dispatch(showStartedRecordingNotification(mode, initiatorName));
} else if (typeof recordingLimit === 'object') {
// Show notification with additional information to the initiator.
dispatch(showRecordingLimitNotification(mode));
}
initiatorName && dispatch(showStartedRecordingNotification(mode, initiatorName));
sendAnalytics(createRecordingEvent('start', mode));
if (disableRecordAudioNotification) {