WIP chatApp

This commit is contained in:
Exodus4D
2016-11-12 14:38:50 +01:00
parent fd8e0749f9
commit e8123dfb61
10 changed files with 570 additions and 0 deletions

45
app/main/chat.php Normal file
View File

@@ -0,0 +1,45 @@
<?php
namespace Exodus4D\Socket\Main;
use Ratchet\MessageComponentInterface;
use Ratchet\ConnectionInterface;
class Chat implements MessageComponentInterface {
protected $clients;
public function __construct() {
$this->clients = new \SplObjectStorage;
echo "__construct() \n";
}
public function onOpen(ConnectionInterface $conn) {
//store the new connection
$this->clients->attach($conn);
echo "NEW connection! ({$conn->resourceId})\n";
}
public function onMessage(ConnectionInterface $from, $msg) {
$numRecv = count($this->clients) - 1;
echo sprintf('Connection %d sending to %d other connection%s' . "\n"
, $from->resourceId, $numRecv, $numRecv == 1 ? '' : 's');
foreach ($this->clients as $client) {
// if ($from !== $client) {
$client->send($msg);
// }
}
}
public function onClose(ConnectionInterface $conn) {
$this->clients->detach($conn);
echo "Connection {$conn->resourceId} has disconnected\n";
}
public function onError(ConnectionInterface $conn, \Exception $e) {
echo "An error has occurred: {$e->getMessage()}\n";
$conn->close();
}
}

34
app/websockets.php Normal file
View File

@@ -0,0 +1,34 @@
<?php
/**
* Created by PhpStorm.
* User: Exodus
* Date: 01.11.2016
* Time: 18:21
*/
namespace Exodus4D\Socket;
use Ratchet\Server\IoServer;
use Ratchet\Http\HttpServer;
use Ratchet\WebSocket\WsServer;
class WebSockets {
function __construct(){
$this->startChat();
}
private function startChat(){
$server = IoServer::factory(
new HttpServer(
new WsServer(
new Main\Chat()
)
),
8020
);
$server->run();
}
}

6
cmd.php Normal file
View File

@@ -0,0 +1,6 @@
<?php
require 'vendor/autoload.php';
use Exodus4D\Socket;
new Socket\WebSockets();

10
composer.json Normal file
View File

@@ -0,0 +1,10 @@
{
"require": {
"cboden/ratchet": "dev-master"
},
"autoload": {
"psr-4": {
"Exodus4D\\Socket\\": "app/"
}
}
}

72
css/style.css Normal file
View File

@@ -0,0 +1,72 @@
.hidden {
display: none;
}
#wrapper {
width: 800px;
margin: 0 auto;
}
#leave-room {
margin-bottom: 10px;
float: right;
}
#user-container {
width: 500px;
margin: 0 auto;
text-align: center;
}
#main-container {
width: 500px;
margin: 0 auto;
}
#messages {
height: 300px;
width: 500px;
border: 1px solid #ccc;
padding: 20px;
text-align: left;
overflow-y: scroll;
}
#msg-container {
padding: 20px;
}
#msg {
width: 400px;
}
.user {
font-weight: bold;
}
.msg {
margin-bottom: 10px;
overflow: hidden;
}
.time {
float: right;
color: #939393;
font-size: 13px;
}
.details {
margin-top: 20px;
}
#socket-status,
#notification-status {
font-size: 40px;
}
.red{
color: red;
}
.green{
color: green;
}

53
index.html Normal file
View File

