mirror of
https://github.com/TrentSPalmer/fcc-challenges.git
synced 2025-08-23 10:13:57 -07:00
add pomodoro-clock-react
This commit is contained in:
98
pomodoro-clock-react/src/App.js
Normal file
98
pomodoro-clock-react/src/App.js
Normal file
@@ -0,0 +1,98 @@
|
||||
import React from 'react';
|
||||
import { connect } from "react-redux";
|
||||
|
||||
import PomodoroClock from './PomodoroClock';
|
||||
|
||||
import { colors } from './globals'
|
||||
|
||||
import { innerWindowWidthAction } from "./actions/innerWindowWidthAction";
|
||||
import { innerWindowHeightAction } from "./actions/innerWindowHeightAction";
|
||||
|
||||
const mapStateToProps = (state) => ({ ...state });
|
||||
|
||||
const mapDispatchToProps = (dispatch) => ({
|
||||
innerWindowWidthAction: (windowInnerWidth) => dispatch(innerWindowWidthAction(windowInnerWidth)),
|
||||
innerWindowHeightAction: (windowInnerHeight) => dispatch(innerWindowHeightAction(windowInnerHeight)),
|
||||
});
|
||||
|
||||
class App extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.handleResize = this.handleResize.bind(this);
|
||||
};
|
||||
|
||||
handleResize() {
|
||||
this.props.innerWindowWidthAction(window.innerWidth);
|
||||
this.props.innerWindowHeightAction(window.innerHeight);
|
||||
};
|
||||
|
||||
componentDidMount() {
|
||||
window.addEventListener('resize',this.handleResize);
|
||||
};
|
||||
|
||||
componentWillUnmount() {
|
||||
window.removeEventListener('resize',this.handleResize);
|
||||
};
|
||||
|
||||
render() {
|
||||
const height = this.props.innerWindowHeight;
|
||||
const width = this.props.innerWindowWidth;
|
||||
const gitHubLabelStyle = {
|
||||
backgroundColor: colors.prussianBlue,
|
||||
border: 'none',
|
||||
width: 149,
|
||||
height: 0,
|
||||
position: 'absolute',
|
||||
right: 0,
|
||||
};
|
||||
|
||||
const attachmentStyle = {
|
||||
position: 'absolute',
|
||||
right: 0,
|
||||
height: 149,
|
||||
width: 149,
|
||||
};
|
||||
|
||||
const style = {
|
||||
textAlign: 'center',
|
||||
backgroundColor: colors.prussianBlue,
|
||||
height: '100vh',
|
||||
width: '100vw',
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
color: colors.ivoryBlack,
|
||||
overFlow: 'hidden',
|
||||
fontFamily: 'Ubuntu sans-serif',
|
||||
fontSize: 48,
|
||||
};
|
||||
|
||||
if (width > height && height < 400) {
|
||||
gitHubLabelStyle['width'] = 99;
|
||||
attachmentStyle['width'] = 99;
|
||||
attachmentStyle['height'] = 99;
|
||||
} else if (height > width) {
|
||||
gitHubLabelStyle['width'] = 99;
|
||||
attachmentStyle['width'] = 99;
|
||||
attachmentStyle['height'] = 99;
|
||||
}
|
||||
|
||||
return (
|
||||
<div style={style}>
|
||||
<a href="https://github.com/TrentSPalmer/fcc-challenges/tree/gh-pages/pomodoro-clock-react"
|
||||
style={gitHubLabelStyle}
|
||||
target="_blank" rel="noopener noreferrer">
|
||||
<img
|
||||
src="https://github.blog/wp-content/uploads/2008/12/forkme_right_white_ffffff.png?resize=149%2C149"
|
||||
className="size-full"
|
||||
style={attachmentStyle}
|
||||
alt="Fork me on GitHub"
|
||||
data-recalc-dims="1">
|
||||
</img>
|
||||
</a>
|
||||
<PomodoroClock />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
};
|
||||
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(App);
|
9
pomodoro-clock-react/src/App.test.js
Normal file
9
pomodoro-clock-react/src/App.test.js
Normal file
@@ -0,0 +1,9 @@
|
||||
import React from 'react';
|
||||
import { render } from '@testing-library/react';
|
||||
import App from './App';
|
||||
|
||||
test('renders learn react link', () => {
|
||||
const { getByText } = render(<App />);
|
||||
const linkElement = getByText(/learn react/i);
|
||||
expect(linkElement).toBeInTheDocument();
|
||||
});
|
BIN
pomodoro-clock-react/src/BeepSound.ogg
Normal file
BIN
pomodoro-clock-react/src/BeepSound.ogg
Normal file
Binary file not shown.
31
pomodoro-clock-react/src/Break.js
Normal file
31
pomodoro-clock-react/src/Break.js
Normal file
@@ -0,0 +1,31 @@
|
||||
import React from 'react';
|
||||
|
||||
import { colors } from './globals'
|
||||
import BreakControls from './BreakControls';
|
||||
|
||||
const Break = () => {
|
||||
const style = {
|
||||
height: '100%',
|
||||
width: '50%',
|
||||
};
|
||||
|
||||
const breakTextStyle = {
|
||||
height: '50%',
|
||||
width: '90%',
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
justifyContent: 'center',
|
||||
textAlign: 'right',
|
||||
marginRight: '10%',
|
||||
color: colors.goldLeaf,
|
||||
};
|
||||
|
||||
return(
|
||||
<div style={style}>
|
||||
<div id="break-label" style={breakTextStyle}>Break Length</div>
|
||||
<BreakControls />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default Break;
|
124
pomodoro-clock-react/src/BreakControls.js
Normal file
124
pomodoro-clock-react/src/BreakControls.js
Normal file
@@ -0,0 +1,124 @@
|
||||
import React from 'react';
|
||||
import { connect } from "react-redux";
|
||||
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
import { faArrowCircleDown } from "@fortawesome/free-solid-svg-icons";
|
||||
import { faArrowCircleUp } from "@fortawesome/free-solid-svg-icons";
|
||||
import { colors,makeClock } from './globals'
|
||||
|
||||
import { breakLengthAction } from "./actions/breakLengthAction";
|
||||
import { clockAction } from "./actions/clockAction";
|
||||
|
||||
const mapDispatchToProps = (dispatch) => ({
|
||||
breakLengthAction: (breakLength) => dispatch(breakLengthAction(breakLength)),
|
||||
clockAction: (time) => dispatch(clockAction(time)),
|
||||
});
|
||||
|
||||
const mapStateToProps = (state) => ({ ...state });
|
||||
|
||||
class BreakControls extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.handleDownArrowMouseEvent = this.handleDownArrowMouseEvent.bind(this);
|
||||
this.handleUpArrowMouseEvent = this.handleUpArrowMouseEvent.bind(this);
|
||||
this.incrementBreak = this.incrementBreak.bind(this);
|
||||
this.decrementBreak = this.decrementBreak.bind(this);
|
||||
this.state = {
|
||||
downColor: colors.ivoryBlack,
|
||||
upColor: colors.ivoryBlack,
|
||||
};
|
||||
};
|
||||
|
||||
incrementBreak() {
|
||||
if (this.props.breakLength < 60) {
|
||||
const newBreakLength = this.props.breakLength + 1;
|
||||
if (!this.props.clockIsRunning) {
|
||||
this.props.breakLengthAction(newBreakLength);
|
||||
if (this.props.timer === 'Break') {
|
||||
this.props.clockAction(makeClock(newBreakLength * 60));
|
||||
}
|
||||
} else {
|
||||
if (this.props.timer === 'Session') {
|
||||
this.props.breakLengthAction(newBreakLength);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
decrementBreak() {
|
||||
if (this.props.breakLength > 1) {
|
||||
const newBreakLength = this.props.breakLength - 1;
|
||||
if (!this.props.clockIsRunning) {
|
||||
this.props.breakLengthAction(newBreakLength);
|
||||
if (this.props.timer === 'Break') {
|
||||
this.props.clockAction(makeClock(newBreakLength * 60));
|
||||
}
|
||||
} else {
|
||||
if (this.props.timer === 'Session') {
|
||||
this.props.breakLengthAction(newBreakLength);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
handleDownArrowMouseEvent(e) {
|
||||
if (e.type === 'mousedown' || e.type === 'touchstart') {
|
||||
this.setState({downColor: colors.goldLeaf});
|
||||
} else if (e.type === 'mouseup' || e.type === 'touchend') {
|
||||
this.setState({downColor: colors.ivoryBlack});
|
||||
}
|
||||
};
|
||||
|
||||
handleUpArrowMouseEvent(e) {
|
||||
if (e.type === 'mousedown' || e.type === 'touchstart') {
|
||||
this.setState({upColor: colors.goldLeaf});
|
||||
} else if (e.type === 'mouseup' || e.type === 'touchend') {
|
||||
this.setState({upColor: colors.ivoryBlack});
|
||||
}
|
||||
};
|
||||
|
||||
render() {
|
||||
const style = {
|
||||
height: '50%',
|
||||
width: '75%',
|
||||
display: 'flex',
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'flex-end',
|
||||
marginRight: '25%',
|
||||
fontSize: 32,
|
||||
};
|
||||
|
||||
const breakStyle = {
|
||||
width: 50,
|
||||
}
|
||||
|
||||
const downArrowStyle = {
|
||||
color: this.state.downColor,
|
||||
}
|
||||
|
||||
const upArrowStyle = {
|
||||
color: this.state.upColor,
|
||||
}
|
||||
|
||||
return(
|
||||
<div style={style}>
|
||||
<div id='break-decrement' style={downArrowStyle} onClick={this.decrementBreak}
|
||||
onTouchStart={this.handleDownArrowMouseEvent} onTouchEnd={this.handleDownArrowMouseEvent}
|
||||
onMouseDown={this.handleDownArrowMouseEvent} onMouseUp={this.handleDownArrowMouseEvent}>
|
||||
<FontAwesomeIcon icon={faArrowCircleDown}/>
|
||||
</div>
|
||||
|
||||
<p id='break-length' style={breakStyle}>{this.props.breakLength}</p>
|
||||
|
||||
<div id='break-increment' style={upArrowStyle} onClick={this.incrementBreak}
|
||||
onTouchStart={this.handleUpArrowMouseEvent} onTouchEnd={this.handleUpArrowMouseEvent}
|
||||
onMouseDown={this.handleUpArrowMouseEvent} onMouseUp={this.handleUpArrowMouseEvent}>
|
||||
<FontAwesomeIcon icon={faArrowCircleUp}/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
};
|
||||
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(BreakControls);
|
42
pomodoro-clock-react/src/Clock.js
Normal file
42
pomodoro-clock-react/src/Clock.js
Normal file
@@ -0,0 +1,42 @@
|
||||
import React from 'react';
|
||||
import { connect } from "react-redux";
|
||||
|
||||
import { colors } from './globals'
|
||||
|
||||
const mapStateToProps = (state) => ({ ...state });
|
||||
|
||||
class Clock extends React.Component {
|
||||
render() {
|
||||
const clockStyle = {
|
||||
height: '40%',
|
||||
width: '100%',
|
||||
}
|
||||
|
||||
const timerLabelStyle = {
|
||||
height: '30%',
|
||||
width: '100%',
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
justifyContent: 'flex-end',
|
||||
color: colors.goldLeaf,
|
||||
}
|
||||
|
||||
const timerStyle = {
|
||||
height: '70%',
|
||||
width: '100%',
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
justifyContent: 'center',
|
||||
fontSize: 96,
|
||||
}
|
||||
|
||||
return(
|
||||
<div style={clockStyle}>
|
||||
<div id='timer-label' style={timerLabelStyle}>{this.props.timer}</div>
|
||||
<div id='time-left' style={timerStyle}>{this.props.clock}</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
};
|
||||
|
||||
export default connect(mapStateToProps)(Clock);
|
186
pomodoro-clock-react/src/Controls.js
Normal file
186
pomodoro-clock-react/src/Controls.js
Normal file
@@ -0,0 +1,186 @@
|
||||
import React from 'react';
|
||||
import { connect } from "react-redux";
|
||||
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
import { faPlayCircle } from "@fortawesome/free-solid-svg-icons";
|
||||
import { faPauseCircle } from "@fortawesome/free-solid-svg-icons";
|
||||
import { faSyncAlt } from "@fortawesome/free-solid-svg-icons";
|
||||
import { colors,makeClock } from './globals'
|
||||
|
||||
import { clockIsRunningAction } from "./actions/clockIsRunningAction";
|
||||
import { zeroTimeAction } from "./actions/zeroTimeAction";
|
||||
import { clockAction } from "./actions/clockAction";
|
||||
import { timerAction } from "./actions/timerAction";
|
||||
import { sessionLengthAction } from "./actions/sessionLengthAction";
|
||||
import { breakLengthAction } from "./actions/breakLengthAction";
|
||||
import BeepSoundOgg from './BeepSound.ogg';
|
||||
|
||||
const mapDispatchToProps = (dispatch) => ({
|
||||
clockIsRunningAction: (clockIsRunning) => dispatch(clockIsRunningAction(clockIsRunning)),
|
||||
zeroTimeAction: (zeroTime) => dispatch(zeroTimeAction(zeroTime)),
|
||||
clockAction: (time) => dispatch(clockAction(time)),
|
||||
timerAction: (timer) => dispatch(timerAction(timer)),
|
||||
sessionLengthAction: (sessionLength) => dispatch(sessionLengthAction(sessionLength)),
|
||||
breakLengthAction: (breakLength) => dispatch(breakLengthAction(breakLength)),
|
||||
});
|
||||
|
||||
const mapStateToProps = (state) => ({ ...state });
|
||||
|
||||
class Controls extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.handleMouseEventPlay = this.handleMouseEventPlay.bind(this);
|
||||
this.handleMouseEventPause = this.handleMouseEventPause.bind(this);
|
||||
this.handleMouseEventReset = this.handleMouseEventReset.bind(this);
|
||||
this.handlePlay = this.handlePlay.bind(this);
|
||||
this.handleReset = this.handleReset.bind(this);
|
||||
this.state = {
|
||||
playColor: colors.ivoryBlack,
|
||||
pauseColor: colors.ivoryBlack,
|
||||
resetColor: colors.ivoryBlack,
|
||||
sound: '',
|
||||
};
|
||||
};
|
||||
|
||||
handlePlay() {
|
||||
if (this.props.clockIsRunning) {
|
||||
this.props.clockIsRunningAction(false);
|
||||
} else {
|
||||
this.props.clockIsRunningAction(true);
|
||||
const now = new Date();
|
||||
const nowSeconds = Math.floor(now.getTime() / 1000);
|
||||
const remainingTime = this.props.clock.split(':');
|
||||
const remainingSeconds = (60 * parseInt(remainingTime[0])) + parseInt(remainingTime[1]);
|
||||
this.props.zeroTimeAction(nowSeconds + remainingSeconds);
|
||||
const self = this;
|
||||
let refreshClock = setInterval(function(){
|
||||
if (!self.props.clockIsRunning) {
|
||||
clearInterval(refreshClock);
|
||||
} else {
|
||||
const now = new Date();
|
||||
const nowSeconds = Math.floor(now.getTime() / 1000);
|
||||
if (nowSeconds <= self.props.zeroTime) {
|
||||
const remainingTime = self.props.zeroTime - nowSeconds;
|
||||
const newClock = makeClock(remainingTime);
|
||||
if (newClock !== self.props.clock) {
|
||||
self.props.clockAction(newClock);
|
||||
}
|
||||
} else {
|
||||
let remainingTime = self.props.breakLength * 60;
|
||||
if (self.props.timer === 'Session') {
|
||||
self.props.timerAction('Break');
|
||||
} else {
|
||||
self.props.timerAction('Session');
|
||||
remainingTime = self.props.sessionLength * 60;
|
||||
}
|
||||
self.props.zeroTimeAction(nowSeconds + remainingTime);
|
||||
self.props.clockAction(makeClock(remainingTime));
|
||||
}
|
||||
}
|
||||
},1000);
|
||||
}
|
||||
};
|
||||
|
||||
handleReset() {
|
||||
if (this.props.clockIsRunning) {
|
||||
this.props.clockIsRunningAction(false);
|
||||
}
|
||||
if (this.props.timer === 'Break') {
|
||||
this.props.timerAction('Session');
|
||||
}
|
||||
if (this.props.sessionLength !== 25) {
|
||||
this.props.sessionLengthAction(25);
|
||||
}
|
||||
if (this.props.breakLength !== 5) {
|
||||
this.props.breakLengthAction(5);
|
||||
}
|
||||
if (this.props.clock !== '25:00') {
|
||||
this.props.clockAction('25:00');
|
||||
}
|
||||
this.state.sound.pause();
|
||||
const oldSound = this.state.sound;
|
||||
oldSound.currentTime = 0;
|
||||
};
|
||||
|
||||
componentDidUpdate(prevProps) {
|
||||
if (this.props.clock === '00:00') {
|
||||
this.state.sound.play();
|
||||
}
|
||||
};
|
||||
|
||||
handleMouseEventPlay(e) {
|
||||
if (e.type === 'mousedown' || e.type === 'touchstart') {
|
||||
this.setState({playColor: colors.goldLeaf});
|
||||
} else if (e.type === 'mouseup' || e.type === 'touchend') {
|
||||
this.setState({playColor: colors.ivoryBlack});
|
||||
}
|
||||
};
|
||||
|
||||
handleMouseEventPause(e) {
|
||||
if (e.type === 'mousedown' || e.type === 'touchstart') {
|
||||
this.setState({pauseColor: colors.goldLeaf});
|
||||
} else if (e.type === 'mouseup' || e.type === 'touchend') {
|
||||
this.setState({pauseColor: colors.ivoryBlack});
|
||||
}
|
||||
};
|
||||
|
||||
handleMouseEventReset(e) {
|
||||
if (e.type === 'mousedown' || e.type === 'touchstart') {
|
||||
this.setState({resetColor: colors.goldLeaf});
|
||||
} else if (e.type === 'mouseup' || e.type === 'touchend') {
|
||||
this.setState({resetColor: colors.ivoryBlack});
|
||||
}
|
||||
};
|
||||
|
||||
componentDidMount() {
|
||||
this.setState({sound: document.getElementById('beep')});
|
||||
};
|
||||
|
||||
render() {
|
||||
const controlsStyle = {
|
||||
height: '20%',
|
||||
width: '100%',
|
||||
display: 'flex',
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
fontSize: 48,
|
||||
}
|
||||
|
||||
const playStyle = {
|
||||
color: this.state.playColor,
|
||||
};
|
||||
|
||||
const pauseStyle = {
|
||||
color: this.state.pauseColor,
|
||||
width: 150,
|
||||
};
|
||||
|
||||
const resetStyle = {
|
||||
color: this.state.resetColor,
|
||||
};
|
||||
|
||||
return(
|
||||
<div style={controlsStyle}>
|
||||
<div id='start_stop' style={playStyle} onClick={this.handlePlay}
|
||||
onMouseDown={this.handleMouseEventPlay} onMouseUp={this.handleMouseEventPlay}
|
||||
onTouchStart={this.handleMouseEventPlay} onTouchEnd={this.handleMouseEventPlay}>
|
||||
<FontAwesomeIcon icon={faPlayCircle}/>
|
||||
</div>
|
||||
|
||||
<FontAwesomeIcon style={pauseStyle} onMouseDown={this.handleMouseEventPause}
|
||||
onMouseUp={this.handleMouseEventPause} onTouchStart={this.handleMouseEventPause}
|
||||
onTouchEnd={this.handleMouseEventPause} onClick={() => {this.props.clockIsRunningAction(false)}} icon={faPauseCircle}/>
|
||||
|
||||
<div id='reset' style={resetStyle} onClick={this.handleReset}
|
||||
onMouseDown={this.handleMouseEventReset} onMouseUp={this.handleMouseEventReset}
|
||||
onTouchStart={this.handleMouseEventReset} onTouchEnd={this.handleMouseEventReset}>
|
||||
<FontAwesomeIcon icon={faSyncAlt}/>
|
||||
</div>
|
||||
|
||||
<audio id='beep' preload='auto' src={BeepSoundOgg} type='audio/ogg'></audio>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
};
|
||||
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(Controls);
|
69
pomodoro-clock-react/src/PomodoroClock.js
Normal file
69
pomodoro-clock-react/src/PomodoroClock.js
Normal file
@@ -0,0 +1,69 @@
|
||||
import React from 'react';
|
||||
import { connect } from "react-redux";
|
||||
import { colors } from './globals'
|
||||
|
||||
import Top from './Top';
|
||||
import Clock from './Clock';
|
||||
import Controls from './Controls';
|
||||
|
||||
const mapStateToProps = (state) => ({ ...state });
|
||||
|
||||
class PomodoroClock extends React.Component {
|
||||
render() {
|
||||
const width = this.props.innerWindowWidth;
|
||||
const height = this.props.innerWindowHeight;
|
||||
const eightyPercentWidth = Math.round(width * 0.8);
|
||||
const eightyPercentHeight = Math.round(height * 0.8);
|
||||
const maxHeight = 500;
|
||||
const maxWidth = 800;
|
||||
const style = {
|
||||
margin: 'auto',
|
||||
border: '3px solid',
|
||||
borderColor: colors.ivoryBlack,
|
||||
zIndex: '1',
|
||||
backgroundColor: colors.skyBlue,
|
||||
borderRadius: 10,
|
||||
height: eightyPercentHeight < maxHeight ? eightyPercentHeight : maxHeight,
|
||||
width: eightyPercentWidth < maxWidth ? eightyPercentWidth : maxWidth,
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
justifyContent: 'center',
|
||||
fontSize: 32,
|
||||
userSelect: 'none',
|
||||
MozUserSelect: 'none',
|
||||
WebkitUserSelect: 'none',
|
||||
};
|
||||
|
||||
const clockContainerStyle = {
|
||||
height: '100%',
|
||||
width: '100%',
|
||||
};
|
||||
|
||||
if (width > height && height < 400) {
|
||||
style['height'] = '96vh';
|
||||
style['width'] = '75vw';
|
||||
style['margin'] = '1vh auto 3vh auto';
|
||||
style['fontSize'] = 24;
|
||||
} else if (height > width) {
|
||||
style['height'] = Math.round(height * 0.75);
|
||||
style['maxHeight'] = maxHeight;
|
||||
clockContainerStyle['height'] = width > style['height'] ? style['height'] : width;
|
||||
clockContainerStyle['maxHeight'] = maxHeight;
|
||||
style['width'] = width - 6;
|
||||
style['maxWidth'] = maxWidth;
|
||||
style['fontSize'] = 24;
|
||||
}
|
||||
|
||||
return(
|
||||
<div style={style}>
|
||||
<div style={clockContainerStyle}>
|
||||
<Top />
|
||||
<Clock />
|
||||
<Controls />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
};
|
||||
|
||||
export default connect(mapStateToProps)(PomodoroClock);
|
31
pomodoro-clock-react/src/Session.js
Normal file
31
pomodoro-clock-react/src/Session.js
Normal file
@@ -0,0 +1,31 @@
|
||||
import React from 'react';
|
||||
|
||||
import { colors } from './globals'
|
||||
import SessionControls from './SessionControls';
|
||||
|
||||
const Session = () => {
|
||||
const style = {
|
||||
height: '100%',
|
||||
width: '50%',
|
||||
};
|
||||
|
||||
const sessionTextStyle = {
|
||||
height: '50%',
|
||||
width: '90%',
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
justifyContent: 'center',
|
||||
textAlign: 'left',
|
||||
marginLeft: '10%',
|
||||
color: colors.goldLeaf,
|
||||
};
|
||||
|
||||
return(
|
||||
<div style={style}>
|
||||
<div id="session-label" style={sessionTextStyle}>Session Length</div>
|
||||
<SessionControls />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default Session;
|
124
pomodoro-clock-react/src/SessionControls.js
Normal file
124
pomodoro-clock-react/src/SessionControls.js
Normal file
@@ -0,0 +1,124 @@
|
||||
import React from 'react';
|
||||
import { connect } from "react-redux";
|
||||
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
import { faArrowCircleDown } from "@fortawesome/free-solid-svg-icons";
|
||||
import { faArrowCircleUp } from "@fortawesome/free-solid-svg-icons";
|
||||
import { colors,makeClock } from './globals'
|
||||
|
||||
import { sessionLengthAction } from "./actions/sessionLengthAction";
|
||||
import { clockAction } from "./actions/clockAction";
|
||||
|
||||
const mapDispatchToProps = (dispatch) => ({
|
||||
sessionLengthAction: (sessionLength) => dispatch(sessionLengthAction(sessionLength)),
|
||||
clockAction: (time) => dispatch(clockAction(time)),
|
||||
});
|
||||
|
||||
const mapStateToProps = (state) => ({ ...state });
|
||||
|
||||
class SessionControls extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.handleDownArrowMouseEvent = this.handleDownArrowMouseEvent.bind(this);
|
||||
this.handleUpArrowMouseEvent = this.handleUpArrowMouseEvent.bind(this);
|
||||
this.incrementSession = this.incrementSession.bind(this);
|
||||
this.decrementSession = this.decrementSession.bind(this);
|
||||
this.state = {
|
||||
downColor: colors.ivoryBlack,
|
||||
upColor: colors.ivoryBlack,
|
||||
};
|
||||
};
|
||||
|
||||
incrementSession() {
|
||||
if (this.props.sessionLength < 60) {
|
||||
const newSessionLength = this.props.sessionLength + 1;
|
||||
if (!this.props.clockIsRunning) {
|
||||
this.props.sessionLengthAction(newSessionLength);
|
||||
if (this.props.timer === 'Session') {
|
||||
this.props.clockAction(makeClock(newSessionLength * 60));
|
||||
}
|
||||
} else {
|
||||
if (this.props.timer === 'Break') {
|
||||
this.props.sessionLengthAction(newSessionLength);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
decrementSession() {
|
||||
if (this.props.sessionLength > 1) {
|
||||
const newSessionLength = this.props.sessionLength - 1;
|
||||
if (!this.props.clockIsRunning) {
|
||||
this.props.sessionLengthAction(newSessionLength);
|
||||
if (this.props.timer === 'Session') {
|
||||
this.props.clockAction(makeClock(newSessionLength * 60));
|
||||
}
|
||||
} else {
|
||||
if (this.props.timer === 'Break') {
|
||||
this.props.sessionLengthAction(newSessionLength);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
handleDownArrowMouseEvent(e) {
|
||||
if (e.type === 'mousedown' || e.type === 'touchstart') {
|
||||
this.setState({downColor: colors.goldLeaf});
|
||||
} else if (e.type === 'mouseup' || e.type === 'touchend') {
|
||||
this.setState({downColor: colors.ivoryBlack});
|
||||
}
|
||||
};
|
||||
|
||||
handleUpArrowMouseEvent(e) {
|
||||
if (e.type === 'mousedown' || e.type === 'touchstart') {
|
||||
this.setState({upColor: colors.goldLeaf});
|
||||
} else if (e.type === 'mouseup' || e.type === 'touchend') {
|
||||
this.setState({upColor: colors.ivoryBlack});
|
||||
}
|
||||
};
|
||||
|
||||
render() {
|
||||
const style = {
|
||||
height: '50%',
|
||||
width: '75%',
|
||||
display: 'flex',
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'flex-start',
|
||||
marginLeft: '25%',
|
||||
fontSize: 32,
|
||||
};
|
||||
|
||||
const breakStyle = {
|
||||
width: 50,
|
||||
}
|
||||
|
||||
const downArrowStyle = {
|
||||
color: this.state.downColor,
|
||||
}
|
||||
|
||||
const upArrowStyle = {
|
||||
color: this.state.upColor,
|
||||
}
|
||||
|
||||
return(
|
||||
<div style={style}>
|
||||
<div id='session-decrement' style={downArrowStyle} onClick={this.decrementSession}
|
||||
onTouchStart={this.handleDownArrowMouseEvent} onTouchEnd={this.handleDownArrowMouseEvent}
|
||||
onMouseDown={this.handleDownArrowMouseEvent} onMouseUp={this.handleDownArrowMouseEvent}>
|
||||
<FontAwesomeIcon icon={faArrowCircleDown}/>
|
||||
</div>
|
||||
|
||||
<p id='session-length' style={breakStyle}>{this.props.sessionLength}</p>
|
||||
|
||||
<div id='session-increment' style={upArrowStyle} onClick={this.incrementSession}
|
||||
onTouchStart={this.handleUpArrowMouseEvent} onTouchEnd={this.handleUpArrowMouseEvent}
|
||||
onMouseDown={this.handleUpArrowMouseEvent} onMouseUp={this.handleUpArrowMouseEvent}>
|
||||
<FontAwesomeIcon icon={faArrowCircleUp}/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
};
|
||||
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(SessionControls);
|
39
pomodoro-clock-react/src/Top.js
Normal file
39
pomodoro-clock-react/src/Top.js
Normal file
@@ -0,0 +1,39 @@
|
||||
import React from 'react';
|
||||
|
||||
import Break from './Break';
|
||||
import Session from './Session';
|
||||
|
||||
const Top = () => {
|
||||
|
||||
const style = {
|
||||
height: '40%',
|
||||
width: '100%',
|
||||
}
|
||||
|
||||
const titleStyle = {
|
||||
height: '50%',
|
||||
width: '100%',
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
justifyContent: 'center',
|
||||
fontSize: 48,
|
||||
}
|
||||
|
||||
const breakAndSessionStyle = {
|
||||
height: '50%',
|
||||
width: '100%',
|
||||
display: 'flex',
|
||||
}
|
||||
|
||||
return(
|
||||
<div style={style}>
|
||||
<div style={titleStyle}>Pomodoro Clock</div>
|
||||
<div style={breakAndSessionStyle}>
|
||||
<Break />
|
||||
<Session />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default Top;
|
8
pomodoro-clock-react/src/actions/breakLengthAction.js
Normal file
8
pomodoro-clock-react/src/actions/breakLengthAction.js
Normal file
@@ -0,0 +1,8 @@
|
||||
export const SETBREAKLENGTH = "SETBREAKLENGTH";
|
||||
|
||||
export const breakLengthAction = (breakLength) => {
|
||||
return {
|
||||
type: SETBREAKLENGTH,
|
||||
breakLength: breakLength,
|
||||
};
|
||||
};
|
8
pomodoro-clock-react/src/actions/clockAction.js
Normal file
8
pomodoro-clock-react/src/actions/clockAction.js
Normal file
@@ -0,0 +1,8 @@
|
||||
export const SETCLOCK = "SETCLOCK";
|
||||
|
||||
export const clockAction = (time) => {
|
||||
return {
|
||||
type: SETCLOCK,
|
||||
time: time,
|
||||
};
|
||||
};
|
8
pomodoro-clock-react/src/actions/clockIsRunningAction.js
Normal file
8
pomodoro-clock-react/src/actions/clockIsRunningAction.js
Normal file
@@ -0,0 +1,8 @@
|
||||
export const SETCLOCKISRUNNING = "SETCLOCKISRUNNING";
|
||||
|
||||
export const clockIsRunningAction = (clockIsRunning) => {
|
||||
return {
|
||||
type: SETCLOCKISRUNNING,
|
||||
clockIsRunning: clockIsRunning,
|
||||
};
|
||||
};
|
@@ -0,0 +1,8 @@
|
||||
export const SETWINDOWINNERHEIGHT = "SETWINDOWINNERHEIGHT";
|
||||
|
||||
export const innerWindowHeightAction = (windowInnerHeight) => {
|
||||
return {
|
||||
type: SETWINDOWINNERHEIGHT,
|
||||
windowInnerHeight: windowInnerHeight,
|
||||
};
|
||||
};
|
@@ -0,0 +1,8 @@
|
||||
export const SETWINDOWINNERWIDTH = "SETWINDOWINNERWIDTH";
|
||||
|
||||
export const innerWindowWidthAction = (windowInnerWidth) => {
|
||||
return {
|
||||
type: SETWINDOWINNERWIDTH,
|
||||
windowInnerWidth: windowInnerWidth,
|
||||
};
|
||||
};
|
8
pomodoro-clock-react/src/actions/sessionLengthAction.js
Normal file
8
pomodoro-clock-react/src/actions/sessionLengthAction.js
Normal file
@@ -0,0 +1,8 @@
|
||||
export const SETSESSIONLENGTH = "SETSESSIONLENGTH";
|
||||
|
||||
export const sessionLengthAction = (sessionLength) => {
|
||||
return {
|
||||
type: SETSESSIONLENGTH,
|
||||
sessionLength: sessionLength,
|
||||
};
|
||||
};
|
8
pomodoro-clock-react/src/actions/timerAction.js
Normal file
8
pomodoro-clock-react/src/actions/timerAction.js
Normal file
@@ -0,0 +1,8 @@
|
||||
export const SETTIMER = "SETTIMER";
|
||||
|
||||
export const timerAction = (timer) => {
|
||||
return {
|
||||
type: SETTIMER,
|
||||
timer: timer,
|
||||
};
|
||||
};
|
8
pomodoro-clock-react/src/actions/zeroTimeAction.js
Normal file
8
pomodoro-clock-react/src/actions/zeroTimeAction.js
Normal file
@@ -0,0 +1,8 @@
|
||||
export const SETZEROTIME = "SETZEROTIME";
|
||||
|
||||
export const zeroTimeAction = (zeroTime) => {
|
||||
return {
|
||||
type: SETZEROTIME,
|
||||
zeroTime: zeroTime,
|
||||
};
|
||||
};
|
14
pomodoro-clock-react/src/globals.js
Normal file
14
pomodoro-clock-react/src/globals.js
Normal file
@@ -0,0 +1,14 @@
|
||||
export const colors = {
|
||||
prussianBlue: '#0b3c5d',
|
||||
skyBlue: '#328cc1',
|
||||
goldLeaf: '#d9b310',
|
||||
ivoryBlack: '#1d2731',
|
||||
};
|
||||
|
||||
export const makeClock = (remainingTime) => {
|
||||
const remainingSeconds = remainingTime % 60;
|
||||
const remainingMinutes = Math.floor(remainingTime / 60);
|
||||
const minutes = remainingMinutes > 9 ? remainingMinutes.toString() : '0' + remainingMinutes.toString() ;
|
||||
const seconds = remainingSeconds > 9 ? remainingSeconds.toString() : '0' + remainingSeconds.toString() ;
|
||||
return minutes + ':' + seconds;
|
||||
};
|
14
pomodoro-clock-react/src/index.css
Normal file
14
pomodoro-clock-react/src/index.css
Normal file
@@ -0,0 +1,14 @@
|
||||
body {
|
||||
margin: 0;
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
|
||||
'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
|
||||
sans-serif;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
code {
|
||||
font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',
|
||||
monospace;
|
||||
}
|
13
pomodoro-clock-react/src/index.css.bak
Normal file
13
pomodoro-clock-react/src/index.css.bak
Normal file
@@ -0,0 +1,13 @@
|
||||
body {
|
||||
margin: 0;
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
|
||||
'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
|
||||
sans-serif;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
||||
|
||||
code {
|
||||
font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',
|
||||
monospace;
|
||||
}
|
20
pomodoro-clock-react/src/index.js
Normal file
20
pomodoro-clock-react/src/index.js
Normal file
@@ -0,0 +1,20 @@
|
||||
import React from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
import { Provider } from "react-redux";
|
||||
import store from "./store";
|
||||
|
||||
import './index.css';
|
||||
import App from './App';
|
||||
import * as serviceWorker from './serviceWorker';
|
||||
|
||||
ReactDOM.render(
|
||||
<Provider store={store}>
|
||||
<App />
|
||||
</Provider>,
|
||||
document.getElementById('root')
|
||||
);
|
||||
|
||||
// If you want your app to work offline and load faster, you can change
|
||||
// unregister() to register() below. Note this comes with some pitfalls.
|
||||
// Learn more about service workers: https://bit.ly/CRA-PWA
|
||||
serviceWorker.unregister();
|
14
pomodoro-clock-react/src/reducers/breakLengthReducer.js
Normal file
14
pomodoro-clock-react/src/reducers/breakLengthReducer.js
Normal file
@@ -0,0 +1,14 @@
|
||||
import { SETBREAKLENGTH } from "../actions/breakLengthAction";
|
||||
|
||||
export default (state, action) => {
|
||||
if (!state) {
|
||||
state = 5;
|
||||
}
|
||||
|
||||
switch (action.type) {
|
||||
case SETBREAKLENGTH:
|
||||
return action.breakLength;
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
};
|
14
pomodoro-clock-react/src/reducers/clockIsRunningReducer.js
Normal file
14
pomodoro-clock-react/src/reducers/clockIsRunningReducer.js
Normal file
@@ -0,0 +1,14 @@
|
||||
import { SETCLOCKISRUNNING } from "../actions/clockIsRunningAction";
|
||||
|
||||
export default (state, action) => {
|
||||
if (!state) {
|
||||
state = false;
|
||||
}
|
||||
|
||||
switch (action.type) {
|
||||
case SETCLOCKISRUNNING:
|
||||
return action.clockIsRunning;
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
};
|
14
pomodoro-clock-react/src/reducers/clockReducer.js
Normal file
14
pomodoro-clock-react/src/reducers/clockReducer.js
Normal file
@@ -0,0 +1,14 @@
|
||||
import { SETCLOCK } from "../actions/clockAction";
|
||||
|
||||
export default (state, action) => {
|
||||
if (!state) {
|
||||
state = '25:00';
|
||||
}
|
||||
|
||||
switch (action.type) {
|
||||
case SETCLOCK:
|
||||
return action.time;
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
};
|
@@ -0,0 +1,14 @@
|
||||
import { SETWINDOWINNERHEIGHT } from "../actions/innerWindowHeightAction";
|
||||
|
||||
export default (state, action) => {
|
||||
if (!state) {
|
||||
state = window.innerHeight;
|
||||
}
|
||||
|
||||
switch (action.type) {
|
||||
case SETWINDOWINNERHEIGHT:
|
||||
return action.windowInnerHeight;
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
};
|
14
pomodoro-clock-react/src/reducers/innerWindowWidthReducer.js
Normal file
14
pomodoro-clock-react/src/reducers/innerWindowWidthReducer.js
Normal file
@@ -0,0 +1,14 @@
|
||||
import { SETWINDOWINNERWIDTH } from "../actions/innerWindowWidthAction";
|
||||
|
||||
export default (state, action) => {
|
||||
if (!state) {
|
||||
state = window.innerWidth;
|
||||
}
|
||||
|
||||
switch (action.type) {
|
||||
case SETWINDOWINNERWIDTH:
|
||||
return action.windowInnerWidth;
|
||||
default:
|
||||
return state;
|
||||
};
|
||||
};
|
20
pomodoro-clock-react/src/reducers/rootReducer.js
Normal file
20
pomodoro-clock-react/src/reducers/rootReducer.js
Normal file
@@ -0,0 +1,20 @@
|
||||
import { combineReducers } from "redux";
|
||||
import innerWindowWidthReducer from "./innerWindowWidthReducer";
|
||||
import innerWindowHeightReducer from "./innerWindowHeightReducer";
|
||||
import breakLengthReducer from "./breakLengthReducer";
|
||||
import sessionLengthReducer from "./sessionLengthReducer";
|
||||
import timerReducer from "./timerReducer";
|
||||
import clockReducer from "./clockReducer";
|
||||
import clockIsRunningReducer from "./clockIsRunningReducer";
|
||||
import zeroTimeReducer from "./zeroTimeReducer";
|
||||
|
||||
export default combineReducers({
|
||||
innerWindowWidth: innerWindowWidthReducer,
|
||||
innerWindowHeight: innerWindowHeightReducer,
|
||||
breakLength: breakLengthReducer,
|
||||
sessionLength: sessionLengthReducer,
|
||||
timer: timerReducer,
|
||||
clock: clockReducer,
|
||||
clockIsRunning: clockIsRunningReducer,
|
||||
zeroTime: zeroTimeReducer,
|
||||
});
|
14
pomodoro-clock-react/src/reducers/sessionLengthReducer.js
Normal file
14
pomodoro-clock-react/src/reducers/sessionLengthReducer.js
Normal file
@@ -0,0 +1,14 @@
|
||||
import { SETSESSIONLENGTH } from "../actions/sessionLengthAction";
|
||||
|
||||
export default (state, action) => {
|
||||
if (!state) {
|
||||
state = 25;
|
||||
}
|
||||
|
||||
switch (action.type) {
|
||||
case SETSESSIONLENGTH:
|
||||
return action.sessionLength;
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
};
|
14
pomodoro-clock-react/src/reducers/timerReducer.js
Normal file
14
pomodoro-clock-react/src/reducers/timerReducer.js
Normal file
@@ -0,0 +1,14 @@
|
||||
import { SETTIMER } from "../actions/timerAction";
|
||||
|
||||
export default (state, action) => {
|
||||
if (!state) {
|
||||
state = 'Session';
|
||||
}
|
||||
|
||||
switch (action.type) {
|
||||
case SETTIMER:
|
||||
return action.timer;
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
};
|
14
pomodoro-clock-react/src/reducers/zeroTimeReducer.js
Normal file
14
pomodoro-clock-react/src/reducers/zeroTimeReducer.js
Normal file
@@ -0,0 +1,14 @@
|
||||
import { SETZEROTIME } from "../actions/zeroTimeAction";
|
||||
|
||||
export default (state, action) => {
|
||||
if (!state) {
|
||||
state = 0;
|
||||
}
|
||||
|
||||
switch (action.type) {
|
||||
case SETZEROTIME:
|
||||
return action.zeroTime;
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
};
|
141
pomodoro-clock-react/src/serviceWorker.js
Normal file
141
pomodoro-clock-react/src/serviceWorker.js
Normal file
@@ -0,0 +1,141 @@
|
||||
// This optional code is used to register a service worker.
|
||||
// register() is not called by default.
|
||||
|
||||
// This lets the app load faster on subsequent visits in production, and gives
|
||||
// it offline capabilities. However, it also means that developers (and users)
|
||||
// will only see deployed updates on subsequent visits to a page, after all the
|
||||
// existing tabs open on the page have been closed, since previously cached
|
||||
// resources are updated in the background.
|
||||
|
||||
// To learn more about the benefits of this model and instructions on how to
|
||||
// opt-in, read https://bit.ly/CRA-PWA
|
||||
|
||||
const isLocalhost = Boolean(
|
||||
window.location.hostname === 'localhost' ||
|
||||
// [::1] is the IPv6 localhost address.
|
||||
window.location.hostname === '[::1]' ||
|
||||
// 127.0.0.0/8 are considered localhost for IPv4.
|
||||
window.location.hostname.match(
|
||||
/^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/
|
||||
)
|
||||
);
|
||||
|
||||
export function register(config) {
|
||||
if (process.env.NODE_ENV === 'production' && 'serviceWorker' in navigator) {
|
||||
// The URL constructor is available in all browsers that support SW.
|
||||
const publicUrl = new URL(process.env.PUBLIC_URL, window.location.href);
|
||||
if (publicUrl.origin !== window.location.origin) {
|
||||
// Our service worker won't work if PUBLIC_URL is on a different origin
|
||||
// from what our page is served on. This might happen if a CDN is used to
|
||||
// serve assets; see https://github.com/facebook/create-react-app/issues/2374
|
||||
return;
|
||||
}
|
||||
|
||||
window.addEventListener('load', () => {
|
||||
const swUrl = `${process.env.PUBLIC_URL}/service-worker.js`;
|
||||
|
||||
if (isLocalhost) {
|
||||
// This is running on localhost. Let's check if a service worker still exists or not.
|
||||
checkValidServiceWorker(swUrl, config);
|
||||
|
||||
// Add some additional logging to localhost, pointing developers to the
|
||||
// service worker/PWA documentation.
|
||||
navigator.serviceWorker.ready.then(() => {
|
||||
console.log(
|
||||
'This web app is being served cache-first by a service ' +
|
||||
'worker. To learn more, visit https://bit.ly/CRA-PWA'
|
||||
);
|
||||
});
|
||||
} else {
|
||||
// Is not localhost. Just register service worker
|
||||
registerValidSW(swUrl, config);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function registerValidSW(swUrl, config) {
|
||||
navigator.serviceWorker
|
||||
.register(swUrl)
|
||||
.then(registration => {
|
||||
registration.onupdatefound = () => {
|
||||
const installingWorker = registration.installing;
|
||||
if (installingWorker == null) {
|
||||
return;
|
||||
}
|
||||
installingWorker.onstatechange = () => {
|
||||
if (installingWorker.state === 'installed') {
|
||||
if (navigator.serviceWorker.controller) {
|
||||
// At this point, the updated precached content has been fetched,
|
||||
// but the previous service worker will still serve the older
|
||||
// content until all client tabs are closed.
|
||||
console.log(
|
||||
'New content is available and will be used when all ' +
|
||||
'tabs for this page are closed. See https://bit.ly/CRA-PWA.'
|
||||
);
|
||||
|
||||
// Execute callback
|
||||
if (config && config.onUpdate) {
|
||||
config.onUpdate(registration);
|
||||
}
|
||||
} else {
|
||||
// At this point, everything has been precached.
|
||||
// It's the perfect time to display a
|
||||
// "Content is cached for offline use." message.
|
||||
console.log('Content is cached for offline use.');
|
||||
|
||||
// Execute callback
|
||||
if (config && config.onSuccess) {
|
||||
config.onSuccess(registration);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('Error during service worker registration:', error);
|
||||
});
|
||||
}
|
||||
|
||||
function checkValidServiceWorker(swUrl, config) {
|
||||
// Check if the service worker can be found. If it can't reload the page.
|
||||
fetch(swUrl, {
|
||||
headers: { 'Service-Worker': 'script' },
|
||||
})
|
||||
.then(response => {
|
||||
// Ensure service worker exists, and that we really are getting a JS file.
|
||||
const contentType = response.headers.get('content-type');
|
||||
if (
|
||||
response.status === 404 ||
|
||||
(contentType != null && contentType.indexOf('javascript') === -1)
|
||||
) {
|
||||
// No service worker found. Probably a different app. Reload the page.
|
||||
navigator.serviceWorker.ready.then(registration => {
|
||||
registration.unregister().then(() => {
|
||||
window.location.reload();
|
||||
});
|
||||
});
|
||||
} else {
|
||||
// Service worker found. Proceed as normal.
|
||||
registerValidSW(swUrl, config);
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
console.log(
|
||||
'No internet connection found. App is running in offline mode.'
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
export function unregister() {
|
||||
if ('serviceWorker' in navigator) {
|
||||
navigator.serviceWorker.ready
|
||||
.then(registration => {
|
||||
registration.unregister();
|
||||
})
|
||||
.catch(error => {
|
||||
console.error(error.message);
|
||||
});
|
||||
}
|
||||
}
|
5
pomodoro-clock-react/src/setupTests.js
Normal file
5
pomodoro-clock-react/src/setupTests.js
Normal file
@@ -0,0 +1,5 @@
|
||||
// jest-dom adds custom jest matchers for asserting on DOM nodes.
|
||||
// allows you to do things like:
|
||||
// expect(element).toHaveTextContent(/react/i)
|
||||
// learn more: https://github.com/testing-library/jest-dom
|
||||
import '@testing-library/jest-dom/extend-expect';
|
6
pomodoro-clock-react/src/store.js
Normal file
6
pomodoro-clock-react/src/store.js
Normal file
@@ -0,0 +1,6 @@
|
||||
import { createStore } from "redux";
|
||||
import rootReducer from "./reducers/rootReducer";
|
||||
|
||||
const store = createStore(rootReducer);
|
||||
|
||||
export default store;
|
Reference in New Issue
Block a user