Add conference timer (#4958)
This commit is contained in:
parent
c73ba37202
commit
c2cf09a2ca
|
@ -41,6 +41,7 @@ import {
|
||||||
conferenceJoined,
|
conferenceJoined,
|
||||||
conferenceLeft,
|
conferenceLeft,
|
||||||
conferenceSubjectChanged,
|
conferenceSubjectChanged,
|
||||||
|
conferenceTimestampChanged,
|
||||||
conferenceWillJoin,
|
conferenceWillJoin,
|
||||||
conferenceWillLeave,
|
conferenceWillLeave,
|
||||||
dataChannelOpened,
|
dataChannelOpened,
|
||||||
|
@ -1818,7 +1819,10 @@ export default {
|
||||||
|
|
||||||
room.on(
|
room.on(
|
||||||
JitsiConferenceEvents.CONFERENCE_LEFT,
|
JitsiConferenceEvents.CONFERENCE_LEFT,
|
||||||
(...args) => APP.store.dispatch(conferenceLeft(room, ...args)));
|
(...args) => {
|
||||||
|
APP.store.dispatch(conferenceTimestampChanged(0));
|
||||||
|
APP.store.dispatch(conferenceLeft(room, ...args));
|
||||||
|
});
|
||||||
|
|
||||||
room.on(
|
room.on(
|
||||||
JitsiConferenceEvents.AUTH_STATUS_CHANGED,
|
JitsiConferenceEvents.AUTH_STATUS_CHANGED,
|
||||||
|
@ -1948,6 +1952,10 @@ export default {
|
||||||
JitsiConferenceEvents.DOMINANT_SPEAKER_CHANGED,
|
JitsiConferenceEvents.DOMINANT_SPEAKER_CHANGED,
|
||||||
id => APP.store.dispatch(dominantSpeakerChanged(id, room)));
|
id => APP.store.dispatch(dominantSpeakerChanged(id, room)));
|
||||||
|
|
||||||
|
room.on(
|
||||||
|
JitsiConferenceEvents.CONFERENCE_CREATED_TIMESTAMP,
|
||||||
|
conferenceTimestamp => APP.store.dispatch(conferenceTimestampChanged(conferenceTimestamp)));
|
||||||
|
|
||||||
room.on(JitsiConferenceEvents.CONNECTION_INTERRUPTED, () => {
|
room.on(JitsiConferenceEvents.CONNECTION_INTERRUPTED, () => {
|
||||||
APP.store.dispatch(localParticipantConnectionStatusChanged(
|
APP.store.dispatch(localParticipantConnectionStatusChanged(
|
||||||
JitsiParticipantConnectionStatus.INTERRUPTED));
|
JitsiParticipantConnectionStatus.INTERRUPTED));
|
||||||
|
|
|
@ -23,4 +23,10 @@
|
||||||
&-text {
|
&-text {
|
||||||
vertical-align: middle;
|
vertical-align: middle;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&-conference-timer {
|
||||||
|
display: block;
|
||||||
|
font-size: 15px;
|
||||||
|
opacity: 0.6;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,6 +30,7 @@ VirtualHost "jitmeet.example.com"
|
||||||
certificate = "/etc/prosody/certs/jitmeet.example.com.crt";
|
certificate = "/etc/prosody/certs/jitmeet.example.com.crt";
|
||||||
}
|
}
|
||||||
speakerstats_component = "speakerstats.jitmeet.example.com"
|
speakerstats_component = "speakerstats.jitmeet.example.com"
|
||||||
|
conference_duration_component = "conference_duration.jitmeet.example.com"
|
||||||
-- we need bosh
|
-- we need bosh
|
||||||
modules_enabled = {
|
modules_enabled = {
|
||||||
"bosh";
|
"bosh";
|
||||||
|
@ -37,6 +38,7 @@ VirtualHost "jitmeet.example.com"
|
||||||
"ping"; -- Enable mod_ping
|
"ping"; -- Enable mod_ping
|
||||||
"speakerstats";
|
"speakerstats";
|
||||||
"turncredentials";
|
"turncredentials";
|
||||||
|
"conference_duration";
|
||||||
}
|
}
|
||||||
c2s_require_encryption = false
|
c2s_require_encryption = false
|
||||||
|
|
||||||
|
@ -65,3 +67,6 @@ Component "focus.jitmeet.example.com"
|
||||||
|
|
||||||
Component "speakerstats.jitmeet.example.com" "speakerstats_component"
|
Component "speakerstats.jitmeet.example.com" "speakerstats_component"
|
||||||
muc_component = "conference.jitmeet.example.com"
|
muc_component = "conference.jitmeet.example.com"
|
||||||
|
|
||||||
|
Component "conference_duration.jitmeet.example.com" "conference_duration_component"
|
||||||
|
muc_component = "conference.jitmeet.example.com"
|
||||||
|
|
|
@ -10869,8 +10869,8 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"lib-jitsi-meet": {
|
"lib-jitsi-meet": {
|
||||||
"version": "github:jitsi/lib-jitsi-meet#9ba4c15e85c6b270c367affab07180a069041ae9",
|
"version": "github:jitsi/lib-jitsi-meet#f35c74222f644b6420cddd8f6a4a2dfffe451f3b",
|
||||||
"from": "github:jitsi/lib-jitsi-meet#9ba4c15e85c6b270c367affab07180a069041ae9",
|
"from": "github:jitsi/lib-jitsi-meet#f35c74222f644b6420cddd8f6a4a2dfffe451f3b",
|
||||||
"requires": {
|
"requires": {
|
||||||
"@jitsi/sdp-interop": "0.1.14",
|
"@jitsi/sdp-interop": "0.1.14",
|
||||||
"@jitsi/sdp-simulcast": "0.2.2",
|
"@jitsi/sdp-simulcast": "0.2.2",
|
||||||
|
|
|
@ -56,7 +56,7 @@
|
||||||
"js-utils": "github:jitsi/js-utils#400ce825d3565019946ee75d86ed773c6f21e117",
|
"js-utils": "github:jitsi/js-utils#400ce825d3565019946ee75d86ed773c6f21e117",
|
||||||
"jsrsasign": "8.0.12",
|
"jsrsasign": "8.0.12",
|
||||||
"jwt-decode": "2.2.0",
|
"jwt-decode": "2.2.0",
|
||||||
"lib-jitsi-meet": "github:jitsi/lib-jitsi-meet#9ba4c15e85c6b270c367affab07180a069041ae9",
|
"lib-jitsi-meet": "github:jitsi/lib-jitsi-meet#f35c74222f644b6420cddd8f6a4a2dfffe451f3b",
|
||||||
"libflacjs": "github:mmig/libflac.js#93d37e7f811f01cf7d8b6a603e38bd3c3810907d",
|
"libflacjs": "github:mmig/libflac.js#93d37e7f811f01cf7d8b6a603e38bd3c3810907d",
|
||||||
"lodash": "4.17.13",
|
"lodash": "4.17.13",
|
||||||
"moment": "2.19.4",
|
"moment": "2.19.4",
|
||||||
|
|
|
@ -52,6 +52,16 @@ export const CONFERENCE_LEFT = 'CONFERENCE_LEFT';
|
||||||
*/
|
*/
|
||||||
export const CONFERENCE_SUBJECT_CHANGED = 'CONFERENCE_SUBJECT_CHANGED';
|
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
|
* The type of (redux) action which signals that a specific conference will be
|
||||||
* joined.
|
* joined.
|
||||||
|
|
|
@ -35,6 +35,7 @@ import {
|
||||||
CONFERENCE_JOINED,
|
CONFERENCE_JOINED,
|
||||||
CONFERENCE_LEFT,
|
CONFERENCE_LEFT,
|
||||||
CONFERENCE_SUBJECT_CHANGED,
|
CONFERENCE_SUBJECT_CHANGED,
|
||||||
|
CONFERENCE_TIMESTAMP_CHANGED,
|
||||||
CONFERENCE_WILL_JOIN,
|
CONFERENCE_WILL_JOIN,
|
||||||
CONFERENCE_WILL_LEAVE,
|
CONFERENCE_WILL_LEAVE,
|
||||||
DATA_CHANNEL_OPENED,
|
DATA_CHANNEL_OPENED,
|
||||||
|
@ -93,10 +94,16 @@ function _addConferenceListeners(conference, dispatch) {
|
||||||
(...args) => dispatch(conferenceJoined(conference, ...args)));
|
(...args) => dispatch(conferenceJoined(conference, ...args)));
|
||||||
conference.on(
|
conference.on(
|
||||||
JitsiConferenceEvents.CONFERENCE_LEFT,
|
JitsiConferenceEvents.CONFERENCE_LEFT,
|
||||||
(...args) => dispatch(conferenceLeft(conference, ...args)));
|
(...args) => {
|
||||||
|
dispatch(conferenceTimestampChanged(0));
|
||||||
|
dispatch(conferenceLeft(conference, ...args));
|
||||||
|
});
|
||||||
conference.on(JitsiConferenceEvents.SUBJECT_CHANGED,
|
conference.on(JitsiConferenceEvents.SUBJECT_CHANGED,
|
||||||
(...args) => dispatch(conferenceSubjectChanged(...args)));
|
(...args) => dispatch(conferenceSubjectChanged(...args)));
|
||||||
|
|
||||||
|
conference.on(JitsiConferenceEvents.CONFERENCE_CREATED_TIMESTAMP,
|
||||||
|
(...args) => dispatch(conferenceTimestampChanged(...args)));
|
||||||
|
|
||||||
conference.on(
|
conference.on(
|
||||||
JitsiConferenceEvents.KICKED,
|
JitsiConferenceEvents.KICKED,
|
||||||
(...args) => dispatch(kickedOut(conference, ...args)));
|
(...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
|
* 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
|
* 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));
|
|| _.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
|
* Returns the current {@code JitsiConference} which is joining or joined and is
|
||||||
* not leaving. Please note the contrast with merely reading the
|
* not leaving. Please note the contrast with merely reading the
|
||||||
|
|
|
@ -11,6 +11,7 @@ import {
|
||||||
CONFERENCE_JOINED,
|
CONFERENCE_JOINED,
|
||||||
CONFERENCE_LEFT,
|
CONFERENCE_LEFT,
|
||||||
CONFERENCE_SUBJECT_CHANGED,
|
CONFERENCE_SUBJECT_CHANGED,
|
||||||
|
CONFERENCE_TIMESTAMP_CHANGED,
|
||||||
CONFERENCE_WILL_JOIN,
|
CONFERENCE_WILL_JOIN,
|
||||||
CONFERENCE_WILL_LEAVE,
|
CONFERENCE_WILL_LEAVE,
|
||||||
LOCK_STATE_CHANGED,
|
LOCK_STATE_CHANGED,
|
||||||
|
@ -59,6 +60,9 @@ ReducerRegistry.register(
|
||||||
case CONFERENCE_SUBJECT_CHANGED:
|
case CONFERENCE_SUBJECT_CHANGED:
|
||||||
return set(state, 'subject', action.subject);
|
return set(state, 'subject', action.subject);
|
||||||
|
|
||||||
|
case CONFERENCE_TIMESTAMP_CHANGED:
|
||||||
|
return set(state, 'conferenceTimestamp', action.conferenceTimestamp);
|
||||||
|
|
||||||
case CONFERENCE_LEFT:
|
case CONFERENCE_LEFT:
|
||||||
case CONFERENCE_WILL_LEAVE:
|
case CONFERENCE_WILL_LEAVE:
|
||||||
return _conferenceLeftOrWillLeave(state, action);
|
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 { PictureInPictureButton } from '../../../mobile/picture-in-picture';
|
||||||
import { isToolboxVisible } from '../../../toolbox';
|
import { isToolboxVisible } from '../../../toolbox';
|
||||||
|
|
||||||
|
import ConferenceTimer from '../ConferenceTimer';
|
||||||
import styles, { NAVBAR_GRADIENT_COLORS } from './styles';
|
import styles, { NAVBAR_GRADIENT_COLORS } from './styles';
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
|
@ -63,6 +64,7 @@ class NavigationBar extends Component<Props> {
|
||||||
style = { styles.roomName }>
|
style = { styles.roomName }>
|
||||||
{ this.props._meetingName }
|
{ this.props._meetingName }
|
||||||
</Text>
|
</Text>
|
||||||
|
<ConferenceTimer />
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
];
|
];
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
// @flow
|
// @flow
|
||||||
|
|
||||||
export { default as Conference } from './Conference';
|
export { default as Conference } from './Conference';
|
||||||
|
export { default as renderConferenceTimer } from './ConferenceTimerDisplay';
|
||||||
|
|
|
@ -105,6 +105,12 @@ export default {
|
||||||
paddingHorizontal: 14
|
paddingHorizontal: 14
|
||||||
},
|
},
|
||||||
|
|
||||||
|
roomTimer: {
|
||||||
|
color: ColorPalette.white,
|
||||||
|
fontSize: 15,
|
||||||
|
opacity: 0.6
|
||||||
|
},
|
||||||
|
|
||||||
roomName: {
|
roomName: {
|
||||||
color: ColorPalette.white,
|
color: ColorPalette.white,
|
||||||
fontSize: 17,
|
fontSize: 17,
|
||||||
|
@ -112,8 +118,8 @@ export default {
|
||||||
},
|
},
|
||||||
|
|
||||||
roomNameWrapper: {
|
roomNameWrapper: {
|
||||||
flexDirection: 'row',
|
flexDirection: 'column',
|
||||||
justifyContent: 'center',
|
alignItems: 'center',
|
||||||
left: 0,
|
left: 0,
|
||||||
paddingHorizontal: 48,
|
paddingHorizontal: 48,
|
||||||
position: 'absolute',
|
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 { connect } from '../../../base/redux';
|
||||||
import { isToolboxVisible } from '../../../toolbox';
|
import { isToolboxVisible } from '../../../toolbox';
|
||||||
|
|
||||||
|
import ConferenceTimer from '../ConferenceTimer';
|
||||||
import ParticipantsCount from './ParticipantsCount';
|
import ParticipantsCount from './ParticipantsCount';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -51,6 +52,7 @@ class Subject extends Component<Props> {
|
||||||
<div className = { `subject ${_visible ? 'visible' : ''}` }>
|
<div className = { `subject ${_visible ? 'visible' : ''}` }>
|
||||||
<span className = 'subject-text'>{ _subject }</span>
|
<span className = 'subject-text'>{ _subject }</span>
|
||||||
{ _showParticipantCount && <ParticipantsCount /> }
|
{ _showParticipantCount && <ParticipantsCount /> }
|
||||||
|
<ConferenceTimer />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
// @flow
|
// @flow
|
||||||
|
|
||||||
export { default as Conference } from './Conference';
|
export { default as Conference } from './Conference';
|
||||||
|
export { default as renderConferenceTimer } from './ConferenceTimerDisplay';
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,5 @@
|
||||||
|
local conference_duration_component
|
||||||
|
= module:get_option_string(
|
||||||
|
"conference_duration_component", "conference_duration"..module.host);
|
||||||
|
|
||||||
|
module:add_identity("component", "conference_duration", conference_duration_component);
|
|
@ -0,0 +1,66 @@
|
||||||
|
local st = require "util.stanza";
|
||||||
|
local socket = require "socket";
|
||||||
|
local json = require "util.json";
|
||||||
|
local ext_events = module:require "ext_events";
|
||||||
|
local it = require "util.iterators";
|
||||||
|
|
||||||
|
-- we use async to detect Prosody 0.10 and earlier
|
||||||
|
local have_async = pcall(require, "util.async");
|
||||||
|
if not have_async then
|
||||||
|
module:log("warn", "conference duration will not work with Prosody version 0.10 or less.");
|
||||||
|
return;
|
||||||
|
end
|
||||||
|
|
||||||
|
local muc_component_host = module:get_option_string("muc_component");
|
||||||
|
if muc_component_host == nil then
|
||||||
|
log("error", "No muc_component specified. No muc to operate on!");
|
||||||
|
return;
|
||||||
|
end
|
||||||
|
|
||||||
|
log("info", "Starting conference duration timer for %s", muc_component_host);
|
||||||
|
|
||||||
|
function occupant_joined(event)
|
||||||
|
local room = event.room;
|
||||||
|
local occupant = event.occupant;
|
||||||
|
|
||||||
|
local participant_count = it.count(room:each_occupant());
|
||||||
|
|
||||||
|
if participant_count > 1 then
|
||||||
|
|
||||||
|
if room.created_timestamp == nil then
|
||||||
|
room.created_timestamp = os.time(os.date("!*t")) * 1000; -- Lua provides UTC time in seconds, so convert to milliseconds
|
||||||
|
end
|
||||||
|
|
||||||
|
local body_json = {};
|
||||||
|
body_json.type = 'conference_duration';
|
||||||
|
body_json.created_timestamp = room.created_timestamp;
|
||||||
|
|
||||||
|
local stanza = st.message({
|
||||||
|
from = module.host;
|
||||||
|
to = occupant.jid;
|
||||||
|
})
|
||||||
|
:tag("json-message", {xmlns='http://jitsi.org/jitmeet'})
|
||||||
|
:text(json.encode(body_json)):up();
|
||||||
|
|
||||||
|
room:route_stanza(stanza);
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- executed on every host added internally in prosody, including components
|
||||||
|
function process_host(host)
|
||||||
|
if host == muc_component_host then -- the conference muc component
|
||||||
|
module:log("info", "Hook to muc events on %s", host);
|
||||||
|
|
||||||
|
local muc_module = module:context(host)
|
||||||
|
muc_module:hook("muc-occupant-joined", occupant_joined, -1);
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if prosody.hosts[muc_component_host] == nil then
|
||||||
|
module:log("info", "No muc component found, will listen for it: %s", muc_component_host);
|
||||||
|
|
||||||
|
-- when a host or component is added
|
||||||
|
prosody.events.add_handler("host-activated", process_host);
|
||||||
|
else
|
||||||
|
process_host(muc_component_host);
|
||||||
|
end
|
Loading…
Reference in New Issue