Add conference timer (#4958)

This commit is contained in:
theunafraid
2020-01-13 17:12:25 +00:00
committed by Дамян Минков
parent c73ba37202
commit c2cf09a2ca
19 changed files with 376 additions and 7 deletions
@@ -52,6 +52,16 @@ export const CONFERENCE_LEFT = 'CONFERENCE_LEFT';
*/
export const CONFERENCE_SUBJECT_CHANGED = 'CONFERENCE_SUBJECT_CHANGED';
/**
* The type of (redux) action, which indicates conference UTC timestamp changes.
*
* {
* type: CONFERENCE_TIMESTAMP_CHANGED
* timestamp: number
* }
*/
export const CONFERENCE_TIMESTAMP_CHANGED = 'CONFERENCE_TIMESTAMP_CHANGED';
/**
* The type of (redux) action which signals that a specific conference will be
* joined.
+24 -1
View File
@@ -35,6 +35,7 @@ import {
CONFERENCE_JOINED,
CONFERENCE_LEFT,
CONFERENCE_SUBJECT_CHANGED,
CONFERENCE_TIMESTAMP_CHANGED,
CONFERENCE_WILL_JOIN,
CONFERENCE_WILL_LEAVE,
DATA_CHANNEL_OPENED,
@@ -93,10 +94,16 @@ function _addConferenceListeners(conference, dispatch) {
(...args) => dispatch(conferenceJoined(conference, ...args)));
conference.on(
JitsiConferenceEvents.CONFERENCE_LEFT,
(...args) => dispatch(conferenceLeft(conference, ...args)));
(...args) => {
dispatch(conferenceTimestampChanged(0));
dispatch(conferenceLeft(conference, ...args));
});
conference.on(JitsiConferenceEvents.SUBJECT_CHANGED,
(...args) => dispatch(conferenceSubjectChanged(...args)));
conference.on(JitsiConferenceEvents.CONFERENCE_CREATED_TIMESTAMP,
(...args) => dispatch(conferenceTimestampChanged(...args)));
conference.on(
JitsiConferenceEvents.KICKED,
(...args) => dispatch(kickedOut(conference, ...args)));
@@ -313,6 +320,22 @@ export function conferenceSubjectChanged(subject: string) {
};
}
/**
* Signals that the conference timestamp has been changed.
*
* @param {number} conferenceTimestamp - The UTC timestamp.
* @returns {{
* type: CONFERENCE_TIMESTAMP_CHANGED,
* conferenceTimestamp
* }}
*/
export function conferenceTimestampChanged(conferenceTimestamp: number) {
return {
type: CONFERENCE_TIMESTAMP_CHANGED,
conferenceTimestamp
};
}
/**
* Adds any existing local tracks to a specific conference before the conference
* is joined. Then signals the intention of the application to have the local
@@ -167,6 +167,20 @@ export function getConferenceName(stateful: Function | Object): string {
|| _.startCase(safeDecodeURIComponent(room));
}
/**
* Returns the UTC timestamp when the first participant joined the conference.
*
* @param {Function | Object} stateful - Reference that can be resolved to Redux
* state with the {@code toState} function.
* @returns {number}
*/
export function getConferenceTimestamp(stateful: Function | Object): number {
const state = toState(stateful);
const { conferenceTimestamp } = state['features/base/conference'];
return conferenceTimestamp;
}
/**
* Returns the current {@code JitsiConference} which is joining or joined and is
* not leaving. Please note the contrast with merely reading the
@@ -11,6 +11,7 @@ import {
CONFERENCE_JOINED,
CONFERENCE_LEFT,
CONFERENCE_SUBJECT_CHANGED,
CONFERENCE_TIMESTAMP_CHANGED,
CONFERENCE_WILL_JOIN,
CONFERENCE_WILL_LEAVE,
LOCK_STATE_CHANGED,
@@ -59,6 +60,9 @@ ReducerRegistry.register(
case CONFERENCE_SUBJECT_CHANGED:
return set(state, 'subject', action.subject);
case CONFERENCE_TIMESTAMP_CHANGED:
return set(state, 'conferenceTimestamp', action.conferenceTimestamp);
case CONFERENCE_LEFT:
case CONFERENCE_WILL_LEAVE:
return _conferenceLeftOrWillLeave(state, action);
@@ -0,0 +1,176 @@
// @flow
import { Component } from 'react';
import { connect } from '../../base/redux';
import { getLocalizedDurationFormatter } from '../../base/i18n';
import { getConferenceTimestamp } from '../../base/conference/functions';
import { renderConferenceTimer } from '../';
/**
* The type of the React {@code Component} props of {@link ConferenceTimer}.
*/
type Props = {
/**
* The UTC timestamp representing the time when first participant joined.
*/
_startTimestamp: ?number,
/**
* The redux {@code dispatch} function.
*/
dispatch: Function
};
/**
* The type of the React {@code Component} state of {@link ConferenceTimer}.
*/
type State = {
/**
* Value of current conference time.
*/
timerValue: string
};
/**
* ConferenceTimer react component.
*
* @class ConferenceTimer
* @extends Component
*/
class ConferenceTimer extends Component<Props, State> {
/**
* Handle for setInterval timer.
*/
_interval;
/**
* Initializes a new {@code ConferenceTimer} instance.
*
* @param {Props} props - The read-only properties with which the new
* instance is to be initialized.
*/
constructor(props: Props) {
super(props);
this.state = {
timerValue: getLocalizedDurationFormatter(0)
};
}
/**
* Starts the conference timer when component will be
* mounted.
*
* @inheritdoc
*/
componentDidMount() {
this._startTimer();
}
/**
* Stops the conference timer when component will be
* unmounted.
*
* @inheritdoc
*/
componentWillUnmount() {
this._stopTimer();
}
/**
* Implements React's {@link Component#render()}.
*
* @inheritdoc
* @returns {ReactElement}
*/
render() {
const { timerValue } = this.state;
const { _startTimestamp } = this.props;
if (!_startTimestamp) {
return null;
}
return renderConferenceTimer(timerValue);
}
/**
* Sets the current state values that will be used to render the timer.
*
* @param {number} refValueUTC - The initial UTC timestamp value.
* @param {number} currentValueUTC - The current UTC timestamp value.
*
* @returns {void}
*/
_setStateFromUTC(refValueUTC, currentValueUTC) {
if (!refValueUTC || !currentValueUTC) {
return;
}
if (currentValueUTC < refValueUTC) {
return;
}
const timerMsValue = currentValueUTC - refValueUTC;
const localizedTime = getLocalizedDurationFormatter(timerMsValue);
this.setState({
timerValue: localizedTime
});
}
/**
* Start conference timer.
*
* @returns {void}
*/
_startTimer() {
if (!this._interval) {
this._setStateFromUTC(this.props._startTimestamp, (new Date()).getTime());
this._interval = setInterval(() => {
this._setStateFromUTC(this.props._startTimestamp, (new Date()).getTime());
}, 1000);
}
}
/**
* Stop conference timer.
*
* @returns {void}
*/
_stopTimer() {
if (this._interval) {
clearInterval(this._interval);
}
this.setState({
timerValue: getLocalizedDurationFormatter(0)
});
}
}
/**
* Maps (parts of) the Redux state to the associated
* {@code ConferenceTimer}'s props.
*
* @param {Object} state - The Redux state.
* @private
* @returns {{
* _startTimestamp: number
* }}
*/
export function _mapStateToProps(state: Object) {
return {
_startTimestamp: getConferenceTimestamp(state)
};
}
export default connect(_mapStateToProps)(ConferenceTimer);
@@ -0,0 +1,23 @@
// @flow
import React from 'react';
import { Text } from 'react-native';
import styles from './styles';
/**
* Returns native element to be rendered.
*
* @param {string} timerValue - String to display as time.
*
* @returns {ReactElement}
*/
export default function renderConferenceTimer(timerValue: string) {
return (
<Text
numberOfLines = { 4 }
style = { styles.roomTimer }>
{ timerValue }
</Text>
);
}
@@ -9,6 +9,7 @@ import { connect } from '../../../base/redux';
import { PictureInPictureButton } from '../../../mobile/picture-in-picture';
import { isToolboxVisible } from '../../../toolbox';
import ConferenceTimer from '../ConferenceTimer';
import styles, { NAVBAR_GRADIENT_COLORS } from './styles';
type Props = {
@@ -63,6 +64,7 @@ class NavigationBar extends Component<Props> {
style = { styles.roomName }>
{ this.props._meetingName }
</Text>
<ConferenceTimer />
</View>
</View>
];
@@ -1,3 +1,4 @@
// @flow
export { default as Conference } from './Conference';
export { default as renderConferenceTimer } from './ConferenceTimerDisplay';
@@ -105,6 +105,12 @@ export default {
paddingHorizontal: 14
},
roomTimer: {
color: ColorPalette.white,
fontSize: 15,
opacity: 0.6
},
roomName: {
color: ColorPalette.white,
fontSize: 17,
@@ -112,8 +118,8 @@ export default {
},
roomNameWrapper: {
flexDirection: 'row',
justifyContent: 'center',
flexDirection: 'column',
alignItems: 'center',
left: 0,
paddingHorizontal: 48,
position: 'absolute',
@@ -0,0 +1,16 @@
// @flow
import React from 'react';
/**
* Returns web element to be rendered.
*
* @param {string} timerValue - String to display as time.
*
* @returns {ReactElement}
*/
export default function renderConferenceTimer(timerValue: string) {
return (
<span className = 'subject-conference-timer' >{ timerValue }</span>
);
}
@@ -7,6 +7,7 @@ import { getParticipantCount } from '../../../base/participants/functions';
import { connect } from '../../../base/redux';
import { isToolboxVisible } from '../../../toolbox';
import ConferenceTimer from '../ConferenceTimer';
import ParticipantsCount from './ParticipantsCount';
/**
@@ -51,6 +52,7 @@ class Subject extends Component<Props> {
<div className = { `subject ${_visible ? 'visible' : ''}` }>
<span className = 'subject-text'>{ _subject }</span>
{ _showParticipantCount && <ParticipantsCount /> }
<ConferenceTimer />
</div>
);
}
@@ -1,3 +1,5 @@
// @flow
export { default as Conference } from './Conference';
export { default as renderConferenceTimer } from './ConferenceTimerDisplay';