Nymlist; status; connection state

This commit is contained in:
Tony Garnock-Jones 2013-10-31 17:38:38 +00:00
parent 2203795902
commit 671c5dbda6
3 changed files with 135 additions and 31 deletions

View File

@ -15,11 +15,11 @@
</head>
<body>
<div class="container-fluid">
<div class="row-fluid">
<div class="span12">
<h1>JS Marketplace</h1>
</div>
</div>
<!-- <div class="row-fluid"> -->
<!-- <div class="span12"> -->
<!-- <h1>JS Marketplace</h1> -->
<!-- </div> -->
<!-- </div> -->
<div class="row-fluid">
<div class="span12">
<form class="form-horizontal" name="nym_form">
@ -29,13 +29,23 @@
<div class="controls">
<input type="text" id="nym" name="nym" value="pseud">
</div>
<label class="control-label" for="status">Status:</label>
<div class="controls">
<input type="text" id="status" name="status" value="">
</div>
</div>
</fieldset>
</form>
</div>
</div>
<div class="row-fluid">
<div id="chat_output" class="span12">
<div class="span9">
<h2>Messages</h2>
<div id="chat_output"></div>
</div>
<div class="span3">
<h2>Active Users</h2>
<div id="nymlist"></div>
</div>
</div>
<div class="row-fluid">

View File

