From 525ff6a1a35f5fb67fe14a1c32d1332e76d775b2 Mon Sep 17 00:00:00 2001 From: betamee Date: Fri, 17 Feb 2017 12:32:03 +0800 Subject: [PATCH 1/4] branch --- index.js | 1 + 1 file changed, 1 insertion(+) diff --git a/index.js b/index.js index e69de29..db88d65 100644 --- a/index.js +++ b/index.js @@ -0,0 +1 @@ +var express = require('express'); \ No newline at end of file From 1153648af2756187aa8b9c78659ab615e3e1efa0 Mon Sep 17 00:00:00 2001 From: betamee Date: Fri, 17 Feb 2017 12:34:50 +0800 Subject: [PATCH 2/4] hhh --- hhh.js | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 hhh.js diff --git a/hhh.js b/hhh.js new file mode 100644 index 0000000..e69de29 From b27448a7606fbb2dc079b24b1a6f625bda2e55f6 Mon Sep 17 00:00:00 2001 From: betamee Date: Fri, 17 Feb 2017 12:50:59 +0800 Subject: [PATCH 3/4] =?UTF-8?q?=E5=AE=8C=E5=96=84readme=E6=96=87=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 23 ++--------------------- 1 file changed, 2 insertions(+), 21 deletions(-) diff --git a/README.md b/README.md index 01bcfaa..e5f32be 100644 --- a/README.md +++ b/README.md @@ -1,25 +1,6 @@ # ReactBlog -a blog system based on React+Node +node_chat分支 -以[NodeBlog](https://github.com/BetaMee/NodeBlog)为基础,开发一个React+Node的blog系统,简单可易用的系统。 +以[NodeBlog](https://github.com/BetaMee/NodeBlog)为基础,开发一个React+Node的实时通信系统,使用WebSocket和Socket.io为包,后期准备独立出来,前期放在ReactBlog下。也学习下github分支管理。 -需求: - -1. 完善的api接口,返回的是json数据,前端使用fetch获取数据。 -2. 后期使用electron开发桌面应用,用于本地写博客。(或许我可以写一个像hexo那样的可以部署到github上的桌面应用) -3. 以我喜爱的material ui开发,后期设计自己的主题 -4. 有用户权限 -5. 留言系统就不自己做了 -6. post模型和user模型 -7. 正式部署到服务器上 - -未来计划: - -1. electron桌面应用 -2. 设计自己的主题风格 -3. 开发部署工具,部署到github上,静态博客 - -步骤: -1. 先以NodeBlog为基础完善后台api设计. -2. 开发react的web页面,react-router作路由,前后端协作。 From e531c142f7052ecd447ef5e0b99060b73b799e38 Mon Sep 17 00:00:00 2001 From: betamee Date: Fri, 17 Feb 2017 14:26:16 +0800 Subject: [PATCH 4/4] =?UTF-8?q?=E5=AE=8C=E6=88=90node=5Fchat=20demo?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hhh.js | 0 index.js | 61 ++++++++++++++++++++++- lib/chat_server.js | 112 +++++++++++++++++++++++++++++++++++++++++++ package.json | 4 +- public/css/style.css | 53 ++++++++++++++++++++ public/index.html | 34 +++++++++++++ public/js/chat.js | 43 +++++++++++++++++ public/js/chat_ui.js | 79 ++++++++++++++++++++++++++++++ 8 files changed, 384 insertions(+), 2 deletions(-) delete mode 100644 hhh.js create mode 100644 lib/chat_server.js create mode 100644 public/css/style.css create mode 100644 public/index.html create mode 100644 public/js/chat.js create mode 100644 public/js/chat_ui.js diff --git a/hhh.js b/hhh.js deleted file mode 100644 index e69de29..0000000 diff --git a/index.js b/index.js index db88d65..06944ad 100644 --- a/index.js +++ b/index.js @@ -1 +1,60 @@ -var express = require('express'); \ No newline at end of file +var http = require('http'); +var fs = require('fs'); +var path = require('path'); +var mime = require('mime'); +var cache = {}; + +function send404(response) { + response.writeHead(404, {'Content-Type': 'text/plain'}); + response.write('Error 404: resource not found.'); + response.end(); +} + +function sendFile(response, filePath, fileContents) { + response.writeHead( + 200, + {"content-type": mime.lookup(path.basename(filePath))} + ); + response.end(fileContents); +} + +function serveStatic(response, cache, absPath) { + if (cache[absPath]) { + sendFile(response, absPath, cache[absPath]); + } else { + fs.exists(absPath, function(exists) { + if (exists) { + fs.readFile(absPath, function(err, data) { + if (err) { + send404(response); + } else { + cache[absPath] = data; + sendFile(response, absPath, data); + } + }); + } else { + send404(response); + } + }); + } +} + +var server = http.createServer(function(request, response) { + var filePath = false; + + if (request.url == '/') { + filePath = 'public/index.html'; + } else { + filePath = 'public' + request.url; + } + + var absPath = './' + filePath; + serveStatic(response, cache, absPath); +}); + +server.listen(3000, function() { + console.log("Server listening on port 3000."); +}); + +var chatServer = require('./lib/chat_server'); +chatServer.listen(server); diff --git a/lib/chat_server.js b/lib/chat_server.js new file mode 100644 index 0000000..aa42a57 --- /dev/null +++ b/lib/chat_server.js @@ -0,0 +1,112 @@ +var socketio = require('socket.io'); +var io; +var guestNumber = 1; +var nickNames = {}; +var namesUsed = []; +var currentRoom = {}; + +exports.listen = function(server) { + io = socketio.listen(server); + io.set('log level', 1); + io.sockets.on('connection', function (socket) { + guestNumber = assignGuestName(socket, guestNumber, nickNames, namesUsed); + joinRoom(socket, 'Lobby'); + handleMessageBroadcasting(socket, nickNames); + handleNameChangeAttempts(socket, nickNames, namesUsed); + handleRoomJoining(socket); + socket.on('rooms', function() { + socket.emit('rooms', io.sockets.manager.rooms); + }); + handleClientDisconnection(socket, nickNames, namesUsed); + }); +}; + +function assignGuestName(socket, guestNumber, nickNames, namesUsed) { + var name = 'Guest' + guestNumber; + nickNames[socket.id] = name; + socket.emit('nameResult', { + success: true, + name: name + }); + namesUsed.push(name); + return guestNumber + 1; +} + +function joinRoom(socket, room) { + socket.join(room); + currentRoom[socket.id] = room; + socket.emit('joinResult', {room: room}); + socket.broadcast.to(room).emit('message', { + text: nickNames[socket.id] + ' has joined ' + room + '.' + }); + + var usersInRoom = io.sockets.clients(room); + if (usersInRoom.length > 1) { + var usersInRoomSummary = 'Users currently in ' + room + ': '; + for (var index in usersInRoom) { + var userSocketId = usersInRoom[index].id; + if (userSocketId != socket.id) { + if (index > 0) { + usersInRoomSummary += ', '; + } + usersInRoomSummary += nickNames[userSocketId]; + } + } + usersInRoomSummary += '.'; + socket.emit('message', {text: usersInRoomSummary}); + } +} + +function handleNameChangeAttempts(socket, nickNames, namesUsed) { + socket.on('nameAttempt', function(name) { + if (name.indexOf('Guest') == 0) { + socket.emit('nameResult', { + success: false, + message: 'Names cannot begin with "Guest".' + }); + } else { + if (namesUsed.indexOf(name) == -1) { + var previousName = nickNames[socket.id]; + var previousNameIndex = namesUsed.indexOf(previousName); + namesUsed.push(name); + nickNames[socket.id] = name; + delete namesUsed[previousNameIndex]; + socket.emit('nameResult', { + success: true, + name: name + }); + socket.broadcast.to(currentRoom[socket.id]).emit('message', { + text: previousName + ' is now known as ' + name + '.' + }); + } else { + socket.emit('nameResult', { + success: false, + message: 'That name is already in use.' + }); + } + } + }); +} + +function handleMessageBroadcasting(socket) { + socket.on('message', function (message) { + socket.broadcast.to(message.room).emit('message', { + text: nickNames[socket.id] + ': ' + message.text + }); + }); +} + +function handleRoomJoining(socket) { + socket.on('join', function(room) { + socket.leave(currentRoom[socket.id]); + joinRoom(socket, room.newRoom); + }); +} + +function handleClientDisconnection(socket) { + socket.on('disconnect', function() { + var nameIndex = namesUsed.indexOf(nickNames[socket.id]); + delete namesUsed[nameIndex]; + delete nickNames[socket.id]; + }); +} diff --git a/package.json b/package.json index ac744f5..3f52a08 100644 --- a/package.json +++ b/package.json @@ -22,6 +22,8 @@ "homepage": "https://github.com/BetaMee/ReactBlog#readme", "dependencies": { "express": "^4.14.1", - "express-session": "^1.15.1" + "express-session": "^1.15.1", + "mime": "^1.3.4", + "socket.io": "^1.7.2" } } diff --git a/public/css/style.css b/public/css/style.css new file mode 100644 index 0000000..9e4a7c5 --- /dev/null +++ b/public/css/style.css @@ -0,0 +1,53 @@ +body { + padding: 50px; + font: 14px "Lucida Grande", Helvetica, Arial, sans-serif; +} + +a { + color: #00B7FF; +} + +#content { + width: 800px; + margin-left: auto; + margin-right: auto; +} + +#room { + background-color: #ddd; + margin-bottom: 1em; +} + +#messages { + width: 690px; + height: 300px; + overflow: auto; + background-color: #eee; + margin-bottom: 1em; + margin-right: 10px; +} + +#room-list { + float: right; + width: 100px; + height: 300px; + overflow: auto; +} + +#room-list div { + border-bottom: 1px solid #eee; +} + +#room-list div:hover { + background-color: #ddd; +} + +#send-message { + width: 700px; + margin-bottom: 1em; + margin-right: 1em; +} + +#help { + font: 10px "Lucida Grande", Helvetica, Arial, sans-serif; +} diff --git a/public/index.html b/public/index.html new file mode 100644 index 0000000..dedf230 --- /dev/null +++ b/public/index.html @@ -0,0 +1,34 @@ + + + + + Chat + + + + +
+
+
+
+ +
+ + + +
+ Chat commands: +
    +
  • Change nickname: /nick [username]
  • +
  • Join/create room: /join [room name]
  • +
