jiti-meet/react/features/local-recording/components/LocalRecordingInfoDialog.js

405 lines
11 KiB
JavaScript
Raw Normal View History

2019-03-19 15:42:25 +00:00
// @flow
import moment from 'moment';
import React, { Component } from 'react';
2019-03-19 15:42:25 +00:00
import type { Dispatch } from 'redux';
import { Dialog } from '../../base/dialog';
import { translate } from '../../base/i18n';
import {
PARTICIPANT_ROLE,
getLocalParticipant
} from '../../base/participants';
2019-03-21 16:38:29 +00:00
import { connect } from '../../base/redux';
import { statsUpdate } from '../actions';
import { recordingController } from '../controller';
/**
* The type of the React {@code Component} props of
* {@link LocalRecordingInfoDialog}.
*/
type Props = {
/**
* Redux store dispatch function.
*/
2019-03-19 15:42:25 +00:00
dispatch: Dispatch<any>,
/**
* Current encoding format.
*/
encodingFormat: string,
/**
* Whether the local user is the moderator.
*/
isModerator: boolean,
/**
* Whether local recording is engaged.
*/
2018-07-10 15:11:22 +00:00
isEngaged: boolean,
/**
* The start time of the current local recording session.
* Used to calculate the duration of recording.
*/
2018-07-10 14:26:16 +00:00
recordingEngagedAt: Date,
/**
* Stats of all the participant.
*/
stats: Object,
/**
* Invoked to obtain translated strings.
*/
t: Function
}
/**
* The type of the React {@code Component} state of
* {@link LocalRecordingInfoDialog}.
*/
type State = {
/**
* The recording duration string to be displayed on the UI.
*/
durationString: string
}
/**
* A React Component with the contents for a dialog that shows information about
* local recording. For users with moderator rights, this is also the "control
* panel" for starting/stopping local recording on all clients.
*
* @extends Component
*/
class LocalRecordingInfoDialog extends Component<Props, State> {
/**
* Saves a handle to the timer for UI updates,
* so that it can be cancelled when the component unmounts.
*/
_timer: ?IntervalID;
/**
2018-07-10 15:11:22 +00:00
* Initializes a new {@code LocalRecordingInfoDialog} instance.
*
* @param {Props} props - The React {@code Component} props to initialize
* the new {@code LocalRecordingInfoDialog} instance with.
*/
2018-07-10 15:11:22 +00:00
constructor(props: Props) {
super(props);
this.state = {
durationString: ''
};
}
/**
2018-07-10 11:20:36 +00:00
* Implements React's {@link Component#componentDidMount()}.
*
* @returns {void}
*/
2018-07-10 11:20:36 +00:00
componentDidMount() {
this._timer = setInterval(
() => {
this.setState((_prevState, props) => {
2018-07-10 14:26:16 +00:00
const nowTime = new Date();
return {
durationString: this._getDuration(nowTime,
2018-07-10 14:26:16 +00:00
props.recordingEngagedAt)
};
});
try {
this.props.dispatch(
statsUpdate(recordingController
.getParticipantsStats()));
} catch (e) {
// do nothing
}
},
1000
);
}
/**
* Implements React's {@link Component#componentWillUnmount()}.
*
* @returns {void}
*/
componentWillUnmount() {
if (this._timer) {
clearInterval(this._timer);
this._timer = null;
}
}
/**
* Implements React's {@link Component#render()}.
*
* @inheritdoc
* @returns {ReactElement}
*/
render() {
2018-07-10 15:11:22 +00:00
const { isModerator, t } = this.props;
return (
2018-07-18 22:29:24 +00:00
<Dialog
2019-03-06 17:23:53 +00:00
cancelKey = { 'dialog.close' }
2018-07-18 22:29:24 +00:00
submitDisabled = { true }
titleKey = 'localRecording.dialogTitle'>
2018-07-31 23:51:37 +00:00
<div className = 'localrec-control'>
<span className = 'localrec-control-info-label'>
{`${t('localRecording.moderator')}:`}
</span>
<span className = 'info-value'>
{ isModerator
? t('localRecording.yes')
: t('localRecording.no') }
</span>
2018-07-10 15:11:22 +00:00
</div>
2018-07-31 23:51:37 +00:00
{ this._renderModeratorControls() }
{ this._renderDurationAndFormat() }
2018-07-18 22:29:24 +00:00
</Dialog>
2018-07-10 15:11:22 +00:00
);
}
/**
* Renders the recording duration and encoding format. Only shown if local
* recording is engaged.
*
* @private
* @returns {ReactElement|null}
*/
_renderDurationAndFormat() {
const { encodingFormat, isEngaged, t } = this.props;
const { durationString } = this.state;
if (!isEngaged) {
return null;
}
return (
<div>
<div>
2018-07-31 23:51:37 +00:00
<span className = 'localrec-control-info-label'>
2018-07-10 15:11:22 +00:00
{`${t('localRecording.duration')}:`}
</span>
<span className = 'info-value'>
{ durationString === ''
? t('localRecording.durationNA')
: durationString }
</span>
</div>
<div>
2018-07-31 23:51:37 +00:00
<span className = 'localrec-control-info-label'>
2018-07-10 15:11:22 +00:00
{`${t('localRecording.encoding')}:`}
</span>
<span className = 'info-value'>
{ encodingFormat }
</span>
</div>
</div>
);
}
/**
* Returns React elements for displaying the local recording stats of
* each participant.
*
* @private
2018-07-31 23:51:37 +00:00
* @returns {ReactElement|null}
2018-07-10 15:11:22 +00:00
*/
_renderStats() {
const { stats } = this.props;
if (stats === undefined) {
2018-07-31 23:51:37 +00:00
return null;
2018-07-10 15:11:22 +00:00
}
const ids = Object.keys(stats);
return (
2018-07-31 23:51:37 +00:00
<div className = 'localrec-participant-stats' >
{ this._renderStatsHeader() }
2018-07-10 15:11:22 +00:00
{ ids.map((id, i) => this._renderStatsLine(i, id)) }
2018-07-31 23:51:37 +00:00
</div>
2018-07-10 15:11:22 +00:00
);
}
/**
* Renders the stats for one participant.
*
* @private
* @param {*} lineKey - The key required by React for elements in lists.
* @param {*} id - The ID of the participant.
* @returns {ReactElement}
*/
_renderStatsLine(lineKey, id) {
2018-07-31 23:51:37 +00:00
const { stats } = this.props;
let statusClass = 'localrec-participant-stats-item__status-dot ';
statusClass += stats[id].recordingStats
? stats[id].recordingStats.isRecording
? 'status-on'
: 'status-off'
: 'status-unknown';
2018-07-10 15:11:22 +00:00
return (
2018-07-31 23:51:37 +00:00
<div
className = 'localrec-participant-stats-item'
key = { lineKey } >
<div className = 'localrec-participant-stats-item__status'>
<span className = { statusClass } />
</div>
<div className = 'localrec-participant-stats-item__name'>
{ stats[id].displayName || id }
</div>
<div className = 'localrec-participant-stats-item__sessionid'>
{ stats[id].recordingStats.currentSessionToken }
</div>
</div>
);
}
/**
* Renders the participant stats header line.
*
* @private
* @returns {ReactElement}
*/
_renderStatsHeader() {
const { t } = this.props;
return (
<div className = 'localrec-participant-stats-item'>
<div className = 'localrec-participant-stats-item__status' />
<div className = 'localrec-participant-stats-item__name'>
{ t('localRecording.participant') }
</div>
<div className = 'localrec-participant-stats-item__sessionid'>
{ t('localRecording.sessionToken') }
</div>
</div>
2018-07-10 15:11:22 +00:00
);
}
/**
* Renders the moderator-only controls: The stats of all users and the
2018-07-10 15:11:22 +00:00
* action links.
*
* @private
* @returns {ReactElement|null}
*/
_renderModeratorControls() {
const { isModerator, isEngaged, t } = this.props;
if (!isModerator) {
return null;
}
return (
2018-07-18 22:29:24 +00:00
<div>
2018-07-31 23:51:37 +00:00
<div className = 'localrec-control-action-links'>
<div className = 'localrec-control-action-link'>
2018-07-18 22:29:24 +00:00
{ isEngaged ? <a
onClick = { this._onStop }>
{ t('localRecording.stop') }
</a>
: <a
onClick = { this._onStart }>
{ t('localRecording.start') }
2018-07-10 15:11:22 +00:00
</a>
2018-07-18 22:29:24 +00:00
}
</div>
</div>
2018-07-31 23:51:37 +00:00
<div>
<span className = 'localrec-control-info-label'>
{`${t('localRecording.participantStats')}:`}
</span>
</div>
{ this._renderStats() }
2018-07-18 22:29:24 +00:00
</div>
);
}
/**
* Creates a duration string "HH:MM:SS" from two Date objects.
*
* @param {Date} now - Current time.
* @param {Date} prev - Previous time, the time to be subtracted.
* @returns {string}
*/
_getDuration(now, prev) {
if (prev === null || prev === undefined) {
return '';
}
// Still a hack, as moment.js does not support formatting of duration
// (i.e. TimeDelta). Only works if total duration < 24 hours.
// But who is going to have a 24-hour long conference?
return moment(now - prev).utc()
.format('HH:mm:ss');
}
/**
* Callback function for the Start UI action.
*
* @private
* @returns {void}
*/
_onStart() {
recordingController.startRecording();
}
/**
* Callback function for the Stop UI action.
*
* @private
* @returns {void}
*/
_onStop() {
recordingController.stopRecording();
}
}
/**
* Maps (parts of) the Redux state to the associated props for the
* {@code LocalRecordingInfoDialog} component.
*
* @param {Object} state - The Redux state.
* @private
* @returns {{
* encodingFormat: string,
* isModerator: boolean,
2018-07-10 15:11:22 +00:00
* isEngaged: boolean,
2018-07-10 14:26:16 +00:00
* recordingEngagedAt: Date,
* stats: Object
* }}
*/
function _mapStateToProps(state) {
const {
encodingFormat,
2018-07-10 15:11:22 +00:00
isEngaged,
2018-07-10 14:26:16 +00:00
recordingEngagedAt,
stats
} = state['features/local-recording'];
const isModerator
= getLocalParticipant(state).role === PARTICIPANT_ROLE.MODERATOR;
return {
encodingFormat,
isModerator,
2018-07-10 15:11:22 +00:00
isEngaged,
2018-07-10 14:26:16 +00:00
recordingEngagedAt,
stats
};
}
export default translate(connect(_mapStateToProps)(LocalRecordingInfoDialog));