bigbluebutton-Github/bigbluebutton-html5/imports/ui/components/timer/component.jsx

471 lines
11 KiB
React
Raw Normal View History

2023-05-16 03:46:44 +08:00
import React, { Component } from 'react';
2020-04-26 03:03:35 +08:00
import PropTypes from 'prop-types';
import { defineMessages, injectIntl } from 'react-intl';
import Service from './service';
import injectWbResizeEvent from '/imports/ui/components/presentation/resize-wrapper/component';
2024-02-17 00:32:27 +08:00
import Header from '/imports/ui/components/common/control-header/component';
2020-04-26 03:03:35 +08:00
import Styled from './styles';
const intlMessages = defineMessages({
hideTimerLabel: {
id: 'app.timer.hideTimerLabel',
description: 'Label for hiding timer button',
},
title: {
id: 'app.timer.title',
description: 'Title for timer',
},
stopwatch: {
id: 'app.timer.button.stopwatch',
description: 'Stopwatch switch button',
},
timer: {
id: 'app.timer.button.timer',
description: 'Timer switch button',
},
start: {
id: 'app.timer.button.start',
description: 'Timer start button',
},
stop: {
id: 'app.timer.button.stop',
description: 'Timer stop button',
},
reset: {
id: 'app.timer.button.reset',
description: 'Timer reset button',
},
2023-05-16 03:46:44 +08:00
hours: {
id: 'app.timer.hours',
description: 'Timer hours label',
},
minutes: {
id: 'app.timer.minutes',
description: 'Timer minutes label',
},
seconds: {
id: 'app.timer.seconds',
description: 'Timer seconds label',
},
songs: {
id: 'app.timer.songs',
description: 'Musics title label',
},
noTrack: {
id: 'app.timer.noTrack',
description: 'No music radio label',
},
track1: {
id: 'app.timer.track1',
description: 'Track 1 radio label',
},
track2: {
id: 'app.timer.track2',
description: 'Track 2 radio label',
},
track3: {
id: 'app.timer.track3',
description: 'Track 3 radio label',
},
2020-04-26 03:03:35 +08:00
});
const propTypes = {
intl: PropTypes.shape({
formatMessage: PropTypes.func.isRequired,
}).isRequired,
timer: PropTypes.shape({
stopwatch: PropTypes.bool,
running: PropTypes.bool,
time: PropTypes.string,
accumulated: PropTypes.number,
timestamp: PropTypes.number,
}).isRequired,
layoutContextDispatch: PropTypes.shape().isRequired,
timeOffset: PropTypes.number.isRequired,
isActive: PropTypes.bool.isRequired,
isModerator: PropTypes.bool.isRequired,
currentTrack: PropTypes.string.isRequired,
isResizing: PropTypes.bool.isRequired,
2020-04-26 03:03:35 +08:00
};
2023-05-16 03:46:44 +08:00
class Timer extends Component {
2020-04-26 03:03:35 +08:00
constructor(props) {
super(props);
2023-05-16 03:46:44 +08:00
this.timeRef = React.createRef();
this.interval = null;
this.updateTime = this.updateTime.bind(this);
}
componentDidMount() {
const { timer } = this.props;
const { running } = timer;
const { current } = this.timeRef;
if (current && running) {
this.interval = setInterval(this.updateTime, Service.getInterval());
}
}
componentDidUpdate(prevProps) {
const { timer } = this.props;
const { timer: prevTimer } = prevProps;
this.updateInterval(prevTimer, timer);
}
componentWillUnmount() {
clearInterval(this.interval);
}
2020-04-26 03:03:35 +08:00
handleControlClick() {
2024-01-19 04:20:28 +08:00
const {
timer, startTimer, stopTimer,
2024-01-19 04:20:28 +08:00
} = this.props;
2023-05-16 03:46:44 +08:00
if (timer.running) {
stopTimer();
2020-04-26 03:03:35 +08:00
} else {
2024-01-17 19:29:19 +08:00
startTimer();
2020-04-26 03:03:35 +08:00
}
}
2020-04-27 01:44:01 +08:00
handleOnHoursChange(event) {
2024-01-17 22:41:33 +08:00
const { timer, setHours } = this.props;
2020-04-27 01:44:01 +08:00
const { target } = event;
if (target && target.value) {
2022-02-02 04:05:34 +08:00
const hours = parseInt(target.value, 10);
2024-01-17 22:41:33 +08:00
setHours(hours, timer.time);
2020-04-27 01:44:01 +08:00
}
}
handleOnMinutesChange(event) {
2024-01-17 22:41:33 +08:00
const { timer, setMinutes } = this.props;
2020-04-27 01:44:01 +08:00
const { target } = event;
if (target && target.value) {
2022-02-02 04:05:34 +08:00
const minutes = parseInt(target.value, 10);
2024-01-17 22:41:33 +08:00
setMinutes(minutes, timer.time);
2020-04-27 01:44:01 +08:00
}
}
handleOnSecondsChange(event) {
2024-01-17 22:41:33 +08:00
const { timer, setSeconds } = this.props;
2020-04-27 01:44:01 +08:00
const { target } = event;
if (target && target.value) {
2022-02-02 04:05:34 +08:00
const seconds = parseInt(target.value, 10);
2024-01-17 22:41:33 +08:00
setSeconds(seconds, timer.time);
2020-04-27 01:44:01 +08:00
}
}
2020-04-26 03:03:35 +08:00
handleSwitchToStopwatch() {
2024-01-19 04:20:28 +08:00
const { timer, stopTimer, switchTimer } = this.props;
2020-04-26 03:03:35 +08:00
2023-05-16 03:46:44 +08:00
if (!timer.stopwatch) {
stopTimer();
2024-01-17 20:12:27 +08:00
switchTimer(true);
2020-04-26 03:03:35 +08:00
}
}
handleSwitchToTimer() {
2024-01-19 04:20:28 +08:00
const { timer, stopTimer, switchTimer } = this.props;
2020-04-26 03:03:35 +08:00
2023-05-16 03:46:44 +08:00
if (timer.stopwatch) {
stopTimer();
2024-01-17 20:12:27 +08:00
switchTimer(false);
2020-04-26 03:03:35 +08:00
}
}
2024-01-17 20:22:50 +08:00
handleOnTrackChange(event) {
const { setTrack } = this.props;
const { target } = event;
setTrack(target.value);
}
2022-02-02 04:06:22 +08:00
getTime() {
const {
timer,
timeOffset,
} = this.props;
const {
stopwatch,
running,
time,
accumulated,
timestamp,
} = timer;
const elapsedTime = Service.getElapsedTime(running, timestamp, timeOffset, accumulated);
let updatedTime;
if (stopwatch) {
updatedTime = elapsedTime;
} else {
updatedTime = Math.max(time - elapsedTime, 0);
}
2024-01-18 21:49:57 +08:00
return updatedTime;
}
getTimeString() {
const { timer } = this.props;
const { stopwatch } = timer;
const updatedTime = this.getTime();
2022-02-02 04:06:22 +08:00
return Service.getTimeAsString(updatedTime, stopwatch);
}
updateTime() {
const { current } = this.timeRef;
if (current) {
2024-01-18 21:49:57 +08:00
current.textContent = this.getTimeString();
2022-02-02 04:06:22 +08:00
}
}
updateInterval(prevTimer, timer) {
const { running } = timer;
const { running: prevRunning } = prevTimer;
if (!prevRunning && running) {
this.interval = setInterval(this.updateTime, Service.getInterval());
}
if (prevRunning && !running) {
clearInterval(this.interval);
}
}
2020-04-26 03:03:35 +08:00
renderControls() {
const {
intl,
2023-05-16 03:46:44 +08:00
timer,
2024-01-17 00:55:31 +08:00
timerReset,
2020-04-26 03:03:35 +08:00
} = this.props;
2023-05-16 03:46:44 +08:00
const { running } = timer;
2020-04-26 03:03:35 +08:00
const label = running ? intlMessages.stop : intlMessages.start;
2023-05-16 04:27:39 +08:00
const color = running ? 'danger' : 'primary';
2020-04-26 03:03:35 +08:00
return (
<Styled.TimerControls>
<Styled.TimerControlButton
2023-05-16 04:27:39 +08:00
color={color}
2020-04-26 03:03:35 +08:00
label={intl.formatMessage(label)}
2023-05-16 03:46:44 +08:00
onClick={() => this.handleControlClick()}
2023-10-24 20:57:08 +08:00
data-test="startStopTimer"
2020-04-26 03:03:35 +08:00
/>
<Styled.TimerControlButton
2023-07-14 21:43:32 +08:00
color="secondary"
2020-04-26 03:03:35 +08:00
label={intl.formatMessage(intlMessages.reset)}
2024-01-17 00:55:31 +08:00
onClick={() => timerReset()}
2023-10-26 04:29:52 +08:00
data-test="resetTimerStopWatch"
2020-04-26 03:03:35 +08:00
/>
</Styled.TimerControls>
);
}
renderSongSelectorRadios() {
const {
intl,
timer,
currentTrack,
} = this.props;
const {
stopwatch,
} = timer;
return (
<Styled.TimerSongsWrapper>
<Styled.TimerSongsTitle
stopwatch={stopwatch}
>
{intl.formatMessage(intlMessages.songs)}
</Styled.TimerSongsTitle>
<Styled.TimerTracks>
{Service.TRACKS.map((track) => (
<Styled.TimerTrackItem
key={track}
>
<label htmlFor={track}>
<input
type="radio"
name="track"
id={track}
value={track}
checked={currentTrack === track}
2024-01-17 20:22:50 +08:00
onChange={(event) => this.handleOnTrackChange(event)}
disabled={stopwatch}
/>
{intl.formatMessage(intlMessages[track])}
</label>
</Styled.TimerTrackItem>
))}
</Styled.TimerTracks>
</Styled.TimerSongsWrapper>
);
}
2020-04-26 03:03:35 +08:00
renderTimer() {
2023-05-16 03:46:44 +08:00
const {
intl,
timer,
} = this.props;
const {
time,
stopwatch,
} = timer;
2020-04-27 01:44:01 +08:00
2023-07-14 21:43:32 +08:00
if (stopwatch) return this.renderControls();
2020-04-27 01:44:01 +08:00
const timeArray = Service.getTimeAsString(time).split(':');
const hasHours = timeArray.length === 3;
const hours = hasHours ? timeArray[0] : '00';
const minutes = hasHours ? timeArray[1] : timeArray[0];
const seconds = hasHours ? timeArray[2] : timeArray[1];
return (
<div>
<Styled.StopwatchTime>
<Styled.StopwatchTimeInput>
2023-07-14 21:43:32 +08:00
<Styled.TimerInput
type="number"
disabled={stopwatch}
value={hours}
maxLength="2"
max={Service.getMaxHours()}
min="0"
onChange={(event) => this.handleOnHoursChange(event)}
2023-10-24 20:57:08 +08:00
data-test="hoursInput"
/>
<Styled.StopwatchTimeInputLabel>
{intl.formatMessage(intlMessages.hours)}
</Styled.StopwatchTimeInputLabel>
</Styled.StopwatchTimeInput>
2020-04-27 01:44:01 +08:00
<Styled.StopwatchTimeColon>:</Styled.StopwatchTimeColon>
<Styled.StopwatchTimeInput>
2023-07-14 21:43:32 +08:00
<Styled.TimerInput
type="number"
disabled={stopwatch}
value={minutes}
maxLength="2"
max="59"
min="0"
onChange={(event) => this.handleOnMinutesChange(event)}
2023-10-24 20:57:08 +08:00
data-test="minutesInput"
/>
<Styled.StopwatchTimeInputLabel>
{intl.formatMessage(intlMessages.minutes)}
</Styled.StopwatchTimeInputLabel>
</Styled.StopwatchTimeInput>
2020-04-27 01:44:01 +08:00
<Styled.StopwatchTimeColon>:</Styled.StopwatchTimeColon>
<Styled.StopwatchTimeInput>
2023-07-14 21:43:32 +08:00
<Styled.TimerInput
type="number"
disabled={stopwatch}
value={seconds}
maxLength="2"
max="59"
min="0"
onChange={(event) => this.handleOnSecondsChange(event)}
2023-10-24 20:57:08 +08:00
data-test="secondsInput"
/>
<Styled.StopwatchTimeInputLabel>
{intl.formatMessage(intlMessages.seconds)}
</Styled.StopwatchTimeInputLabel>
</Styled.StopwatchTimeInput>
2020-04-27 01:44:01 +08:00
</Styled.StopwatchTime>
{ Service.isMusicEnabled()
? this.renderSongSelectorRadios() : null}
2020-04-27 01:44:01 +08:00
{this.renderControls()}
</div>
);
2020-04-26 03:03:35 +08:00
}
renderContent() {
const {
intl,
isResizing,
2023-05-16 03:46:44 +08:00
timer,
2020-04-26 03:03:35 +08:00
} = this.props;
2023-05-16 03:46:44 +08:00
const { stopwatch } = timer;
2020-04-26 03:03:35 +08:00
return (
<Styled.TimerContent
isResizing={isResizing}
>
<Styled.TimerCurrent
aria-hidden
ref={this.timeRef}
2023-10-24 20:57:08 +08:00
data-test="timerCurrent"
>
2024-01-18 21:49:57 +08:00
{this.getTimeString()}
2023-05-16 03:46:44 +08:00
</Styled.TimerCurrent>
2020-04-26 03:03:35 +08:00
<Styled.TimerType>
<Styled.TimerSwitchButton
label={intl.formatMessage(intlMessages.stopwatch)}
2023-05-16 03:46:44 +08:00
onClick={() => this.handleSwitchToStopwatch()}
2023-07-14 21:43:32 +08:00
color={stopwatch ? 'primary' : 'secondary'}
2023-10-24 20:57:08 +08:00
data-test="stopwatch"
2020-04-26 03:03:35 +08:00
/>
<Styled.TimerSwitchButton
label={intl.formatMessage(intlMessages.timer)}
2023-05-16 03:46:44 +08:00
onClick={() => this.handleSwitchToTimer()}
2023-07-14 21:43:32 +08:00
color={!stopwatch ? 'primary' : 'secondary'}
2023-10-24 20:57:08 +08:00
data-test="timer"
2020-04-26 03:03:35 +08:00
/>
</Styled.TimerType>
2023-05-16 03:46:44 +08:00
{this.renderTimer()}
2020-04-26 03:03:35 +08:00
</Styled.TimerContent>
);
}
render() {
const {
intl,
isActive,
isModerator,
2020-04-27 01:44:01 +08:00
layoutContextDispatch,
2023-05-16 03:46:44 +08:00
timer,
2020-04-26 03:03:35 +08:00
} = this.props;
if (!isActive || !isModerator) {
Service.closePanel(layoutContextDispatch);
2020-04-26 03:03:35 +08:00
return null;
}
2023-05-16 03:46:44 +08:00
const { stopwatch } = timer;
const message = stopwatch ? intlMessages.stopwatch : intlMessages.timer;
2020-04-26 03:03:35 +08:00
return (
<Styled.TimerSidebarContent
data-test="timer"
>
2024-02-17 00:32:27 +08:00
<Header
leftButtonProps={{
onClick: () => { Service.closePanel(layoutContextDispatch); },
'aria-label': intl.formatMessage(intlMessages.hideTimerLabel),
label: intl.formatMessage(message),
}}
/>
2020-04-26 03:03:35 +08:00
{this.renderContent()}
</Styled.TimerSidebarContent>
);
}
2022-02-02 04:06:22 +08:00
}
2020-04-26 03:03:35 +08:00
Timer.propTypes = propTypes;
export default injectWbResizeEvent(injectIntl(Timer));