Improved the event/render flow

This commit is contained in:
Mattias Erming 2014-03-07 04:18:53 +01:00
parent acfeac27f4
commit efa3fcd7ab
4 changed files with 191 additions and 150 deletions

View file

@ -36,10 +36,6 @@ h2 {
cursor: pointer; cursor: pointer;
padding: 0 12px; padding: 0 12px;
} }
#sidebar .channel[data-id='0'] {
background: #eaeaea;
border-bottom-color: #e5e5e5;
}
#sidebar .channel:first-child { #sidebar .channel:first-child {
color: #333; color: #333;
overflow: hidden; overflow: hidden;
@ -47,6 +43,10 @@ h2 {
#sidebar .channel:hover { #sidebar .channel:hover {
text-decoration: underline; text-decoration: underline;
} }
#sidebar .network:first-child .channel:first-child {
background: #eaeaea;
border-bottom-color: #e5e5e5;
}
#chat { #chat {
bottom: 0; bottom: 0;
left: 200px; left: 200px;

View file

@ -1,12 +1,8 @@
$(function() { $(function() {
var socket = io.connect(""); var socket = io.connect("");
socket.on( socket.on("event", function(event) {
"event", render(event);
function(event) { });
console.log(event);
View[event.action](event);
}
);
var chat = $("#chat"); var chat = $("#chat");
var sidebar = $("#sidebar"); var sidebar = $("#sidebar");
@ -17,11 +13,77 @@ $(function() {
var messages = $("#messages").html(); var messages = $("#messages").html();
var users = $("#users").html() var users = $("#users").html()
function render(event) {
var type = event.type;
var data = event.data;
var action = event.action;
var target = event.target;
if (action == "REMOVE") {
remove(target);
return;
}
if (target != "") {
target = $("[data-id='" + target + "']");
}
switch (type) {
case "NETWORK":
case "CHANNEL":
refresh(data);
break;
case "USER":
target = target.find(".users");
target.html(Mustache.render(users, {users: event.data}));
break;
case "MESSAGE":
var keepAtBottom = target.isScrollBottom();
target = target.find(".messages");
target.append(Mustache.render(messages, {messages: event.data}));
if (keepAtBottom) {
target.scrollToBottom();
}
break;
}
}
function remove(id) {
$("[data-id='" + id + "']").remove();
}
function refresh(data) {
chat.html("");
var partials = {
users: users,
messages: messages
};
data.forEach(function(network) {
chat.append(Mustache.render(channels, network, partials));
});
sidebar.html(
Mustache.render(networks, {
networks: data
})
);
chat.find(".messages").scrollToBottom();
chat.find(".window")
// Sort windows by `data-id` value.
.sort(function(a, b) { return ($(a).data("id") - $(b).data("id")); })
.last()
.bringToTop()
.find(".input")
.focus();
}
var View = {}; var View = {};
View.refresh = function(event) { View.refresh = function(event) {
var data = event.data; var data = event.data;
sidebar.html( sidebar.html(
Mustache.render(networks, { Mustache.render(networks, {
networks: data networks: data
@ -48,32 +110,23 @@ $(function() {
}; };
View.add = function(event) { View.add = function(event) {
var target = ""; var target = $("[data-id='" + event.target + "'] ");
var render = "";
switch (event.type) { switch (event.type) {
case "user":
target = ".users"; case "users":
render = Mustache.render( target = target.find(".users");
users, {users: event.data} target.html(Mustache.render(users, {users: event.data}));
);
break; break;
case "message": case "messages":
target = ".messages";
render = Mustache.render(
messages, {messages: event.data}
);
break;
}
if (target != "") {
target = $("[data-id='" + event.target + "'] " + target);
var keepAtBottom = target.isScrollBottom(); var keepAtBottom = target.isScrollBottom();
target.append(render); target = target.find(".messages");
target.append(Mustache.render(messages, {messages: event.data}));
if (keepAtBottom) { if (keepAtBottom) {
target.scrollToBottom(); target.scrollToBottom();
} }
break;
} }
}; };
@ -81,10 +134,6 @@ $(function() {
$("[data-id='" + event.target + "']").remove(); $("[data-id='" + event.target + "']").remove();
}; };
View.change = function(event) {
// ..
};
chat.on("submit", "form", function() { chat.on("submit", "form", function() {
var input = $(this).find(".input"); var input = $(this).find(".input");
var text = input.val(); var text = input.val();
@ -107,7 +156,10 @@ $(function() {
(function() { (function() {
var highest = 1; var highest = 1;
$.fn.bringToTop = function() { $.fn.bringToTop = function() {
return this.css('z-index', highest++); return this
.css('z-index', highest++)
.find("input")
.focus();
}; };
$.fn.scrollToBottom = function() { $.fn.scrollToBottom = function() {

View file

@ -2,7 +2,7 @@ var _ = require("lodash");
var moment = require("moment"); var moment = require("moment");
var models = exports; var models = exports;
var id = 0; var id = 1;
models.Network = function(attr) { models.Network = function(attr) {
attr = attr || {}; attr = attr || {};
@ -56,3 +56,11 @@ models.Event = function(attr) {
type: "" type: ""
})); }));
}; };
models.Target = function(attr) {
attr = attr || {};
_.extend(this, _.defaults(attr, {
network: "",
channel: ""
}));
};

View file

@ -1,149 +1,130 @@
var _ = require("lodash");
var connect = require("connect"); var connect = require("connect");
var models = require("./models.js");
var _ = require("lodash");
var irc = require("irc"); var irc = require("irc");
var io = require("socket.io"); var io = require("socket.io");
var models = require("./models.js");
exports.listen = listen; exports.listen = listen;
var sockets; var sockets = false;;
var networks = []; var networks = [];
addNetwork("Lobby", false); addToServer(
"NETWORK",
new models.Network({address: "Start"})
);
function listen(port) { function listen(port) {
var http = connect() var http = connect()
.use(connect.static("client")) .use(connect.static("client"))
.listen(port); .listen(port);
sockets = io.listen(http).sockets; sockets = io
sockets.on("connection", function(socket) { .listen(http)
init(socket); .on("connection", initSocket)
}); .sockets;
} }
function init(socket) { function initSocket(socket) {
socket.on("input", handleUserInput);
refresh(); refresh();
socket.on( }
"input",
function(input) { function sendEvent(params) {
handleUserInput(input) if (sockets) {
} sockets.emit("event", new models.Event(params));
); }
} }
function refresh() { function refresh() {
if (typeof sockets === "undefined") { sendEvent({action: "RENDER", type: "NETWORK", data: networks});
return;
}
sockets.emit("event", new models.Event({
action: "refresh",
data: networks
}));
} }
function handleUserInput(input) { function addToServer(type, model, target) {
var text = input.text; switch (type) {
var target = getChannel(input.id);
if (text.charAt(0) != "/") { case "NETWORK":
return addMessage(target, text); var channel = new models.Channel({
} name: model.address,
type: "network"
});
var args = text.substr(1).split(" "); model.channels.push(channel);
var cmd = args[0].toUpperCase(); networks.push(model);
switch (cmd) { refresh();
case "SERVER":
case "CONNECT":
if (args[1]) {
addNetwork(args[1], true);
}
break;
case "JOIN":
if (args[1]) {
target.network.channels.push(
new models.Channel({
name: args[1]
})
);
refresh();
}
break; break;
case "PART": case "CHANNEL":
target.network.channels = target.network.channels.push(model);
_.without(target.network.channels, target.channel);
refresh(); refresh();
break; break;
default: case "MESSAGE":
addMessage( target.channel.messages
target, .push(model);
"Command '/" + args[0] + "' does not exist." sendEvent({
action: "RENDER",
type: "MESSAGE",
target: target.channel.id,
data: model
});
break;
}
}
function handleUserInput(input) {
var id = input.id;
var text = input.text;
var args = text.substr(1).split(' ');
var cmd = text.charAt(0) == "/" ? args[0].toUpperCase()
: "MESSAGE";
var target = getTarget(id);
switch (cmd) {
case "SERVER":
case "CONNECT":
addToServer(
"NETWORK",
new models.Network({address: args[1]})
); );
break; break;
} case "JOIN":
} addToServer(
"CHANNEL",
function addNetwork(addr, bool) { new models.Channel({name: args[1]}),
bool = bool || false; target
var chan = new models.Channel({
name: addr,
type: "network"
});
var network = new models.Network({
channels: [chan]
});
networks.push(network);
refresh();
if (addr == "Lobby") {
return;
}
network.client = new irc.Client(addr, "default_user");
network.client.addListener("raw", function() {
handleEvent(
network, arguments
); );
}); break;
}
case "PART":
target.network.channels = _.reject(target.network.channels, {id: id});
refresh();
break;
function handleEvent(network) { case "MESSAGE":
var args = arguments; addToServer(
var target = { "MESSAGE",
network: network, new models.Message({text: input.text}),
channel: network.channels[0] getTarget(id)
}; );
break;
console.log(args[1]); }
addMessage(target, args[1][0].args); }
}
function getTarget(id) {
function addMessage(target, text) { var find;
var message = _.extend(new models.Message, {text: text}); _.each(networks, function(n) {
target.channel.messages.push(message); find = {network: n, channel: _.findWhere(n.channels, {id: id})};
sockets.emit("event", new models.Event({ if (find.channel)
action: "add", return;
type: "message", });
target: target.channel.id, if (find.channel) {
data: message return new models.Target(find);
}));
}
function getChannel(id) {
for (var i = 0; i < networks.length; i++) {
var find = {
network: networks[i],
channel: _.findWhere(networks[i].channels, {id: id})
};
if (typeof find.channel !== "undefined") {
return find;
}
} }
} }