+
+
+
+ + + + + + + diff --git a/public/js/chat.js b/public/js/chat.js new file mode 100644 index 0000000..df1dc4e --- /dev/null +++ b/public/js/chat.js @@ -0,0 +1,43 @@ +var Chat = function(socket) { + this.socket = socket; +}; + +Chat.prototype.sendMessage = function(room, text) { + var message = { + room: room, + text: text + }; + this.socket.emit('message', message); +}; + +Chat.prototype.changeRoom = function(room) { + this.socket.emit('join', { + newRoom: room + }); +}; + +Chat.prototype.processCommand = function(command) { + var words = command.split(' '); + var command = words[0] + .substring(1, words[0].length) + .toLowerCase(); + var message = false; + + switch(command) { + case 'join': + words.shift(); + var room = words.join(' '); + this.changeRoom(room); + break; + case 'nick': + words.shift(); + var name = words.join(' '); + this.socket.emit('nameAttempt', name); + break; + default: + message = 'Unrecognized command.'; + break; + }; + + return message; +}; diff --git a/public/js/chat_ui.js b/public/js/chat_ui.js new file mode 100644 index 0000000..049db60 --- /dev/null +++ b/public/js/chat_ui.js @@ -0,0 +1,79 @@ +function divEscapedContentElement(message) { + return $('
').text(message); +} + +function divSystemContentElement(message) { + return $('
').html('' + message + ''); +} + +function processUserInput(chatApp, socket) { + var message = $('#send-message').val(); + var systemMessage; + + if (message.charAt(0) == '/') { + systemMessage = chatApp.processCommand(message); + if (systemMessage) { + $('#messages').append(divSystemContentElement(systemMessage)); + } + } else { + chatApp.sendMessage($('#room').text(), message); + $('#messages').append(divEscapedContentElement(message)); + $('#messages').scrollTop($('#messages').prop('scrollHeight')); + } + + $('#send-message').val(''); +} + +var socket = io.connect(); + +$(document).ready(function() { + var chatApp = new Chat(socket); + + socket.on('nameResult', function(result) { + var message; + + if (result.success) { + message = 'You are now known as ' + result.name + '.'; + } else { + message = result.message; + } + $('#messages').append(divSystemContentElement(message)); + }); + + socket.on('joinResult', function(result) { + $('#room').text(result.room); + $('#messages').append(divSystemContentElement('Room changed.')); + }); + + socket.on('message', function (message) { + var newElement = $('
').text(message.text); + $('#messages').append(newElement); + }); + + socket.on('rooms', function(rooms) { + $('#room-list').empty(); + + for(var room in rooms) { + room = room.substring(1, room.length); + if (room != '') { + $('#room-list').append(divEscapedContentElement(room)); + } + } + + $('#room-list div').click(function() { + chatApp.processCommand('/join ' + $(this).text()); + $('#send-message').focus(); + }); + }); + + setInterval(function() { + socket.emit('rooms'); + }, 1000); + + $('#send-message').focus(); + + $('#send-form').submit(function() { + processUserInput(chatApp, socket); + return false; + }); +});