diff --git a/.gitignore b/.gitignore index 3c3629e..37f59f9 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,3 @@ node_modules +*.DS_Store + diff --git a/App.jsx b/app/App.jsx similarity index 52% rename from App.jsx rename to app/App.jsx index c3a69f1..eb887e8 100644 --- a/App.jsx +++ b/app/App.jsx @@ -1,15 +1,7 @@ import React from 'react'; import Message from './Message.jsx'; -import * as firebase from 'firebase'; import Input from './Input.jsx'; - -const firebaseConfig = { - apiKey: "AIzaSyDzTLJhoMxTNBADq2AOB83rclB2KIrRcEU", - authDomain: "chatbox-6e584.firebaseapp.com", - databaseURL: "https://chatbox-6e584.firebaseio.com", - storageBucket: "chatbox-6e584.appspot.com", -}; -const firebaseApp = firebase.initializeApp(firebaseConfig); +import * as messageService from './messageService/client/messageService.js'; const App = React.createClass({ getInitialState() { @@ -21,11 +13,15 @@ const App = React.createClass({ }, componentWillMount() { - this.messagesRef = firebaseApp.database().ref('messages'); + messageService.getMessages(function(messages) { + this.setState({messages: messages}); + }.bind(this)); }, componentDidMount() { - this.listenForItems(this.messagesRef); + messageService.listenForMessages(function(messages) { + this.setState({messages: messages}); + }.bind(this)); }, handleNameChange(event) { @@ -37,25 +33,7 @@ const App = React.createClass({ }, clearChat(event) { - this.messagesRef.remove(); - }, - - listenForItems(messagesRef) { - messagesRef.on('value', (snap) => { - // get children as an array - var newMessages = []; - snap.forEach((child) => { - newMessages.push({ - name: child.val().name, - message: child.val().message, - _key: child.key - }); - }); - - this.setState({ - messages: newMessages - }); - }); + messageService.clearMessages(); }, handleKeyPress(event) { @@ -63,15 +41,15 @@ const App = React.createClass({ return; } if (event.key === 'Enter') { - this.messagesRef.push({ name: this.state.name, message: this.state.newMessage }); this.setState({newMessage: ""}); + messageService.sendMessage(this.state.name, this.state.newMessage); } }, render() { - const messageDivs = this.state.messages.map((message) => { - return ; - }); + const messageDivs = this.state.messages.reduce((pre, cur) => { + return pre.concat(); + }, []); return (
@@ -84,6 +62,7 @@ const App = React.createClass({
+
); } diff --git a/Input.jsx b/app/Input.jsx similarity index 100% rename from Input.jsx rename to app/Input.jsx diff --git a/Message.jsx b/app/Message.jsx similarity index 100% rename from Message.jsx rename to app/Message.jsx diff --git a/main.js b/app/main.js similarity index 100% rename from main.js rename to app/main.js diff --git a/app/messageService/client/messageService.js b/app/messageService/client/messageService.js new file mode 100644 index 0000000..268e42a --- /dev/null +++ b/app/messageService/client/messageService.js @@ -0,0 +1,42 @@ +import Ajax from 'simple-ajax'; +import io from 'socket.io-client/socket.io'; + +module.exports = { + sendMessage: function(posterName, newMessage) { + var ajax = new Ajax({ + url: "http://localhost:8082/putMessage", + method: 'PUT', + data: {name: posterName , message: newMessage}, + dataType: 'json' + }); + ajax.on('error', (err) => console.log('ERROR', err)); + ajax.send(); + }, + getMessages: function(callback) { + var ajax = new Ajax({ + url: "http://localhost:8082/getMessages", + method: 'GET', + dataType: 'json' + }); + ajax.on('success', function(event) { + callback(JSON.parse(ajax.request.response)); + }); + ajax.on('error', (err) => console.log('ERROR', err)); + ajax.send(); + }, + clearMessages: function() { + var ajax = new Ajax({ + url: "http://localhost:8082/deleteMessages", + method: 'PUT', + dataType: 'json' + }); + ajax.on('error', (err) => console.log('ERROR', err)); + ajax.send(); + }, + listenForMessages: function(callback) { + io.connect('http://localhost:8082/', { path: '/listen/socket.io'}) + .on('notification', function(data) { + callback(data.messages); + }); + } +} diff --git a/app/messageService/server/dbConfig.js b/app/messageService/server/dbConfig.js new file mode 100644 index 0000000..43a956d --- /dev/null +++ b/app/messageService/server/dbConfig.js @@ -0,0 +1,8 @@ +module.exports = { + dbConfig: { + host: 'localhost', + user: 'root', + password: '', + database: 'chatroom' + } +} \ No newline at end of file diff --git a/app/messageService/server/poller.js b/app/messageService/server/poller.js new file mode 100644 index 0000000..d53d0fa --- /dev/null +++ b/app/messageService/server/poller.js @@ -0,0 +1,64 @@ +var mysql = require('mysql'); +var dbConfig = require('./dbConfig').dbConfig; +var connectionsArray = []; + +var connection = mysql.createConnection(dbConfig); + +var pollingLoop = function() { + var messages = []; + // Query the database + var query = connection.query('SELECT * FROM messages'); + + query.on('error', function(err) { + // Handle error, and 'end' event will be emitted after this as well + console.log(err); + updateSockets(err); + }); + query.on('result', function(message) { + // it fills our array looping on each user row inside the db + messages.push(message); + }); + query.on('end', function() { + // loop on itself only if there are sockets still connected + if (connectionsArray.length) { + + setTimeout(pollingLoop, 100); + + updateSockets({ + messages: messages + }); + } else { + console.log('No more connections. Ending polling'); + } + }); +}; + +var updateSockets = function(data) { + // sending new data to all the sockets connected + connectionsArray.forEach(function(socket) { + socket.volatile.emit('notification', data); + }); +}; + +module.exports = { + poll: function(socket) { + + // creating a new websocket to keep the content updated without any AJAX request + socket.on('connection', function(socket) { + + // start the polling only if there were no previous connections + if (!connectionsArray.length) { + pollingLoop(); + } + + socket.on('disconnect', function() { + var socketIndex = connectionsArray.indexOf(socket); + connectionsArray.splice(socketIndex, 1); + console.log('socketID = %s got disconnected', socketIndex); + }); + + connectionsArray.push(socket); + console.log('A new socket is connected!'); + }); + } +} diff --git a/app/messageService/server/routes/deleteMessages.js b/app/messageService/server/routes/deleteMessages.js new file mode 100644 index 0000000..25991b5 --- /dev/null +++ b/app/messageService/server/routes/deleteMessages.js @@ -0,0 +1,20 @@ +var mysql = require('mysql'); +var dbConfig = require('../dbConfig').dbConfig; + +module.exports = { + execute: function(req, res) { + var body = req.body; + var connection = mysql.createConnection(dbConfig); + + connection.connect(); + connection.query('DELETE from messages', function(error, rows, fields) { + if (error) {console.log(error); return;} + }); + + connection.end(function(err) { + // Do nothing + }); + + res.end(); + } +} \ No newline at end of file diff --git a/app/messageService/server/routes/getMessages.js b/app/messageService/server/routes/getMessages.js new file mode 100644 index 0000000..047601e --- /dev/null +++ b/app/messageService/server/routes/getMessages.js @@ -0,0 +1,18 @@ +var mysql = require('mysql'); +var dbConfig = require('../dbConfig').dbConfig; + +module.exports = { + execute: function(req, res) { + var connection = mysql.createConnection(dbConfig); + + connection.connect(); + connection.query('SELECT * from messages', function(error, rows, fields) { + if (error) {console.log(error); return;} + res.json(rows); + }); + + connection.end(function(err) { + // Do nothing + }); + } +} \ No newline at end of file diff --git a/app/messageService/server/routes/putMessage.js b/app/messageService/server/routes/putMessage.js new file mode 100644 index 0000000..a53531f --- /dev/null +++ b/app/messageService/server/routes/putMessage.js @@ -0,0 +1,20 @@ +var mysql = require('mysql'); +var dbConfig = require('../dbConfig').dbConfig; + +module.exports = { + execute: function(req, res) { + var body = req.body; + var connection = mysql.createConnection(dbConfig); + + connection.connect(); + connection.query('INSERT into messages (name, message) values("' + body.name + '", "' + body.message + '")', function(error, rows, fields) { + if (error) {console.log(error); return;} + }); + + connection.end(function(err) { + // Do nothing + }); + + res.end(); + } +} \ No newline at end of file diff --git a/app/messageService/server/server.js b/app/messageService/server/server.js new file mode 100644 index 0000000..475088d --- /dev/null +++ b/app/messageService/server/server.js @@ -0,0 +1,34 @@ +var express = require('express'); +var bodyParser = require('body-parser'); +var poller = require('./poller.js'); + +var getMessages = require('./routes/getMessages.js'); +var deleteMessages = require('./routes/deleteMessages.js'); +var putMessage = require('./routes/putMessage.js'); + +var app = express(); +var server = require('http').Server(app); +app.use(bodyParser.json()); +app.use(function(req, res, next) { + res.header("Access-Control-Allow-Origin", "http://localhost:8081"); + res.header("Access-Control-Allow-Credentials", true); + res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept"); + res.header("Access-Control-Allow-Methods", "GET, PUT"); + next(); +}); + +/* Set up the routes */ +app.get("/getMessages", function(req, res) { + getMessages.execute(req, res); +}); +app.put("/deleteMessages", function(req, res) { + deleteMessages.execute(req, res); +}); +app.put("/putMessage", function(req, res) { + putMessage.execute(req, res); +}); +app.get("/listen/socket.io", function(req, res) { + res.json(poller.poll(require('socket.io')(server, {path: '/listen/socket.io'}))); +}); + +server.listen(8082, "localhost"); diff --git a/package.json b/package.json index decce43..6cad04b 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,8 @@ "description": "test react app", "main": "index.js", "scripts": { - "start": "webpack-dev-server --hot", + "poststart": "node ./app/messageService/server/server.js", + "start": "webpack-dev-server --hot &", "test": "echo \"Error: no test specified\" && exit 1" }, "author": "", @@ -16,12 +17,19 @@ "babel-loader": "^6.2.5", "babel-preset-es2015": "^6.13.2", "babel-preset-react": "^6.11.1", - "firebase": "^3.2.1", + "body-parser": "^1.15.2", + "express": "^4.14.0", + "http": "0.0.0", "json-loader": "^0.5.4", + "mysql": "^2.11.1", "node-emoji": "^1.3.1", + "path": "^0.12.7", "react": "^15.3.0", "react-dom": "^15.3.0", + "simple-ajax": "^2.6.0", + "socket.io": "^1.4.8", "webpack": "^1.13.1", - "webpack-dev-server": "^1.14.1" + "webpack-dev-server": "^1.14.1", + "webpack-node-externals": "^1.3.3" } } diff --git a/webpack.config.js b/webpack.config.js index 74e8e94..55a6542 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -1,5 +1,14 @@ -var config = { - entry: './main.js', +const path = require('path'); + +module.exports = { + entry: path.resolve(__dirname, 'app/main.js'), + + resolve: { + extensions: ['', '.js', '.jsx'], + root: [ + path.resolve('./app') + ] + }, output: { path:'./', @@ -19,14 +28,18 @@ var config = { { test: /\.jsx?$/, exclude: /node_modules/, - loader: 'babel', + loader: 'babel-loader', query: { presets: ['es2015', 'react'] } } ] + }, + + node: { + fs: 'empty', + net: 'empty', + tls: 'empty' } } - -module.exports = config;