@ -100,9 +100,15 @@ function WebSocketConnection(label, wsurl, shouldReconnect) {
this.deduplicator = new Deduplicator();
}
WebSocketConnection.prototype.statusRoute = function (status) {
return pub([this.label + "_state", status]);
};
WebSocketConnection.prototype.relayRoutes = function () {
// fresh copy each time, suitable for in-place extension/mutation
return [pub([this.label, __, __], 0, 1000), sub([this.label, __, __], 0, 1000)];
return [this.statusRoute(this.isConnected() ? "connected" : "disconnected"),
pub([this.label, __, __], 0, 1000),
sub([this.label, __, __], 0, 1000)];
};
WebSocketConnection.prototype.aggregateRoutes = function () {
@ -119,7 +125,6 @@ WebSocketConnection.prototype.aggregateRoutes = function () {
};
WebSocketConnection.prototype.boot = function () {
World.updateRoutes(this.aggregateRoutes());
this.reconnect();
};
@ -224,6 +229,10 @@ WebSocketConnection.prototype.onmessage = function (wse) {
WebSocketConnection.prototype.onclose = function (e) {
var self = this;
// console.log("onclose", e);
// Update routes to give clients some indication of the discontinuity
World.updateRoutes(this.aggregateRoutes());
if (this.shouldReconnect) {
console.log("reconnecting to " + this.wsurl + " in " + this.reconnectDelay + "ms");
setTimeout(World.wrap(function () { self.reconnect(); }), this.reconnectDelay);
@ -238,34 +247,55 @@ WebSocketConnection.prototype.onclose = function (e) {
///////////////////////////////////////////////////////////////////////////
// Main
function outputItem(item) {
var stamp = $("<span/>").text((new Date()).toGMTString()).addClass("timestamp");
var item = $("<div/>").append([stamp].concat(item));
var o = $("#chat_output");
o.append(item);
o[0].scrollTop = o[0].scrollHeight;
return item;
}
function updateNymList(rs) {
var nyms = [];
var statuses = {};
for (var i = 0; i < rs.length; i++) {
var p = rs[i].pattern;
if (p[0] === "broker"
&& p[1] === 0
&& p[2][1] === "says")
{
if (p[0] === "broker" && p[1] === 0 && p[2][1] === "says") {
nyms.push(p[2][0]);
}
if (p[0] === "broker" && p[1] === 0 && p[2][1] === "status") {
statuses[p[2][0]] = p[2][2];
}
}
console.log(nyms);
var container = $("#nymlist");
container[0].innerHTML = ""; // remove all children
for (var i = 0; i < nyms.length; i++) {
var n = $("<span/>").text(nyms[i]).addClass("nym");
var s = statuses[nyms[i]];
if (s) {
container.append($("<div/>").append([n, $("<span/>").text(s).addClass("nym_status")]));
} else {
container.append($("<div/>").append(n));
}
}
}
function outputState(state) {
outputItem([$("<span/>").text(state).addClass(state).addClass("state")])
.addClass("state_" + state);
}
function outputUtterance(who, what) {
var stamp = $("<span/>").text((new Date()).toGMTString()).addClass("timestamp");
var nymLabel = $("<span/>").text(who).addClass("nym");
var utterance = $("<span/>").text(what).addClass("utterance");
var o = $("#chat_output");
o.append($("<div/>")
.append([stamp, nymLabel, utterance])
.addClass("utterance"));
o[0].scrollTop = o[0].scrollHeight;
outputItem([$("<span/>").text(who).addClass("nym"),
$("<span/>").text(what).addClass("utterance")]).addClass("utterance");
}
$(document).ready(function () {
$("#chat_form").submit(function (e) { e.preventDefault(); return false; });
$("#nym_form").submit(function (e) { e.preventDefault(); return false; });
$("#nym").val("nym" + Math.floor(Math.random() * 65536));
var g = new Ground(function () {
console.log('starting ground boot');
@ -273,20 +303,43 @@ $(document).ready(function () {
spawnJQueryDriver();
World.spawn(new WebSocketConnection("broker", "ws://localhost:8000/", true));
World.spawn({
// step: function () { console.log('dummy step'); },
// Monitor connection, notifying connectivity changes
state: null,
boot: function () {
World.updateRoutes([sub(["broker_state", __], 0, 1)]);
},
handleEvent: function (e) {
if (e.type === "routes") {
if (e.routes.length > 0) {
var newState = e.routes[0].pattern[1];
if (this.state != newState) {
outputState(newState);
this.state = newState;
}
}
}
}
});
World.spawn({
// Actual chat functionality
peers: new PresenceDetector(),
peerMap: {},
boot: function () {
World.updateRoutes(this.subscriptions());
},
nym: function () {
return $("#nym").val();
},
nym: function () { return $("#nym").val(); },
currentStatus: function () { return $("#status").val(); },
subscriptions: function () {
return [sub(["jQuery", "#send_chat", "click", __]),
sub(["jQuery", "#nym", "change", __]),
sub(["jQuery", "#status", "change", __]),
pub(["broker", 0, [this.nym(), "says", __]]),
sub(["broker", 0, [__, "says", __]], 0, 1)];
pub(["broker", 0, [this.nym(), "status", this.currentStatus()]]),
sub(["broker", 0, [__, "says", __]], 0, 1),
sub(["broker", 0, [__, "status", __]], 0, 1)];
},
handleEvent: function (e) {
var self = this;
switch (e.type) {
case "routes":
updateNymList(e.routes);
@ -304,6 +357,7 @@ $(document).ready(function () {
}
break;
case "#nym":
case "#status":
World.updateRoutes(this.subscriptions());
break;
default:

View File

@ -1,17 +1,41 @@
div.utterance span.timestamp {
span.timestamp {
color: #d0d0d0;
}
div.utterance span.timestamp:after {
span.timestamp:after {
content: " ";
}
div.utterance span.nym {
.utterance span.nym:after {
content: ": ";
}
span.arrived:after {
content: " arrived";
}
span.departed:after {
content: " departed";
}
div.notification {
background-color: #eeeeff;
}
span.state.connected, span.arrived {
color: #00c000;
}
div.utterance span.nym:after {
content: ": ";
span.state.disconnected, span.departed {
color: #c00000;
}
div.state_disconnected {
background-color: #ffeeee;
}
div.state_connected {
background-color: #eeffee;
}
#chat_output {
@ -22,3 +46,19 @@ div.utterance span.nym:after {
#chat_input {
width: 80%;
}
.nym {
color: #00c000;
}
.nym_status:before {
content: " (";
}
.nym_status:after {
content: ")";
}
.nym_status {
font-size: smaller;
}