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

View File

@ -1,12 +1,8 @@
$(function() {
var socket = io.connect("");
socket.on(
"event",
function(event) {
console.log(event);
View[event.action](event);
}
);
socket.on("event", function(event) {
render(event);
});
var chat = $("#chat");
var sidebar = $("#sidebar");
@ -17,11 +13,77 @@ $(function() {
var messages = $("#messages").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 = {};
View.refresh = function(event) {
var data = event.data;
sidebar.html(
Mustache.render(networks, {
networks: data
@ -48,32 +110,23 @@ $(function() {
};
View.add = function(event) {
var target = "";
var render = "";
var target = $("[data-id='" + event.target + "'] ");
switch (event.type) {
case "user":
target = ".users";
render = Mustache.render(
users, {users: event.data}
);
case "users":
target = target.find(".users");
target.html(Mustache.render(users, {users: event.data}));
break;
case "message":
target = ".messages";
render = Mustache.render(
messages, {messages: event.data}
);
break;
}
if (target != "") {
target = $("[data-id='" + event.target + "'] " + target);
case "messages":
var keepAtBottom = target.isScrollBottom();
target.append(render);
target = target.find(".messages");
target.append(Mustache.render(messages, {messages: event.data}));
if (keepAtBottom) {
target.scrollToBottom();
}
break;
}
};
@ -81,10 +134,6 @@ $(function() {
$("[data-id='" + event.target + "']").remove();
};
View.change = function(event) {
// ..
};
chat.on("submit", "form", function() {
var input = $(this).find(".input");
var text = input.val();
@ -107,7 +156,10 @@ $(function() {
(function() {
var highest = 1;
$.fn.bringToTop = function() {
return this.css('z-index', highest++);
return this
.css('z-index', highest++)
.find("input")
.focus();
};
$.fn.scrollToBottom = function() {

View File

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