diff --git a/package-lock.json b/package-lock.json index 52c14fe..662fa8e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "tomadoro", - "version": "1.4.1", + "version": "1.5.1", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/package.json b/package.json index 1e79a0f..cc344ab 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "tomadoro", "description": "tomadoro", - "version": "1.5.1", + "version": "1.6.0", "private": true, "engines": { "npm": "6.4.1", @@ -9,6 +9,7 @@ }, "dependencies": { "bootstrap": "^4.3.1", + "prop-types": "^15.7.2", "react": "^16.11.0", "react-dom": "^16.11.0", "react-scripts": "^3.2.0", diff --git a/screenshot.png b/screenshot.png index 34f802b..a8d5d0a 100644 Binary files a/screenshot.png and b/screenshot.png differ diff --git a/src/App.css b/src/App.css deleted file mode 100644 index f304220..0000000 --- a/src/App.css +++ /dev/null @@ -1,57 +0,0 @@ -@import url('https://fonts.googleapis.com/css?family=Fredoka+One'); - -body { - background-color: #2e2929; /* e74c3c f04d3b 272222 */ - user-select: none; - font-family: 'Fredoka One', cursive; - color: white; - margin: 0; - padding: 0; - -webkit-font-smoothing: antialiased; - -moz-osx-font-smoothing: grayscale; -} - -.title { - font-weight: bold; -} - -.App { - text-align: center; - color: #131313; -} - -.App-header { - display: flex; - min-height: 5vh; - flex-direction: column; - text-align: 'left'; - align-items: center; - justify-content: center; - font-size: 44px; - color: white; - text-shadow: 0 2px 4px #909090; /* 909090 */ - margin-bottom: 6px; -} - -a { - color: #f1fb61; -} - -a:hover { - color: #fddec0; -} - -.footer { - font-family: Arial, Helvetica, sans-serif; - margin-top: 8px; - color: #ffffff; - text-shadow: 0 2px 3px #a3a3a3; -} - - -.timer { - font-size: 110px; - font-weight: normal; - text-shadow: 0 2px 3px #8a8888; /*0 0 1px #4b4b4b, 0 0 5px rgb(193, 194, 190); a3a3a3*/ - color: white; -} \ No newline at end of file diff --git a/src/App.js b/src/App.js deleted file mode 100644 index 5d3babd..0000000 --- a/src/App.js +++ /dev/null @@ -1,180 +0,0 @@ -import React, { Component } from 'react'; -import { Container, Row, Col } from 'reactstrap'; -import Logo from './components/Logo'; -import Box from './components/Box'; -import Notification from './components/Notification'; -import './App.css'; - -class App extends Component { - constructor(props) { - super(props); - - this.appName = 'tomadoro'; - this.pomodoroSeconds = 1500; - this.breakSeconds = 300; - - this.state = { - startClickNotification: false, // TODO: refactoring identificativo - seconds: 0, - started: false, - break: false, - sendNotification: false - }; - - // binding - this.startTimer = this.startTimer.bind(this); - this.stopTimer = this.stopTimer.bind(this); - this.resetTimer = this.resetTimer.bind(this); - this.terminatedTimer = this.terminatedTimer.bind(this); - this.pomodoroMode = this.pomodoroMode.bind(this); - this.breakMode = this.breakMode.bind(this); - this.switchMode = this.switchMode.bind(this); - this.formatMinute = this.formatMinute.bind(this); - this.handleNotification = this.handleNotification.bind(this); - } - - componentWillMount() { - this.pomodoroMode(); - } - - formatMinute(s) { - return (s - (s %= 60)) / 60 + (9 < s ? ':' : ':0') + s; - } - - tick() { - this.setState(state => ({ - seconds: state.seconds - 1 - })); - - document.title = `(${this.formatMinute(this.state.seconds)}) ${this.appName}`; - - if (this.state.seconds === 0) { - this.stopTimer(); - this.terminatedTimer(); - } - } - - startTimer() { - this.setState({ - started: true, - startClickNotification: true - }); - - this.interval = setInterval(() => this.tick(), 1000); - } - - stopTimer() { - this.setState({ - started: false - }); - - clearInterval(this.interval); - } - - resetTimer() { - if (this.state.break) { - this.breakMode(); - } else { - this.pomodoroMode(); - } - - document.title = this.appName; - } - - terminatedTimer() { - this.setState({ - sendNotification: true - }); - - this.switchMode(); - } - - pomodoroMode() { - this.setState({ - seconds: this.pomodoroSeconds, - break: false - }); - } - - breakMode() { - this.setState({ - seconds: this.breakSeconds, - break: true - }); - } - - switchMode() { // TODO: refactoring naming - if (this.state.break && !this.state.started) { - this.pomodoroMode(); - } else if (!this.state.break && !this.state.started) { - this.breakMode(); - } - } - - handleNotification(flag) { - this.setState({ - sendNotification: flag - }); - } - - render() { - return ( -
-
- tomadoro -
- - - - - - - - - - -

- {this.formatMinute(this.state.seconds)} -

- -
- - - - - - -
- - { // notification - this.state.startClickNotification ? ( - ) - : ''} - -
-

- tomadoro{` `} - by Francesco Esposito -

-
-
- ); - } -} - -export default App; diff --git a/src/logo.svg b/src/assets/logo.svg similarity index 100% rename from src/logo.svg rename to src/assets/logo.svg diff --git a/src/sound.mp3 b/src/assets/sound.mp3 similarity index 100% rename from src/sound.mp3 rename to src/assets/sound.mp3 diff --git a/src/components/Box.css b/src/components/Box.css deleted file mode 100644 index 982d197..0000000 --- a/src/components/Box.css +++ /dev/null @@ -1,16 +0,0 @@ -.button { - font-size: 16px; - font-weight: lighter; - height: 50px; - border-radius: 30px; - width: 100%; - margin: 4px 0px 4px 0px; -} - -.box { - padding: 6px; - background-color: white; - border-radius: 6px; - border-bottom: 16px solid #c84132; - box-shadow: 0 2px 3px #8a8888; -} \ No newline at end of file diff --git a/src/components/Box.js b/src/components/Box.js index 95a5e7a..23e536b 100644 --- a/src/components/Box.js +++ b/src/components/Box.js @@ -1,27 +1,37 @@ -import React, { Component } from 'react'; +import React from 'react'; import { Button, Row, Col } from 'reactstrap'; -import './Box.css'; +import PropTypes from 'prop-types'; -class Box extends Component { - constructor(props) { - super(props); - this.state = {}; - } +const box = props => { + const buttonStyle = { + fontSize: '16px', + fontWeight: 'lighter', + height: '50px', + borderRadius: '30px', + width: '100%', + margin: '4px 0px 4px 0px' + }; - render() { - return ( -
+ const boxStyle = { + padding: '6px', + backgroundColor: 'white', + borderRadius: '6px', + borderBottom: '16px solid #c84132', + boxShadow: '0 2px 3px #8a8888' + }; + + return ( +
@@ -29,30 +39,35 @@ class Box extends Component {
- ); - } - } - - export default Box; \ No newline at end of file + ); +} + +box.propTypes = { + startButton: PropTypes.func.isRequired, + stopButton: PropTypes.func.isRequired, + resetButton: PropTypes.func.isRequired, + isStarted: PropTypes.bool.isRequired, + seconds: PropTypes.number.isRequired +}; + +export default box; \ No newline at end of file diff --git a/src/components/Footer.js b/src/components/Footer.js new file mode 100644 index 0000000..c703ae1 --- /dev/null +++ b/src/components/Footer.js @@ -0,0 +1,22 @@ +import React from 'react'; + +const footer = () => { + const style = { + fontFamily: 'Arial, Helvetica, sans-serif', + marginTop: '8px', + color: '#ffffff', + textShadow: '0 2px 3px #a3a3a3' + }; + + return ( +
+

+ tomadoro{` `} + by Francesco Esposito +

+
+ ); + +}; + +export default footer; \ No newline at end of file diff --git a/src/components/Headbar.js b/src/components/Headbar.js new file mode 100644 index 0000000..1d7f253 --- /dev/null +++ b/src/components/Headbar.js @@ -0,0 +1,25 @@ +import React from 'react'; + +const headbar = () => { + const style = { + display: 'flex', + minHeight: '5vh', + flexDirection: 'column', + textAlign: 'left', + alignItems: 'center', + justifyContent: 'center', + fontSize: '44px', + color: 'white', + textShadow: '0 2px 4px #909090', + marginBottom: '6px' + }; + + return ( +
+ tomadoro +
+ ); +}; + + +export default headbar; \ No newline at end of file diff --git a/src/components/Logo.js b/src/components/Logo.js index d8b3162..a984945 100644 --- a/src/components/Logo.js +++ b/src/components/Logo.js @@ -1,25 +1,26 @@ -import React, { Component } from 'react'; -import logoSVG from '../logo.svg'; +import React from 'react'; +import PropTypes from 'prop-types'; + +import logoSVG from '../assets/logo.svg'; import './Logo.css'; -class Logo extends Component { - constructor(props) { - super(props); - this.defaultClassName = 'App-logo-rotation'; - } +const logo = props => { + const logoRotationClass = 'App-logo-rotation'; - render() { - return ( - Tomato - ) - } - + ) } -export default Logo; \ No newline at end of file +logo.propTypes = { + isStarted: PropTypes.bool.isRequired, + click: PropTypes.func.isRequired +}; + +export default logo; \ No newline at end of file diff --git a/src/components/Timer.js b/src/components/Timer.js new file mode 100644 index 0000000..5d8f4e7 --- /dev/null +++ b/src/components/Timer.js @@ -0,0 +1,27 @@ +import React from "react"; +import PropTypes from 'prop-types'; + +import { formatMinute } from '../utils'; + +const timer = props => { + const style = { + fontSize: "110px", + fontWeight: "normal", + color: "white", + textShadow: "0 2px 3px #8a8888" + }; + + return ( +
+ { formatMinute(props.seconds) } +
+ ) + ; +}; + +timer.propTypes = { + seconds: PropTypes.number.isRequired +}; + + +export default timer; diff --git a/src/containers/App.css b/src/containers/App.css new file mode 100644 index 0000000..6b3aa37 --- /dev/null +++ b/src/containers/App.css @@ -0,0 +1,22 @@ +@import url('https://fonts.googleapis.com/css?family=Fredoka+One'); + +body { + background-color: #2e2929; + text-align: center; + color: #131313; + user-select: none; + font-family: 'Fredoka One', cursive; + color: white; + margin: 0; + padding: 0; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} + +a { + color: #f1fb61; +} + +a:hover { + color: #fddec0; +} diff --git a/src/containers/App.js b/src/containers/App.js new file mode 100644 index 0000000..8294205 --- /dev/null +++ b/src/containers/App.js @@ -0,0 +1,152 @@ +import React, { Component } from 'react'; +import { Container, Row, Col } from 'reactstrap'; + +import Notification from './Notification'; +import Headbar from '../components/Headbar'; +import Timer from '../components/Timer'; +import Logo from '../components/Logo'; +import Box from '../components/Box'; +import Footer from '../components/Footer'; +import { formatMinute } from '../utils'; + +import './App.css'; + +class App extends Component { + constructor(props) { + super(props); + + this.APP_NAME = 'tomadoro'; + this.POMODORO_SECONDS = 1500; + this.BREAK_SECONDS = 300; + + this.state = { + seconds: this.POMODORO_SECONDS, + started: false, + break: false, + activePopupNotification: false, + sendNotificationFlag: false + }; + } + + tick = () => { + this.setState(state => ({ seconds: state.seconds - 1})); + + document.title = `(${formatMinute(this.state.seconds)}) ${this.APP_NAME}`; + + if (this.state.seconds === 0) { + this.stopTimer(); + this.finishedTimer(); + } + }; + + startTimer = () => { + this.setState({ started: true, activePopupNotification: true }); + + this.interval = setInterval(() => this.tick(), 1000); + } + + stopTimer = () => { + this.setState({ started: false}); + + clearInterval(this.interval); + } + + resetTimer = () => { + if (this.state.break) { + this.breakMode(); + } else { + this.pomodoroMode(); + } + + document.title = this.APP_NAME; + } + + finishedTimer = () => { + this.setState({ + sendNotificationFlag: true + }); + + this.changeMode(); + } + + changeMode = () => { + if (this.state.break && !this.state.started) { + this.pomodoroMode(); + } else if (!this.state.break && !this.state.started) { + this.breakMode(); + } + } + + pomodoroMode = () => { + this.setState({ + seconds: this.POMODORO_SECONDS, + break: false + }); + } + + breakMode = () => { + this.setState({ + seconds: this.BREAK_SECONDS, + break: true + }); + } + + handleNotification = flag => { + this.setState({ + sendNotificationFlag: flag + }); + } + + render() { + let notification = null; + + if (this.state.activePopupNotification) { + notification = ( + + ); + } + + return ( +
+ {notification} + + + + + + + + + + + + + + + + + + + +
+ + + +
+ ); + } +} + +export default App; \ No newline at end of file diff --git a/src/App.test.js b/src/containers/App.test.js similarity index 100% rename from src/App.test.js rename to src/containers/App.test.js diff --git a/src/components/Notification.js b/src/containers/Notification.js similarity index 97% rename from src/components/Notification.js rename to src/containers/Notification.js index 617a6eb..fdf3ebb 100644 --- a/src/components/Notification.js +++ b/src/containers/Notification.js @@ -1,6 +1,6 @@ import React, { Component } from 'react'; import WebNotification from 'react-web-notification'; -import sound from '../sound.mp3'; +import sound from '../assets/sound.mp3'; class Notification extends Component { constructor(props) { diff --git a/src/index.js b/src/index.js index 5d9961d..e047d30 100644 --- a/src/index.js +++ b/src/index.js @@ -1,7 +1,7 @@ import React from 'react'; import ReactDOM from 'react-dom'; import 'bootstrap/dist/css/bootstrap.min.css'; -import App from './App'; +import App from './containers/App'; import * as serviceWorker from './serviceWorker'; ReactDOM.render(, document.getElementById('root')); diff --git a/src/utils.js b/src/utils.js new file mode 100644 index 0000000..eb65d12 --- /dev/null +++ b/src/utils.js @@ -0,0 +1,3 @@ +export const formatMinute = s => { + return (s - (s %= 60)) / 60 + (9 < s ? ":" : ":0") + s; +};