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 (
-
+ 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 (
+
+ );
+
+};
+
+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 (
-
- )
- }
-
+ )
}
-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;
+};