feat(recording): Limit notification
This commit is contained in:
parent
d740752522
commit
6773aed67f
15
config.js
15
config.js
|
@ -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,
|
||||
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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.
|
||||
*/
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -40,7 +40,7 @@ export default {
|
|||
notification: {
|
||||
backgroundColor: '#768898',
|
||||
flexDirection: 'row',
|
||||
height: 48,
|
||||
minHeight: 48,
|
||||
marginTop: 0.5 * BoxModel.margin
|
||||
},
|
||||
|
||||
|
|
|
@ -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));
|
||||
};
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -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));
|
|
@ -1,3 +1,4 @@
|
|||
// @flow
|
||||
|
||||
export { default as RecordingLabel } from './RecordingLabel';
|
||||
export { default as RecordingLimitNotificationDescription } from './RecordingLimitNotificationDescription';
|
||||
|
|
|
@ -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)) {
|
||||
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));
|
||||
}
|
||||
|
||||
|
||||
sendAnalytics(createRecordingEvent('start', mode));
|
||||
|
||||
if (disableRecordAudioNotification) {
|
||||
|
|
Loading…
Reference in New Issue