From 7b6c0866071ab80259d5d08238164b1f49862ebb Mon Sep 17 00:00:00 2001 From: dmrib Date: Tue, 7 Apr 2020 08:02:15 -0300 Subject: [PATCH 01/40] Implement websocket connection --- src/App.js | 47 +++++++++++++++++++++++++++++++++++++++++- src/config/config.json | 4 ++++ 2 files changed, 50 insertions(+), 1 deletion(-) create mode 100644 src/config/config.json diff --git a/src/App.js b/src/App.js index bf0bedd..2a71a68 100644 --- a/src/App.js +++ b/src/App.js @@ -1,11 +1,16 @@ import React from 'react'; +import { useState, useRef, useEffect } from 'react'; import Content from './components/Content'; import Header from './components/Header'; import Footer from './components/Footer'; +import config from './config/config.json'; import './App.css'; import 'antd/dist/antd.css'; +const CONNECTION_ADDRESS = `ws://${config['HOST']}:${config['PORT']}` + + /** * App components aggregator. * @@ -13,9 +18,49 @@ import 'antd/dist/antd.css'; */ function App() { + // component state hooks + const socket = useRef(null); + const [socketState, setSocketState] = useState(0); + + + /** + * I am called when this component is mounted. + */ + useEffect(() => + { + // create websocket connection + socket.current = new WebSocket(CONNECTION_ADDRESS); + + // on connection opened event callback + socket.current.onopen = (event) => + { + console.log('Websocket connection established.') + setSocketState(socket.current.readyState); + } + + // on connection closed event callback + socket.current.onclose = (event) => + { + console.log("Websocket connection closed."); + setSocketState(socket.current.readyState); + } + + // on websocket error event callback + socket.current.onerror = (event) => + { + console.log('Websocket error: ', {event}); + setSocketState(socket.current.readyState); + } + + // cleanup on lifecycle end + return () => socket.current.close(); + + }, []); + + return (
-
+
diff --git a/src/config/config.json b/src/config/config.json new file mode 100644 index 0000000..008458e --- /dev/null +++ b/src/config/config.json @@ -0,0 +1,4 @@ +{ + "HOST": "localhost", + "PORT": 8765 +} From bf48563087dcfeeb8c4372df64c449c7c93bdcd7 Mon Sep 17 00:00:00 2001 From: dmrib Date: Tue, 7 Apr 2020 08:11:07 -0300 Subject: [PATCH 02/40] Define connection status style --- src/components/Header/Header.css | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/components/Header/Header.css b/src/components/Header/Header.css index 45e331a..c106775 100644 --- a/src/components/Header/Header.css +++ b/src/components/Header/Header.css @@ -39,3 +39,10 @@ align-items: center; margin-right: 5%; } + +.connection-status { + height: 15px; + width: 15px; + margin-right: 10px; + border-radius: 50%; +} From c5fe5da0c2c7226cfbd49b1d25c5585fca40b422 Mon Sep 17 00:00:00 2001 From: dmrib Date: Tue, 7 Apr 2020 08:23:01 -0300 Subject: [PATCH 03/40] Create connection status indicator --- .../Header/StatusIndicator/index.js | 52 ++++++++++++++++++ src/components/Header/index.js | 53 ++++++++++++++++++- 2 files changed, 103 insertions(+), 2 deletions(-) create mode 100644 src/components/Header/StatusIndicator/index.js diff --git a/src/components/Header/StatusIndicator/index.js b/src/components/Header/StatusIndicator/index.js new file mode 100644 index 0000000..632966c --- /dev/null +++ b/src/components/Header/StatusIndicator/index.js @@ -0,0 +1,52 @@ +import React from 'react'; +import { Popover, Typography } from 'antd'; +import PropTypes from 'prop-types'; + + +/** + * Websocket connection status indicator component. + * + * @component + */ +function StatusIndicator({color, message}) +{ + return ( +
+ + {`Predictor is ${message}!`} +

+ } + placement="bottom" + trigger="hover" + align={{offset: [0, 8]}} + > + +
+ + +
+ ); +} + + +StatusIndicator.propTypes = { + /** + * Connection indicator current color. + */ + color: PropTypes.string.isRequired, + + /** + * Connection indicator current message. + */ + message: PropTypes.string.isRequired +} + + +export default StatusIndicator; diff --git a/src/components/Header/index.js b/src/components/Header/index.js index 74dc8b4..1533ef9 100644 --- a/src/components/Header/index.js +++ b/src/components/Header/index.js @@ -1,16 +1,53 @@ import React from 'react'; +import { useEffect, useState } from 'react'; import Logo from "./logo.png"; -import { Icon, Popover } from 'antd'; +import StatusIndicator from './StatusIndicator'; +import PropTypes from 'prop-types'; import './Header.css' +const CONNECTION = { + 0: { + message: 'connecting', + color: '#b5982d' + }, + 1: { + message: 'connected', + color: '#39963f' + }, + 2: { + message: 'closing', + color: '#b5982d' + }, + 3: { + message: 'closed', + color: '#a83232' + } +}; + + /** * Linguiçator editor header component. * * @component */ -function Header() +function Header({socketState}) { + // component state hooks + const [status, setStatus] = useState(CONNECTION[socketState].message); + const [colorCode, setColorCode] = useState(CONNECTION[socketState].color); + + + /** + * I am called whenever connection to predictor state changes. + */ + useEffect(() => + { + setStatus(CONNECTION[socketState].message); + setColorCode(CONNECTION[socketState].color); + }, [socketState]); + + return (
@@ -21,6 +58,10 @@ function Header()

Lingüiçator

+ Coming soon

} trigger="hover" @@ -36,4 +77,12 @@ function Header() } +Header.propTypes = { + /** + * Websocket connection to predictor current status. + */ + socketState: PropTypes.number.isRequired +} + + export default Header; From a68303b46504eabe384f32d30c312a8430a1deef Mon Sep 17 00:00:00 2001 From: dmrib Date: Tue, 7 Apr 2020 10:24:44 -0300 Subject: [PATCH 04/40] Componentize header identity section --- src/components/Header/Identity/index.js | 37 +++++++++++++++++++++++++ src/components/Header/index.js | 12 ++++---- 2 files changed, 42 insertions(+), 7 deletions(-) create mode 100644 src/components/Header/Identity/index.js diff --git a/src/components/Header/Identity/index.js b/src/components/Header/Identity/index.js new file mode 100644 index 0000000..2d66f2b --- /dev/null +++ b/src/components/Header/Identity/index.js @@ -0,0 +1,37 @@ +import React from 'react'; +import PropTypes from 'prop-types'; + + +/** + * Linguiçator identity component. + * + * @component + */ +function Identity({logo, brand}) +{ + return( +
+ Linguiçator Logo +

{brand}

+
+ ); +} + + +Identity.propTypes = { + /** + * Identity logo. + */ + logo: PropTypes.string.isRequired, + + /** + * Identity brand name. + */ + brand: PropTypes.string.isRequired +} + + +export default Identity; diff --git a/src/components/Header/index.js b/src/components/Header/index.js index 1533ef9..6ee5af6 100644 --- a/src/components/Header/index.js +++ b/src/components/Header/index.js @@ -1,5 +1,6 @@ import React from 'react'; import { useEffect, useState } from 'react'; +import Identity from './Identity'; import Logo from "./logo.png"; import StatusIndicator from './StatusIndicator'; import PropTypes from 'prop-types'; @@ -50,13 +51,10 @@ function Header({socketState}) return (
-
- Linguiçator Logo -

Lingüiçator

-
+
Date: Tue, 7 Apr 2020 10:32:15 -0300 Subject: [PATCH 05/40] Componentize settings menu --- src/components/Header/Settings/index.js | 26 +++++++++++++++++++++++++ src/components/Header/index.js | 13 +++---------- 2 files changed, 29 insertions(+), 10 deletions(-) create mode 100644 src/components/Header/Settings/index.js diff --git a/src/components/Header/Settings/index.js b/src/components/Header/Settings/index.js new file mode 100644 index 0000000..445a17f --- /dev/null +++ b/src/components/Header/Settings/index.js @@ -0,0 +1,26 @@ +import React from 'react'; +import { Icon, Popover } from 'antd'; + + +/** + * Settings selector component. + * + * @component + */ +function Settings() +{ + return( + Coming soon

} + trigger="hover" + > + +
+ ); +} + + +export default Settings; diff --git a/src/components/Header/index.js b/src/components/Header/index.js index 6ee5af6..735fe12 100644 --- a/src/components/Header/index.js +++ b/src/components/Header/index.js @@ -1,7 +1,8 @@ import React from 'react'; import { useEffect, useState } from 'react'; import Identity from './Identity'; -import Logo from "./logo.png"; +import Logo from './logo.png'; +import Settings from './Settings'; import StatusIndicator from './StatusIndicator'; import PropTypes from 'prop-types'; import './Header.css' @@ -60,15 +61,7 @@ function Header({socketState}) color={colorCode} message={status} /> - Coming soon

} - trigger="hover" - > - -
+
); From 87c25ebf798ed2a8b63b554c0b50cc95977af4ce Mon Sep 17 00:00:00 2001 From: dmrib Date: Tue, 7 Apr 2020 21:03:13 -0300 Subject: [PATCH 06/40] Add @ant-design/icons as a dependency --- package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/package.json b/package.json index 689d38b..04ca4f8 100644 --- a/package.json +++ b/package.json @@ -3,6 +3,7 @@ "version": "0.1.0", "private": true, "dependencies": { + "@ant-design/icons": "^4.0.5", "antd": "^3.26.11", "dompurify": "^2.0.8", "express": "^4.17.1", From 520cdf7604cee835f73dd4717593c53cc244af44 Mon Sep 17 00:00:00 2001 From: dmrib Date: Tue, 7 Apr 2020 21:12:00 -0300 Subject: [PATCH 07/40] Override antd styling for slider component --- src/components/Header/Header.css | 84 +++++++++++++++++++++++++++++++- 1 file changed, 83 insertions(+), 1 deletion(-) diff --git a/src/components/Header/Header.css b/src/components/Header/Header.css index c106775..e0825be 100644 --- a/src/components/Header/Header.css +++ b/src/components/Header/Header.css @@ -1,5 +1,16 @@ @import url('https://fonts.googleapis.com/css?family=Sacramento&display=swap'); +html { + --antd-wave-shadow-color: #a7425c; +} + +::selection { + background: #a7425c; + color: #a7425c; + text-shadow: none; + text-decoration: none; +} + .header-container { height: 10%; width: 100%; @@ -44,5 +55,76 @@ height: 15px; width: 15px; margin-right: 10px; - border-radius: 50%; + border-radius: 100%; +} + +.ant-slider-track { + background-color: #a7425c; +} + +.ant-slider-track:hover { + background-color: #a7425c; + border: solid 2px #a7425c; +} + +.ant-slider-mark:hover { + background-color: #a7425c; + border: solid 2px #a7425c; +} + +.ant-slider-handle { + border: solid 2px #a7425c; +} + +.ant-slider-track { + border: solid 2px #a7425c; + color: #a7425c; +} + +.ant-slider-handle:focus { + border: solid 2px #a7425c; + color: #a7425c; +} + +.ant-slider-handle.ant-tooltip-open { + border: solid 2px #a7425c; + color: #a7425c; +} + +.ant-slider-rail { + border: solid 2px #a7425c; + color: #a7425c; +} + +.ant-slider-handle-click-focused { + border: solid 2px #a7425c; + color: #a7425c; +} + +.ant-slider-handle:focus { + color: #a7425c; + border: solid 2px #a7425c; + box-shadow: none; +} + +.ant-slider-handle-click-focused::selection { + background: #a7425c; + color: #a7425c; + border: solid 2px #a7425c; + box-shadow: none; +} + +.ant-slider-handle-click-focused:hover { + border: solid 2px #a7425c; + color: #a7425c; +} + +.ant-slider-handle ant-tooltip-open { + border: solid 2px #a7425c; + color: #a7425c; +} + +.ant-slider:hover .ant-slider-handle:not(.ant-tooltip-open) { + border: solid 2px #a7425c; + color: #a7425c; } From 88e72b1aa45ab045b482660dc1070f5469193507 Mon Sep 17 00:00:00 2001 From: dmrib Date: Tue, 7 Apr 2020 21:12:50 -0300 Subject: [PATCH 08/40] Add prediction size selection to settings --- src/components/Header/Settings/index.js | 32 +++++++++++++++++++++---- 1 file changed, 28 insertions(+), 4 deletions(-) diff --git a/src/components/Header/Settings/index.js b/src/components/Header/Settings/index.js index 445a17f..f4418f5 100644 --- a/src/components/Header/Settings/index.js +++ b/src/components/Header/Settings/index.js @@ -1,5 +1,7 @@ import React from 'react'; -import { Icon, Popover } from 'antd'; +import { SettingFilled } from '@ant-design/icons'; +import { Typography, Icon, Popover, Slider } from 'antd'; +import 'antd/dist/antd.css'; /** @@ -11,12 +13,34 @@ function Settings() { return( Coming soon

} + content={ +
+
+ + Prediction size: + +
+ + +
+ } trigger="hover" >
); From c7c4cb9597352570b0c310febcdd2568a5a436ac Mon Sep 17 00:00:00 2001 From: dmrib Date: Tue, 7 Apr 2020 21:49:49 -0300 Subject: [PATCH 09/40] Add Salami icon --- src/components/Header/salami.svg | 1 + 1 file changed, 1 insertion(+) create mode 100644 src/components/Header/salami.svg diff --git a/src/components/Header/salami.svg b/src/components/Header/salami.svg new file mode 100644 index 0000000..c465aa9 --- /dev/null +++ b/src/components/Header/salami.svg @@ -0,0 +1 @@ + \ No newline at end of file From 99e11b017bbddc8ac2e9c9108c5d3dc750b872b6 Mon Sep 17 00:00:00 2001 From: dmrib Date: Tue, 7 Apr 2020 21:50:21 -0300 Subject: [PATCH 10/40] Implement prediction hints component --- src/components/Header/Predictor/index.js | 42 ++++++++++++++++++++++++ 1 file changed, 42 insertions(+) create mode 100644 src/components/Header/Predictor/index.js diff --git a/src/components/Header/Predictor/index.js b/src/components/Header/Predictor/index.js new file mode 100644 index 0000000..202a8dd --- /dev/null +++ b/src/components/Header/Predictor/index.js @@ -0,0 +1,42 @@ +import React from 'react'; +import { Popover, Icon } from 'antd'; +import { ReactComponent as Salami } from '../salami.svg'; + + +/** + * Predictions hints component. + * + * @component + */ +function Predictor() +{ + return( + + {'Lorem ipsum dolor sit amet, consectetur adipiscing elit.'} +

+ } + placement="bottomRight" + style={{ + color: '#a7425c' + }} + align={{offset: [0, -5]}} + > + +
+ ); +} + + +export default Predictor; From 723d8300aa7b0d108c6fb0627f51d2584ab6850a Mon Sep 17 00:00:00 2001 From: dmrib Date: Tue, 7 Apr 2020 21:51:21 -0300 Subject: [PATCH 11/40] Expose prediction hints component --- src/components/Header/index.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/components/Header/index.js b/src/components/Header/index.js index 735fe12..2507e01 100644 --- a/src/components/Header/index.js +++ b/src/components/Header/index.js @@ -4,6 +4,7 @@ import Identity from './Identity'; import Logo from './logo.png'; import Settings from './Settings'; import StatusIndicator from './StatusIndicator'; +import Predictor from './Predictor'; import PropTypes from 'prop-types'; import './Header.css' @@ -61,6 +62,7 @@ function Header({socketState}) color={colorCode} message={status} /> +
From 4a127d0e55ed9b4e8232facbb0d839a06e37a68e Mon Sep 17 00:00:00 2001 From: dmrib Date: Wed, 8 Apr 2020 18:36:55 -0300 Subject: [PATCH 12/40] Add redux as a dependency --- package.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index 04ca4f8..0c45039 100644 --- a/package.json +++ b/package.json @@ -10,7 +10,8 @@ "marked": "^0.8.0", "react": "^16.12.0", "react-dom": "^16.12.0", - "react-scripts": "3.4.0" + "react-scripts": "3.4.0", + "redux": "^4.0.5" }, "scripts": { "start": "react-scripts start", From be5ad8e8c01f79c953dd84bc6fe192f9f0d7b9da Mon Sep 17 00:00:00 2001 From: dmrib Date: Wed, 8 Apr 2020 18:38:01 -0300 Subject: [PATCH 13/40] Add react-redux as a dependency --- package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/package.json b/package.json index 0c45039..5f3ea86 100644 --- a/package.json +++ b/package.json @@ -10,6 +10,7 @@ "marked": "^0.8.0", "react": "^16.12.0", "react-dom": "^16.12.0", + "react-redux": "^7.2.0", "react-scripts": "3.4.0", "redux": "^4.0.5" }, From 45f6dfdca0830f01bd7b954934eb57103f235433 Mon Sep 17 00:00:00 2001 From: dmrib Date: Wed, 8 Apr 2020 19:26:16 -0300 Subject: [PATCH 14/40] Add redux-devtools-extension as a development dependency --- package.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index 5f3ea86..c279f6f 100644 --- a/package.json +++ b/package.json @@ -38,6 +38,7 @@ }, "devDependencies": { "better-docs": "^1.4.7", - "jsdoc": "^3.6.3" + "jsdoc": "^3.6.3", + "redux-devtools-extension": "^2.13.8" } } From 929a9cd164aa182f453e4599ff2a5a06d0539cd1 Mon Sep 17 00:00:00 2001 From: dmrib Date: Wed, 8 Apr 2020 19:57:47 -0300 Subject: [PATCH 15/40] Implement set maximum prediction size action --- src/store/actions/predictions.js | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 src/store/actions/predictions.js diff --git a/src/store/actions/predictions.js b/src/store/actions/predictions.js new file mode 100644 index 0000000..cd73ed4 --- /dev/null +++ b/src/store/actions/predictions.js @@ -0,0 +1,20 @@ +/** + * I update predictions maximum size. + * + * @param {number} size - new predictions maximum size + * + * @returns {object} - action to be dispatched + */ +function setMaxSize(size) { + return { + type: 'SET_MAX_SIZE', + payload: { + size + } + }; +} + + +export { + setMaxSize +} From 0197218e1cdcd0937102368e8201d80e07bcbe28 Mon Sep 17 00:00:00 2001 From: dmrib Date: Wed, 8 Apr 2020 19:18:09 -0300 Subject: [PATCH 16/40] Create Predictions aggregate reducer --- src/store/reducers/predictions.js | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100644 src/store/reducers/predictions.js diff --git a/src/store/reducers/predictions.js b/src/store/reducers/predictions.js new file mode 100644 index 0000000..341ce91 --- /dev/null +++ b/src/store/reducers/predictions.js @@ -0,0 +1,31 @@ +/** + * Predictions store initial state. + */ +const INITIAL_STATE = { + 'maxSize': 75 +} + + +/** + * Predictions reducer. + * + * @param {object} state - current predictions store state + * @param {object} action - dispatched action + * + * @returns {object} - updated state + */ +function predictions(state = INITIAL_STATE, action) +{ + switch (action.type) { + case 'SET_MAX_SIZE': + return { + ...state, + maxSize: action.payload.size + }; + default: + return state; + } +} + + +export default predictions; From 098114fa986806b8413ab53f4333262a2b31695f Mon Sep 17 00:00:00 2001 From: dmrib Date: Wed, 8 Apr 2020 19:18:43 -0300 Subject: [PATCH 17/40] Create Redux store reducer --- src/store/reducers/index.js | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 src/store/reducers/index.js diff --git a/src/store/reducers/index.js b/src/store/reducers/index.js new file mode 100644 index 0000000..8202aad --- /dev/null +++ b/src/store/reducers/index.js @@ -0,0 +1,7 @@ +import { combineReducers } from 'redux'; +import predictions from './predictions'; + + +export default combineReducers({ + predictions +}); From 7ffe0687b3d038823a4776d14fdfb934a770dce5 Mon Sep 17 00:00:00 2001 From: dmrib Date: Wed, 8 Apr 2020 19:25:31 -0300 Subject: [PATCH 18/40] Create application Redux store --- src/store/index.js | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 src/store/index.js diff --git a/src/store/index.js b/src/store/index.js new file mode 100644 index 0000000..802360d --- /dev/null +++ b/src/store/index.js @@ -0,0 +1,12 @@ +import { createStore } from 'redux'; +import { composeWithDevTools } from 'redux-devtools-extension'; +import rootReducer from './reducers'; + + +/** + * Create application store with Redux-Dev-Tools support. + */ +const store = createStore(rootReducer, composeWithDevTools()); + + +export default store; From c35d6b99965757120f5397e428145371d23fba6c Mon Sep 17 00:00:00 2001 From: dmrib Date: Wed, 8 Apr 2020 19:25:50 -0300 Subject: [PATCH 19/40] Expose Redux store to components --- src/App.js | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/src/App.js b/src/App.js index 2a71a68..71ef76c 100644 --- a/src/App.js +++ b/src/App.js @@ -1,5 +1,7 @@ import React from 'react'; import { useState, useRef, useEffect } from 'react'; +import { Provider } from 'react-redux'; +import store from './store'; import Content from './components/Content'; import Header from './components/Header'; import Footer from './components/Footer'; @@ -59,11 +61,13 @@ function App() return ( -
-
- -
-
+ +
+
+ +
+
+
); } From a68cf8401b9771401b5bb33126cbbf73f5f15278 Mon Sep 17 00:00:00 2001 From: dmrib Date: Wed, 8 Apr 2020 20:00:40 -0300 Subject: [PATCH 20/40] Expose set maximum prediction size action --- src/components/Header/Settings/index.js | 20 ++++++++++++++++++++ src/store/actions/predictions.js | 3 ++- 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/src/components/Header/Settings/index.js b/src/components/Header/Settings/index.js index f4418f5..6bf3e1b 100644 --- a/src/components/Header/Settings/index.js +++ b/src/components/Header/Settings/index.js @@ -1,4 +1,6 @@ import React from 'react'; +import { useDispatch } from 'react-redux'; +import { setMaxSize } from '../../../store/actions/predictions'; import { SettingFilled } from '@ant-design/icons'; import { Typography, Icon, Popover, Slider } from 'antd'; import 'antd/dist/antd.css'; @@ -11,6 +13,23 @@ import 'antd/dist/antd.css'; */ function Settings() { + // action dispatch hook + const dispatch = useDispatch(); + + + /** + * I am a callback for prediction maximum size slider value changed. + * + * @param {number} size - new maximum prediction size + * + * @returns {undefined} - nothing + */ + function setMaximumPredictionSize(size) + { + dispatch(setMaxSize(size)); + } + + return(
diff --git a/src/store/actions/predictions.js b/src/store/actions/predictions.js index cd73ed4..b6d11c5 100644 --- a/src/store/actions/predictions.js +++ b/src/store/actions/predictions.js @@ -5,7 +5,8 @@ * * @returns {object} - action to be dispatched */ -function setMaxSize(size) { +function setMaxSize(size) +{ return { type: 'SET_MAX_SIZE', payload: { From 7d623d92fc976c02f8f7abc2fb096c6da0b0bcf7 Mon Sep 17 00:00:00 2001 From: dmrib Date: Thu, 9 Apr 2020 10:43:30 -0300 Subject: [PATCH 21/40] Implement set idle time until prediction action --- src/store/actions/predictions.js | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/src/store/actions/predictions.js b/src/store/actions/predictions.js index b6d11c5..70ec148 100644 --- a/src/store/actions/predictions.js +++ b/src/store/actions/predictions.js @@ -16,6 +16,25 @@ function setMaxSize(size) } +/** + * I update idle time until predictions are scheduled. + * + * @param {number} time - time until predictions in miliseconds + * + * @returns {object} - action to be dispatched + */ +function setIdleTime(time) +{ + return { + type: 'SET_IDLE_TIME', + payload: { + time + } + }; +} + + export { - setMaxSize + setMaxSize, + setIdleTime } From 9fb44ecb8536869a1c2d2d708a5e6b232f172639 Mon Sep 17 00:00:00 2001 From: dmrib Date: Thu, 9 Apr 2020 10:44:14 -0300 Subject: [PATCH 22/40] Implement set idle time until prediction reducer --- src/store/reducers/predictions.js | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/store/reducers/predictions.js b/src/store/reducers/predictions.js index 341ce91..bd20004 100644 --- a/src/store/reducers/predictions.js +++ b/src/store/reducers/predictions.js @@ -2,8 +2,9 @@ * Predictions store initial state. */ const INITIAL_STATE = { - 'maxSize': 75 -} + maxSize: 75, + idleTime: 3000 +}; /** @@ -22,6 +23,11 @@ function predictions(state = INITIAL_STATE, action) ...state, maxSize: action.payload.size }; + case 'SET_IDLE_TIME': + return { + ...state, + idleTime: action.payload.time + } default: return state; } From 4612d5b5b7e8a7a3cfbd208a3ddedb762c17f907 Mon Sep 17 00:00:00 2001 From: dmrib Date: Thu, 9 Apr 2020 10:51:20 -0300 Subject: [PATCH 23/40] Style line separators on settings --- src/components/Header/Header.css | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/components/Header/Header.css b/src/components/Header/Header.css index e0825be..9ab820e 100644 --- a/src/components/Header/Header.css +++ b/src/components/Header/Header.css @@ -58,6 +58,12 @@ html { border-radius: 100%; } +.settings-separator { + background-color: #432f44; + height: 1px; + border: 0; +} + .ant-slider-track { background-color: #a7425c; } From c02cd1fb34aed9e7603c27b5d8393420510b03bb Mon Sep 17 00:00:00 2001 From: dmrib Date: Thu, 9 Apr 2020 10:44:42 -0300 Subject: [PATCH 24/40] Make idle time until prediction adjustable --- src/components/Header/Settings/index.js | 38 ++++++++++++++++++++++++- 1 file changed, 37 insertions(+), 1 deletion(-) diff --git a/src/components/Header/Settings/index.js b/src/components/Header/Settings/index.js index 6bf3e1b..65eaf21 100644 --- a/src/components/Header/Settings/index.js +++ b/src/components/Header/Settings/index.js @@ -1,6 +1,6 @@ import React from 'react'; import { useDispatch } from 'react-redux'; -import { setMaxSize } from '../../../store/actions/predictions'; +import { setMaxSize, setIdleTime } from '../../../store/actions/predictions'; import { SettingFilled } from '@ant-design/icons'; import { Typography, Icon, Popover, Slider } from 'antd'; import 'antd/dist/antd.css'; @@ -30,6 +30,19 @@ function Settings() } + /** + * I am a callback for idle time until prediction slider value changed. + * + * @param {number} time - new idle time until prediction + * + * @returns {undefined} - nothing + */ + function setIdleSecondsUntilPrediction(time) + { + dispatch(setIdleTime(time * 1000)); + } + + return( +
+
+
+
+ + Idle seconds until prediction: + +
+ + } trigger="hover" From 74a9ba23c4c16a8f716be322c432c9f2e86ef78d Mon Sep 17 00:00:00 2001 From: dmrib Date: Thu, 9 Apr 2020 13:19:59 -0300 Subject: [PATCH 25/40] Implement reset prediction action --- src/store/actions/predictions.js | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/src/store/actions/predictions.js b/src/store/actions/predictions.js index 70ec148..cd4b530 100644 --- a/src/store/actions/predictions.js +++ b/src/store/actions/predictions.js @@ -34,7 +34,22 @@ function setIdleTime(time) } +/** + * I reset the current prediction. + * + * @returns {object} - action to be dispatched + */ +function resetPrediction() +{ + return { + type: 'RESET_PREDICTION', + payload: {} + } +} + + export { setMaxSize, - setIdleTime + setIdleTime, + resetPrediction } From 7e77dd241ce362122ae47991e04e202b4bee62de Mon Sep 17 00:00:00 2001 From: dmrib Date: Thu, 9 Apr 2020 13:21:51 -0300 Subject: [PATCH 26/40] Implement reset prediction reducer --- src/store/reducers/predictions.js | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/store/reducers/predictions.js b/src/store/reducers/predictions.js index bd20004..b83aeb9 100644 --- a/src/store/reducers/predictions.js +++ b/src/store/reducers/predictions.js @@ -2,8 +2,11 @@ * Predictions store initial state. */ const INITIAL_STATE = { + hasPrediction: false, + idleTime: 3000, + isPredicting: false, maxSize: 75, - idleTime: 3000 + prediction: '' }; @@ -28,6 +31,14 @@ function predictions(state = INITIAL_STATE, action) ...state, idleTime: action.payload.time } + case 'RESET_PREDICTION': + return { + ...state, + isPredicting: false, + expectedId: null, + prediction: '', + hasPrediction: false + } default: return state; } From 2154d5e43ac573098282bd2631c50ef4b6fa8f07 Mon Sep 17 00:00:00 2001 From: dmrib Date: Thu, 9 Apr 2020 13:22:58 -0300 Subject: [PATCH 27/40] Implement wait prediction action --- src/store/actions/predictions.js | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/src/store/actions/predictions.js b/src/store/actions/predictions.js index cd4b530..a67acbb 100644 --- a/src/store/actions/predictions.js +++ b/src/store/actions/predictions.js @@ -48,8 +48,27 @@ function resetPrediction() } +/** + * I wait for a prediction from backstage. + * + * @param {string} id - expected prediction id + * + * @returns {object} - action to be dispatched + */ +function waitPrediction(id) +{ + return { + type: 'WAIT_PREDICTION', + payload: { + id + } + } +} + + export { setMaxSize, setIdleTime, - resetPrediction + resetPrediction, + waitPrediction } From ea319d78f20e4d1507088b906d053d21215b0307 Mon Sep 17 00:00:00 2001 From: dmrib Date: Thu, 9 Apr 2020 13:23:16 -0300 Subject: [PATCH 28/40] Implement wait prediction reducer --- src/store/reducers/predictions.js | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/store/reducers/predictions.js b/src/store/reducers/predictions.js index b83aeb9..3c6bd26 100644 --- a/src/store/reducers/predictions.js +++ b/src/store/reducers/predictions.js @@ -2,6 +2,7 @@ * Predictions store initial state. */ const INITIAL_STATE = { + expectedId: null, hasPrediction: false, idleTime: 3000, isPredicting: false, @@ -39,6 +40,12 @@ function predictions(state = INITIAL_STATE, action) prediction: '', hasPrediction: false } + case 'WAIT_PREDICTION': + return { + ...state, + isPredicting: true, + expectedId: action.payload.id + } default: return state; } From 1808c1e42104919734c84c8ca3d3b410a4474688 Mon Sep 17 00:00:00 2001 From: dmrib Date: Thu, 9 Apr 2020 13:33:36 -0300 Subject: [PATCH 29/40] Add uuid as a dependency --- package.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index c279f6f..54fcf57 100644 --- a/package.json +++ b/package.json @@ -12,7 +12,8 @@ "react-dom": "^16.12.0", "react-redux": "^7.2.0", "react-scripts": "3.4.0", - "redux": "^4.0.5" + "redux": "^4.0.5", + "uuid": "^7.0.3" }, "scripts": { "start": "react-scripts start", From 5cf43b157a166780b716dd0fa0469f564b28a0e8 Mon Sep 17 00:00:00 2001 From: dmrib Date: Thu, 9 Apr 2020 13:33:20 -0300 Subject: [PATCH 30/40] Make predictions request on user idle --- src/App.js | 8 ++++- src/components/Content/index.js | 61 ++++++++++++++++++++++++++++++++- 2 files changed, 67 insertions(+), 2 deletions(-) diff --git a/src/App.js b/src/App.js index 71ef76c..b4e74e5 100644 --- a/src/App.js +++ b/src/App.js @@ -54,6 +54,12 @@ function App() setSocketState(socket.current.readyState); } + // on websocket message received event callback + socket.current.onmessage = (event) => + { + console.log(`Received: "${event.data}"`); + }; + // cleanup on lifecycle end return () => socket.current.close(); @@ -64,7 +70,7 @@ function App()
- +
diff --git a/src/components/Content/index.js b/src/components/Content/index.js index 0d6e078..3b4e409 100644 --- a/src/components/Content/index.js +++ b/src/components/Content/index.js @@ -1,6 +1,10 @@ import React from 'react'; import { useEffect, useState } from 'react'; +import { useDispatch, useSelector } from 'react-redux'; +import { resetPrediction } from '../../store/actions/predictions'; +import { waitPrediction } from '../../store/actions/predictions'; import { sanitize } from 'dompurify'; +import { v4 as uuidv4 } from 'uuid'; import marked from 'marked'; import './Content.css' @@ -10,11 +14,14 @@ import './Content.css' * * @component */ -function Content() +function Content({socket}) { // component state hooks const [text, setText] = useState(''); const [preview, setPreview] = useState(''); + const [scheduled, setScheduled] = useState(null); + const dispatch = useDispatch(); + const predictor = useSelector(state => state.predictions); /** @@ -26,6 +33,57 @@ function Content() }, [text]); + /** + * I ask a prediction from the backstage. + * + * @returns {undefined} - nothing + */ + function askForPrediction() + { + // generate prediction request id + const id = uuidv4(); + + // update application state to wait for prediction + dispatch(waitPrediction(id)); + + // create prediction request payload + const data = JSON.stringify({ + id, + resource: '/predict', + data: { + text, + length: predictor.maxSize + } + }); + + // send payload through socket + socket.send(data); + } + + + /** + * I update predictions schedule state. + * + * @returns {undefined} - nothing + */ + function updateSchedule() + { + // prediction scheduled: cancel current scheduled prediction + if (scheduled !== null) + { + clearTimeout(scheduled); + setScheduled(null); + } + + // reset prediction state on store + dispatch(resetPrediction()); + + // schedule next prediction attempt + const id = setTimeout(askForPrediction, predictor.idleTime); + setScheduled(id); + } + + /** * I am a callback for input text changed events. * @@ -36,6 +94,7 @@ function Content() function onTextChange(event) { setText(event.target.value); + updateSchedule(); } From 3a0bdd35758d9545e383e88feb6eed59c8727bc8 Mon Sep 17 00:00:00 2001 From: dmrib Date: Thu, 9 Apr 2020 13:44:47 -0300 Subject: [PATCH 31/40] Animate Salami while waiting for predictions --- src/components/Header/Predictor/index.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/components/Header/Predictor/index.js b/src/components/Header/Predictor/index.js index 202a8dd..b3c6810 100644 --- a/src/components/Header/Predictor/index.js +++ b/src/components/Header/Predictor/index.js @@ -1,4 +1,5 @@ import React from 'react'; +import { useSelector } from 'react-redux'; import { Popover, Icon } from 'antd'; import { ReactComponent as Salami } from '../salami.svg'; @@ -10,6 +11,9 @@ import { ReactComponent as Salami } from '../salami.svg'; */ function Predictor() { + // component state hooks + const shouldSpin = useSelector(state => state.predictions.isPredicting); + return( ); From 89f3f2f880bb8d62b7516cce44de9ca72d7c84ec Mon Sep 17 00:00:00 2001 From: dmrib Date: Sat, 11 Apr 2020 18:35:50 -0300 Subject: [PATCH 32/40] Implement show hint action --- src/store/actions/predictions.js | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/src/store/actions/predictions.js b/src/store/actions/predictions.js index a67acbb..ccb6f21 100644 --- a/src/store/actions/predictions.js +++ b/src/store/actions/predictions.js @@ -66,9 +66,28 @@ function waitPrediction(id) } +/** + * I show a prediction hint. + * + * @param {string} prediction - prediction text + * + * @returns {object} - action to be dispatched + */ +function showHint(prediction) +{ + return { + type: 'SHOW_HINT', + payload: { + prediction + } + } +} + + export { setMaxSize, setIdleTime, resetPrediction, - waitPrediction + waitPrediction, + showHint } From db08e660e1462b9f58c11994b19a6e55b5546cca Mon Sep 17 00:00:00 2001 From: dmrib Date: Sat, 11 Apr 2020 18:36:14 -0300 Subject: [PATCH 33/40] Implement show hint reducer --- src/store/reducers/predictions.js | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/store/reducers/predictions.js b/src/store/reducers/predictions.js index 3c6bd26..ae90736 100644 --- a/src/store/reducers/predictions.js +++ b/src/store/reducers/predictions.js @@ -46,6 +46,13 @@ function predictions(state = INITIAL_STATE, action) isPredicting: true, expectedId: action.payload.id } + case 'SHOW_HINT': + return { + ...state, + prediction: action.payload.prediction, + isPredicting: false, + hasPrediction: true + } default: return state; } From 9ecb9690a0744eb1fec457fe86694dd23999e000 Mon Sep 17 00:00:00 2001 From: dmrib Date: Sat, 11 Apr 2020 18:42:08 -0300 Subject: [PATCH 34/40] Handle incoming predictions on socket connection --- src/App.js | 6 ----- src/components/Content/index.js | 39 ++++++++++++++++++++++++++++++++- 2 files changed, 38 insertions(+), 7 deletions(-) diff --git a/src/App.js b/src/App.js index b4e74e5..9851966 100644 --- a/src/App.js +++ b/src/App.js @@ -54,12 +54,6 @@ function App() setSocketState(socket.current.readyState); } - // on websocket message received event callback - socket.current.onmessage = (event) => - { - console.log(`Received: "${event.data}"`); - }; - // cleanup on lifecycle end return () => socket.current.close(); diff --git a/src/components/Content/index.js b/src/components/Content/index.js index 3b4e409..b3acf05 100644 --- a/src/components/Content/index.js +++ b/src/components/Content/index.js @@ -1,7 +1,7 @@ import React from 'react'; import { useEffect, useState } from 'react'; import { useDispatch, useSelector } from 'react-redux'; -import { resetPrediction } from '../../store/actions/predictions'; +import { resetPrediction, showHint } from '../../store/actions/predictions'; import { waitPrediction } from '../../store/actions/predictions'; import { sanitize } from 'dompurify'; import { v4 as uuidv4 } from 'uuid'; @@ -33,6 +33,43 @@ function Content({socket}) }, [text]); + /** + * I set the callback for on message events on my socket. + */ + useEffect(() => + { + // socket connection is established: set on message event callback + if (socket !== null) + { + socket.onmessage = (event) => + { + // parse message data into object + const prediction = JSON.parse(event.data); + + // handle received prediction + handlePrediction(prediction); + } + } + }); + + + /** + * I handle an incoming prediction from backstage. + * + * @param {object} prediction - received prediction + * + * @returns {undefined} - nothing + */ + function handlePrediction(prediction) + { + // prediction is expected: show it as hint + if (prediction.id === predictor.expectedId) + { + dispatch(showHint(prediction.data)); + } + } + + /** * I ask a prediction from the backstage. * From e3af40c0c12d943adec576675be97460d700d85d Mon Sep 17 00:00:00 2001 From: dmrib Date: Sat, 11 Apr 2020 18:46:43 -0300 Subject: [PATCH 35/40] Parse text before sending for backstage predictions --- src/components/Content/index.js | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/components/Content/index.js b/src/components/Content/index.js index b3acf05..a536c53 100644 --- a/src/components/Content/index.js +++ b/src/components/Content/index.js @@ -77,6 +77,15 @@ function Content({socket}) */ function askForPrediction() { + // build prediction prefix + const prefix = text.slice(-300); + + // no input string: abort + if (prefix === '') + { + return; + } + // generate prediction request id const id = uuidv4(); @@ -88,7 +97,7 @@ function Content({socket}) id, resource: '/predict', data: { - text, + text: prefix, length: predictor.maxSize } }); From 07717a935743e0e13b0f6cb2c768a25f5dee989b Mon Sep 17 00:00:00 2001 From: dmrib Date: Sat, 11 Apr 2020 18:48:20 -0300 Subject: [PATCH 36/40] Make Salami reactive to incoming predictions --- src/components/Header/Predictor/index.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/components/Header/Predictor/index.js b/src/components/Header/Predictor/index.js index b3c6810..9221389 100644 --- a/src/components/Header/Predictor/index.js +++ b/src/components/Header/Predictor/index.js @@ -12,7 +12,7 @@ import { ReactComponent as Salami } from '../salami.svg'; function Predictor() { // component state hooks - const shouldSpin = useSelector(state => state.predictions.isPredicting); + const predictor = useSelector(state => state.predictions); return( - {'Lorem ipsum dolor sit amet, consectetur adipiscing elit.'} + {predictor.prediction}

} + visible = {predictor.hasPrediction} placement="bottomRight" style={{ color: '#a7425c' }} - align={{offset: [0, -5]}} >
); From c8968710d48b6d0a464a36b5a20fa9699be81721 Mon Sep 17 00:00:00 2001 From: dmrib Date: Sat, 11 Apr 2020 18:39:01 -0300 Subject: [PATCH 37/40] Override antd styling for popvers --- src/components/Header/Header.css | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/components/Header/Header.css b/src/components/Header/Header.css index 9ab820e..e424e45 100644 --- a/src/components/Header/Header.css +++ b/src/components/Header/Header.css @@ -134,3 +134,11 @@ html { border: solid 2px #a7425c; color: #a7425c; } + +.ant-popover { + max-width: 200px; +} + +.ant-popover p { + margin-bottom: 5px; +} From d033f15c8e85766d76d09135da25b5d38ad53797 Mon Sep 17 00:00:00 2001 From: dmrib Date: Sat, 11 Apr 2020 18:48:54 -0300 Subject: [PATCH 38/40] Implement predictions appending to text body --- src/components/Content/index.js | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/components/Content/index.js b/src/components/Content/index.js index a536c53..bf57abf 100644 --- a/src/components/Content/index.js +++ b/src/components/Content/index.js @@ -143,6 +143,17 @@ function Content({socket}) updateSchedule(); } + function onKeyDown(event) + { + if(event.keyCode === 9) + { + event.preventDefault(); + setText(text + predictor.prediction); + dispatch(resetPrediction()); + } + + } + return (
@@ -151,6 +162,7 @@ function Content({socket}) className="content-editor" value={text} onChange={onTextChange} + onKeyDown={onKeyDown} >
Date: Sat, 11 Apr 2020 19:02:26 -0300 Subject: [PATCH 39/40] Update project documentation --- docs/App.html | 6 +- docs/App.js.html | 63 +- docs/Content.html | 442 ++- docs/Footer.html | 4 +- docs/Header.html | 40 +- docs/Identity.html | 274 ++ docs/Predictor.html | 233 ++ docs/Settings.html | 596 +++ docs/StatusIndicator.html | 274 ++ docs/build/entry.css | 4 +- docs/build/entry.css.map | 2 +- docs/build/entry.js | 3355 ++++++++++++++++- docs/build/entry.js.map | 2 +- docs/build/salami.bcf222bb.svg | 1 + docs/components_Content_index.js.html | 122 +- docs/components_Footer_index.js.html | 4 +- docs/components_Header_Identity_index.js.html | 131 + .../components_Header_Predictor_index.js.html | 140 + docs/components_Header_Settings_index.js.html | 200 + ...nents_Header_StatusIndicator_index.js.html | 146 + docs/components_Header_index.js.html | 84 +- docs/entry.js | 14 +- docs/global.html | 1344 +++++++ docs/index.html | 4 +- docs/store_actions_predictions.js.html | 187 + docs/store_index.js.html | 106 + docs/store_reducers_predictions.js.html | 156 + 27 files changed, 7837 insertions(+), 97 deletions(-) create mode 100644 docs/Identity.html create mode 100644 docs/Predictor.html create mode 100644 docs/Settings.html create mode 100644 docs/StatusIndicator.html create mode 100644 docs/build/salami.bcf222bb.svg create mode 100644 docs/components_Header_Identity_index.js.html create mode 100644 docs/components_Header_Predictor_index.js.html create mode 100644 docs/components_Header_Settings_index.js.html create mode 100644 docs/components_Header_StatusIndicator_index.js.html create mode 100644 docs/global.html create mode 100644 docs/store_actions_predictions.js.html create mode 100644 docs/store_index.js.html create mode 100644 docs/store_reducers_predictions.js.html diff --git a/docs/App.html b/docs/App.html index 8508bf5..de21330 100644 --- a/docs/App.html +++ b/docs/App.html @@ -44,7 +44,7 @@

linguiçator-editor

@@ -149,7 +149,7 @@

View Source - App.js, line 14 + App.js, line 21

@@ -218,7 +218,7 @@