WIP chatApp
This commit is contained in:
45
app/main/chat.php
Normal file
45
app/main/chat.php
Normal 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
34
app/websockets.php
Normal 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
6
cmd.php
Normal file
@@ -0,0 +1,6 @@
|
||||
<?php
|
||||
require 'vendor/autoload.php';
|
||||
|
||||
use Exodus4D\Socket;
|
||||
|
||||
new Socket\WebSockets();
|
||||
10
composer.json
Normal file
10
composer.json
Normal file
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"require": {
|
||||
"cboden/ratchet": "dev-master"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Exodus4D\\Socket\\": "app/"
|
||||
}
|
||||
}
|
||||
}
|
||||
72
css/style.css
Normal file
72
css/style.css
Normal 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
53
index.html
Normal 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">●</span>
|
||||
Notification: <span id="notification-status" class="red">●</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
155
js/app.js
Normal 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
87
js/worker/chat.js
Normal 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
90
js/worker/chat_new.js
Normal 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
18
js/worker/message.js
Normal 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;
|
||||
}
|
||||
};
|
||||
Reference in New Issue
Block a user