Skip to content

Instantly share code, notes, and snippets.

@walemust
Created September 13, 2020 12:02
Show Gist options
  • Save walemust/7d67444a0817e65a942f5495501fe82c to your computer and use it in GitHub Desktop.
Save walemust/7d67444a0817e65a942f5495501fe82c to your computer and use it in GitHub Desktop.
Fork Me! FCC: 25 + 5 Clock
#container
#app
// coded by @no-stack-dub-sack (github) / @no_stack_sub_sack (codepen)
/** NOTES:
/** Dependencies are React, ReactDOM, and
Accurate_Interval.js by Squuege (external script
to keep setInterval() from drifting over time &
thus ensuring timer goes off at correct mark).
/** Utilizes embedded <Audio> tag to ensure audio
plays when timer tab is inactive or browser is
minimized ( rather than new Audio().play() ).
/** Audio of this fashion not supported on most
mobile devices it would seem (bummer I know).
**/
// PROJECTOR SELECTOR FOR EXTERNAL TEST SCRIPT:
const projectName = '25-5-clock';
localStorage.setItem('example_project', '25 + 5 Clock');
// COMPONENTS:
class TimerLengthControl extends React.Component {
render() {
return (
<div className="length-control">
<div id={this.props.titleID}>
{this.props.title}
</div>
<button id={this.props.minID}
className="btn-level" value="-"
onClick={this.props.onClick}>
<i className="fa fa-arrow-down fa-2x"/>
</button>
<div id={this.props.lengthID} className="btn-level">
{this.props.length}
</div>
<button id={this.props.addID}
className="btn-level" value="+"
onClick={this.props.onClick}>
<i className="fa fa-arrow-up fa-2x"/>
</button>
</div>
)
}
};
class Timer extends React.Component {
constructor(props) {
super(props);
this.state = {
brkLength: 5,
seshLength: 25,
timerState: 'stopped',
timerType: 'Session',
timer: 1500,
intervalID: '',
alarmColor: {color: 'white'}
}
this.setBrkLength = this.setBrkLength.bind(this);
this.setSeshLength = this.setSeshLength.bind(this);
this.lengthControl = this.lengthControl.bind(this);
this.timerControl = this.timerControl.bind(this);
this.beginCountDown = this.beginCountDown.bind(this);
this.decrementTimer = this.decrementTimer.bind(this);
this.phaseControl = this.phaseControl.bind(this);
this.warning = this.warning.bind(this);
this.buzzer = this.buzzer.bind(this);
this.switchTimer = this.switchTimer.bind(this);
this.clockify = this.clockify.bind(this);
this.reset = this.reset.bind(this);
}
setBrkLength(e) {
this.lengthControl('brkLength', e.currentTarget.value,
this.state.brkLength, 'Session');
}
setSeshLength(e) {
this.lengthControl('seshLength', e.currentTarget.value,
this.state.seshLength, 'Break');
}
lengthControl(stateToChange, sign, currentLength, timerType) {
if (this.state.timerState == 'running') return;
if (this.state.timerType == timerType) {
if (sign == "-" && currentLength != 1 ) {
this.setState({[stateToChange]: currentLength - 1});
} else if (sign == "+" && currentLength != 60) {
this.setState({[stateToChange]: currentLength + 1});
}
} else {
if (sign == "-" && currentLength != 1 ) {
this.setState({[stateToChange]: currentLength - 1,
timer: currentLength * 60 - 60});
} else if (sign == "+" && currentLength != 60) {
this.setState({[stateToChange]: currentLength + 1,
timer: currentLength * 60 + 60});
}
}
}
timerControl() {
let control = this.state.timerState == 'stopped' ? (
this.beginCountDown(),
this.setState({timerState: 'running'})
) : (
this.setState({timerState: 'stopped'}),
this.state.intervalID && this.state.intervalID.cancel()
);
}
beginCountDown() {
this.setState({
intervalID: accurateInterval(() => {
this.decrementTimer();
this.phaseControl();
}, 1000)
})
}
decrementTimer() {
this.setState({timer: this.state.timer - 1});
}
phaseControl() {
let timer = this.state.timer;
this.warning(timer);
this.buzzer(timer);
if (timer < 0) {
this.state.timerType == 'Session' ? (
this.state.intervalID && this.state.intervalID.cancel(),
this.beginCountDown(),
this.switchTimer(this.state.brkLength * 60, 'Break')
) : (
this.state.intervalID && this.state.intervalID.cancel(),
this.beginCountDown(),
this.switchTimer(this.state.seshLength * 60, 'Session')
);
}
}
warning(_timer) {
let warn = _timer < 61 ?
this.setState({alarmColor: {color: '#a50d0d'}}) :
this.setState({alarmColor: {color: 'white'}});
}
buzzer(_timer) {
if (_timer === 0) {
this.audioBeep.play();
}
}
switchTimer(num, str) {
this.setState({
timer: num,
timerType: str,
alarmColor: {color: 'white'}
})
}
clockify() {
let minutes = Math.floor(this.state.timer / 60);
let seconds = this.state.timer - minutes * 60;
seconds = seconds < 10 ? '0' + seconds : seconds;
minutes = minutes < 10 ? '0' + minutes : minutes;
return minutes + ':' + seconds;
}
reset() {
this.setState({
brkLength: 5,
seshLength: 25,
timerState: 'stopped',
timerType: 'Session',
timer: 1500,
intervalID: '',
alarmColor: {color: 'white'}
});
this.state.intervalID && this.state.intervalID.cancel();
this.audioBeep.pause();
this.audioBeep.currentTime = 0;
}
render() {
return (
<div>
<div className="main-title">
25 + 5 Clock
</div>
<TimerLengthControl
titleID="break-label" minID="break-decrement"
addID="break-increment" lengthID="break-length"
title="Break Length" onClick={this.setBrkLength}
length={this.state.brkLength}/>
<TimerLengthControl
titleID="session-label" minID="session-decrement"
addID="session-increment" lengthID="session-length"
title="Session Length" onClick={this.setSeshLength}
length={this.state.seshLength}/>
<div className="timer" style={this.state.alarmColor}>
<div className="timer-wrapper">
<div id='timer-label'>
{this.state.timerType}
</div>
<div id='time-left'>
{this.clockify()}
</div>
</div>
</div>
<div className="timer-control">
<button id="start_stop" onClick={this.timerControl}>
<i className="fa fa-play fa-2x"/>
<i className="fa fa-pause fa-2x"/>
</button>
<button id="reset" onClick={this.reset}>
<i className="fa fa-refresh fa-2x"/>
</button>
</div>
<div className="author"> Designed and Coded by <br />
<a target="_blank" href="https://goo.gl/6NNLMG">
Peter Weinberg
</a>
</div>
<audio
id="beep"
preload="auto"
src="https://raw.githubusercontent.com/freeCodeCamp/cdn/master/build/testable-projects-fcc/audio/BeepSound.wav"
ref={(audio) => {
this.audioBeep = audio;
}}
/>
</div>
)
}
};
ReactDOM.render(<Timer />, document.getElementById('app'));
<script src="https://codepen.io/no_stack_dub_sack/pen/VKJGKd"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.3.2/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.3.1/react-dom.min.js"></script>
<script src="https://cdn.freecodecamp.org/testable-projects-fcc/v1/bundle.js"></script>
@font-face {
font-family: "Digital";
src: url("//db.onlinewebfonts.com/t/8e22783d707ad140bffe18b2a3812529.eot");
src: url("//db.onlinewebfonts.com/t/8e22783d707ad140bffe18b2a3812529.eot?#iefix") format("embedded-opentype"), url("//db.onlinewebfonts.com/t/8e22783d707ad140bffe18b2a3812529.woff2") format("woff2"), url("//db.onlinewebfonts.com/t/8e22783d707ad140bffe18b2a3812529.woff") format("woff"), url("//db.onlinewebfonts.com/t/8e22783d707ad140bffe18b2a3812529.ttf") format("truetype"), url("//db.onlinewebfonts.com/t/8e22783d707ad140bffe18b2a3812529.svg#Digital-7") format("svg");
}
body {
background: #1E555C;
color: white;
font-size: 30px;
text-align: center;
font-family: 'Righteous';
#container {
display: flex;
height: 100vh;
justify-content: center;
align-items: center;
.main-title {
font-size: 50px;
margin-bottom: 20px;
}
.length-control {
width: 250px;
}
button {
background: none;
outline: none;
border: none;
color: white;
cursor: pointer;
}
.btn-level,
.length-control {
display: inline-block;
.btn-level:nth-child(3) {
width: 40px;
}
}
.timer {
border: 7px solid #13353a;
margin: 20px auto 10px auto;
width: 270px;
height: 160px;
border-radius: 50px;
position: relative;
.timer-wrapper {
position: absolute;
width: 190px;
height: 110px;
left: 50%;
top: 50%;
margin-left: -95px;
margin-top: -57px;
}
#time-left {
font-family: digital;
font-size: 80px;
}
.timer-control button:active {
color: #13353a;
}
}
}
}
@media screen and (max-width: 500px) {
#container {
transform: scale(0.8);
-webkit-transform: scale(0.8);
-moz-transform: scale(0.8);
-ms-transform: scale(0.8);
-o-transform: scale(0.8);
}
}
.author {
text-align: center;
font-family: Share Tech Mono, sans;
margin-top: 15px;
font-size: 14px;
color: #a50d0d;;
a {
text-decoration: none;
color: #00264d;
line-height: 26px;
}
}
<link href="https://maxcdn.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css" rel="stylesheet" />
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment