diff --git a/index.html b/index.html index fe9b4e0..ad5ca3e 100644 --- a/index.html +++ b/index.html @@ -15,11 +15,11 @@
-
-
-

JS Marketplace

-
-
+ + + + +
@@ -29,13 +29,23 @@
+ +
+ +
-
+
+

Messages

+
+
+
+

Active Users

+
diff --git a/index.js b/index.js index bf29f61..dc5011b 100644 --- a/index.js +++ b/index.js @@ -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 = $("").text((new Date()).toGMTString()).addClass("timestamp"); + var item = $("
").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 = $("").text(nyms[i]).addClass("nym"); + var s = statuses[nyms[i]]; + if (s) { + container.append($("
").append([n, $("").text(s).addClass("nym_status")])); + } else { + container.append($("
").append(n)); + } + } +} + +function outputState(state) { + outputItem([$("").text(state).addClass(state).addClass("state")]) + .addClass("state_" + state); } function outputUtterance(who, what) { - var stamp = $("").text((new Date()).toGMTString()).addClass("timestamp"); - var nymLabel = $("").text(who).addClass("nym"); - var utterance = $("").text(what).addClass("utterance"); - var o = $("#chat_output"); - o.append($("
") - .append([stamp, nymLabel, utterance]) - .addClass("utterance")); - o[0].scrollTop = o[0].scrollHeight; + outputItem([$("").text(who).addClass("nym"), + $("").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: diff --git a/style.css b/style.css index fc50d76..7d581ee 100644 --- a/style.css +++ b/style.css @@ -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; +} \ No newline at end of file