@@ -0,0 +1,53 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Chat</title>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/2.1.4/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/handlebars.js/3.0.3/handlebars.min.js"></script>
<script src="http://cdnjs.cloudflare.com/ajax/libs/moment.js/2.10.2/moment.min.js"></script>
<link rel="stylesheet" href="css/style.css">
</head>
<body>
<div id="wrapper">
Status: <span id="socket-status" class="red">&#x25cf;</span>
Notification: <span id="notification-status" class="red">&#x25cf;</span>
<div id="user-container">
<label for="user">What's your name?</label>
<input type="text" id="user" name="user">
<button type="button" id="join-chat">Join Chat</button>
</div>
<div id="main-container" class="hidden">
<button type="button" id="leave-room">Leave</button>
<button type="button" id="toggle-notification">Notification</button>
<div id="messages">
</div>
<div id="msg-container">
<input type="text" id="msg" name="msg">
<button type="button" id="send-msg">Send</button>
</div>
</div>
</div>
<script id="messages-template" type="text/x-handlebars-template">
{{#each messages}}
<div class="msg">
<div class="time">{{time}}</div>
<div class="details">
<span class="user">{{user}}</span>: <span class="text">{{text}}</span>
</div>
</div>
{{/each}}
</script>
<script src="js/worker/message.js"></script>
<script src="js/app.js"></script>
</body>
</html>

155
js/app.js Normal file
View File

@@ -0,0 +1,155 @@
window.onload = function (){
var msgWorker = this.msgWorker;
var worker = new SharedWorker('js/worker/chat_new.js', 'worker_name');
worker.port.addEventListener('message', function(e){
let load = e.data;
load.__proto__ = msgWorker.prototype;
switch(load.command){
case 'ws:open':
// WebSocket in SharedWorker is open
setSocketStatus(true);
initChat();
break;
case 'ws:send':
updateMessages(load.data());
break;
case 'ws:closed':
setSocketStatus(false);
break;
}
}, false);
worker.onerror = function(e){
console.error('SharedWorker onerror:');
};
worker.port.start();
var msgWorkerInit = new msgWorker('ws:init');
msgWorkerInit.data({
uri: 'ws://wstest.local:8080'
});
worker.port.postMessage(msgWorkerInit);
// Chat init ==================================================================================
var user;
var messages = [];
var messages_template = Handlebars.compile($('#messages-template').html());
var initChat = function(){
$('#join-chat').click(function(){
user = $('#user').val();
$('#user-container').addClass('hidden');
$('#main-container').removeClass('hidden');
var msgWorkerSend = new msgWorker('ws:send');
msgWorkerSend.data({
'user': user,
'text': user + ' entered the room',
'time': moment().format('hh:mm a')
});
worker.port.postMessage(msgWorkerSend);
$('#user').val('');
});
$('#send-msg').click(function(){
var text = $('#msg').val();
var msgWorkerSend = new msgWorker('ws:send');
msgWorkerSend.data({
'user': user,
'text': text,
'time': moment().format('hh:mm a')
});
worker.port.postMessage(msgWorkerSend);
$('#msg').val('');
});
$('#leave-room').click(function(){
var msgWorkerSend = new msgWorker('ws:send');
msgWorkerSend.data({
'user': user,
'text': user + ' has left the room',
'time': moment().format('hh:mm a')
});
worker.port.postMessage(msgWorkerSend);
$('#messages').html('');
messages = [];
$('#main-container').addClass('hidden');
$('#user-container').removeClass('hidden');
});
};
var setSocketStatus = function(status){
$('#socket-status').toggleClass('red', !status).toggleClass('green', status);
};
var updateMessages = function(msg){
messages.push(msg);
var messages_html = messages_template({'messages': messages});
$('#messages').html(messages_html);
$("#messages").animate({ scrollTop: $('#messages')[0].scrollHeight}, 1000);
};
// Notification init ==========================================================================
var updateNotification = function(status){
$('#notification-status').toggleClass('red', !status).toggleClass('green', status);
};
var notifyMe = function(){
var msgWorkerNotify = new msgWorker('ws:notify');
if (Notification.permission === 'granted'){
msgWorkerNotify.data({
status: true
});
worker.port.postMessage(msgWorkerNotify);
updateNotification(true);
}else{
Notification.requestPermission(function (permission) {
msgWorkerNotify.data({
status: permission === 'granted'
});
worker.port.postMessage(msgWorkerNotify);
updateNotification(permission === 'granted');
});
}
};
$('#toggle-notification').on('click', notifyMe);
// ============================================================================================
/*
window.onbeforeunload = function() {
var msgWorkerClose = new msgWorker('ws:close');
worker.port.postMessage(msgWorkerClose);
//console.log('test close');
//worker.port.close();
return 'sdf';
};
*/
};

87
js/worker/chat.js Normal file
View File

@@ -0,0 +1,87 @@
'use strict';
self.importScripts('message.js');
var socket = new WebSocket(self.name);
var ports = [];
var notifications = false;
var tmp = self;
console.log(socket._socket);
self.addEventListener('connect', function (event) {
var port = event.ports[0];
ports.push(port);
console.log('B: ' + socket.readyState);
port.onmessage = function (event) {
let load = event.data;
load.__proto__ = msgWorker.prototype;
console.log('C: ' + socket.readyState);
switch(load.command){
case 'send':
socket.send(JSON.stringify(load.data()));
break;
case 'WS_close':
closeSocket(socket);
break;
case 'notify':
notifications = load.data().status;
}
};
if(socket.readyState === socket.OPEN){
var msgWorkerOpen = new msgWorker('ready');
port.postMessage(msgWorkerOpen);
}
port.start();
}, false);
socket.onopen = function(e){
var msgWorkerOpen = new msgWorker('open');
for (var i = 0; i < ports.length; i++) {
ports[i].postMessage(msgWorkerOpen);
}
socket.onmessage = function(e){
let load = JSON.parse(e.data);
var msgWorkerSend = new msgWorker('send');
msgWorkerSend.data(load);
for (var i = 0; i < ports.length; i++) {
ports[i].postMessage(msgWorkerSend);
}
if(notifications){
new Notification('Message: ' + load.text);
}
};
socket.onclose = function(){
console.log(this.remoteAddress);
console.info('ws: onclose()');
};
socket.onerror = function(){
console.error('ws: onerror()');
};
};
// Util ================================================================
var closeSocket = function(socket){
// only close if active
if(socket.readyState === socket.OPEN){
// send "close" event before close call
var msgWorkerWsClosed = new msgWorker('WS_closed');
for (var i = 0; i < ports.length; i++) {
ports[i].postMessage(msgWorkerWsClosed);
}
socket.close();
}
};

90
js/worker/chat_new.js Normal file
View File

@@ -0,0 +1,90 @@
'use strict';
self.importScripts('message.js');
var socket = null;
var ports = [];
var notifications = false;
var initSocket = function(uri){
var msgWorkerOpen = new msgWorker('ws:open');
if(socket === null){
socket = new WebSocket(uri);
socket.onopen = function(e){
//
ports[ports.length - 1].postMessage(msgWorkerOpen);
socket.onmessage = function(e){
let load = JSON.parse(e.data);
var msgWorkerSend = new msgWorker('ws:send');
msgWorkerSend.data(load);
for (var i = 0; i < ports.length; i++) {
ports[i].postMessage(msgWorkerSend);
}
if(notifications){
new Notification('Message: ' + load.text);
}
};
socket.onclose = function(){
console.info('ws: onclose()');
};
socket.onerror = function(){
console.error('ws: onerror()');
};
}
}else{
// socket still open
ports[ports.length - 1].postMessage(msgWorkerOpen);
}
};
self.addEventListener('connect', function (event){
var port = event.ports[0];
ports.push(port);
port.addEventListener('message', function (e){
let load = e.data;
load.__proto__ = msgWorker.prototype;
switch(load.command){
case 'ws:init':
initSocket(load.data().uri);
break;
case 'ws:send':
socket.send(JSON.stringify(load.data()));
break;
case 'ws:close':
closeSocket(socket);
break;
case 'ws:notify':
notifications = load.data().status;
break;
}
}, false);
port.start();
}, false);
// Util ================================================================
var closeSocket = function(socket){
// only close if active
console.log(socket.readyState + ' - ' + socket.OPEN);
if(socket.readyState === socket.OPEN){
// send "close" event before close call
var msgWorkerWsClosed = new msgWorker('ws:closed');
for (var i = 0; i < ports.length; i++) {
ports[i].postMessage(msgWorkerWsClosed);
}
socket.close();
}
};

18
js/worker/message.js Normal file
View File

@@ -0,0 +1,18 @@
var msgWorker = class MsgWorkerTest {
constructor(cmd){
this.cmd = cmd;
this.msgBody = null;
}
get command(){
return this.cmd;
}
data(data) {
if(data){
this.msgBody = data;
}
return this.msgBody;
}
};