diff --git a/client/css/style.css b/client/css/style.css index 801a2ae6..e134aeeb 100644 --- a/client/css/style.css +++ b/client/css/style.css @@ -184,6 +184,9 @@ button { #sidebar .chan.channel:before, #chat .channel .title:before { content: "\f0f6"; /* http://fontawesome.io/icon/file-text-o/ */ } +#sidebar .chan.special:before, +#chat .channel .title:before { content: "\f03a"; /* http://fontawesome.io/icon/list/ */ } + #footer .sign-in:before { content: "\f023"; /* http://fontawesome.io/icon/lock/ */ } #footer .connect:before { content: "\f067"; /* http://fontawesome.io/icon/plus/ */ } #footer .settings:before { content: "\f013"; /* http://fontawesome.io/icon/cog/ */ } @@ -712,6 +715,10 @@ button { right: 180px; } +#chat .special { + bottom: -47px; +} + #viewport.rt .chat { right: 0; } @@ -731,11 +738,13 @@ button { } #chat .lobby .chat, +#chat .special .chat, #chat .query .chat { right: 0; } #chat .lobby .sidebar, +#chat .special .sidebar, #chat .query .sidebar { display: none; } @@ -840,6 +849,11 @@ button { } #loading a, +#chat .special .time, +#chat .special .from { + display: none; +} + #chat a { color: #50a656; } @@ -905,6 +919,46 @@ button { overflow: hidden; } +#chat .msg.channel_list_loading .text { + color: #999; + font-style: italic; + padding-left: 20px; +} + +#chat .msg.channel_list_truncated .text { + color: #f00; + padding-left: 20px; +} + +#chat table.channel-list { + margin: 5px 10px; + width: calc(100% - 30px); +} + +#chat table.channel-list th, +#chat table.channel-list td { + padding: 5px; + vertical-align: top; + border-bottom: #eee 1px solid; +} + +#chat table.channel-list .channel, +#chat table.channel-list .topic { + text-align: left; +} + +#chat table.channel-list .users { + text-align: center; +} + +#chat table.channel-list td.channel .inline-channel { + color: #428bca; +} + +#chat table.channel-list td { + color: #555; +} + #chat.hide-join .join, #chat.hide-mode .mode, #chat.hide-motd .motd, diff --git a/client/js/lounge.js b/client/js/lounge.js index cca024b9..089a0931 100644 --- a/client/js/lounge.js +++ b/client/js/lounge.js @@ -232,6 +232,7 @@ $(function() { "action", "whois", "ctcp", + "channel_list", ].indexOf(type) !== -1) { data.msg.template = "actions/" + type; template = "msg_action"; @@ -616,7 +617,7 @@ $(function() { output += render("contextmenu_divider"); output += render("contextmenu_item", { class: "close", - text: target.hasClass("lobby") ? "Disconnect" : target.hasClass("query") ? "Close" : "Leave", + text: target.hasClass("lobby") ? "Disconnect" : target.hasClass("channel") ? "Leave" : "Close", data: target.data("target") }); } diff --git a/client/views/actions/channel_list.tpl b/client/views/actions/channel_list.tpl new file mode 100644 index 00000000..8fab73d0 --- /dev/null +++ b/client/views/actions/channel_list.tpl @@ -0,0 +1,18 @@ + + + + + + + + + + {{#each channels}} + + + + + + {{/each}} + +
ChannelUsersTopic
{{{parse channel}}}{{num_users}}{{{parse topic}}}
diff --git a/src/client.js b/src/client.js index 5fd87b64..222b7628 100644 --- a/src/client.js +++ b/src/client.js @@ -29,6 +29,7 @@ var events = [ "quit", "topic", "welcome", + "list", "whois" ]; var inputs = [ @@ -46,6 +47,7 @@ var inputs = [ "quit", "raw", "topic", + "list", ].reduce(function(plugins, name) { var path = "./plugins/inputs/" + name; var plugin = require(path); diff --git a/src/models/chan.js b/src/models/chan.js index e64ac8ac..750e798a 100644 --- a/src/models/chan.js +++ b/src/models/chan.js @@ -6,7 +6,8 @@ module.exports = Chan; Chan.Type = { CHANNEL: "channel", LOBBY: "lobby", - QUERY: "query" + QUERY: "query", + SPECIAL: "special", }; var id = 0; diff --git a/src/models/network.js b/src/models/network.js index 65303756..cbeea7d4 100644 --- a/src/models/network.js +++ b/src/models/network.js @@ -23,6 +23,7 @@ function Network(attr) { serverOptions: { PREFIX: [], }, + chanCache: [], }, attr)); this.name = attr.name || prettify(attr.host); this.channels.unshift( diff --git a/src/plugins/inputs/list.js b/src/plugins/inputs/list.js new file mode 100644 index 00000000..70a4a65f --- /dev/null +++ b/src/plugins/inputs/list.js @@ -0,0 +1,7 @@ +exports.commands = ["list"]; + +exports.input = function(network, chan, cmd, args) { + network.chanCache = []; + network.irc.list(args); + return true; +}; diff --git a/src/plugins/irc-events/list.js b/src/plugins/irc-events/list.js new file mode 100644 index 00000000..db6fdd3c --- /dev/null +++ b/src/plugins/irc-events/list.js @@ -0,0 +1,56 @@ +var Chan = require("../../models/chan"); +var Msg = require("../../models/msg"); + +module.exports = function(irc, network) { + var client = this; + var MAX_CHANS = 1000; + + irc.on("channel list start", function() { + network.chanCache = []; + + updateListStatus(new Msg({ + text: "Loading channel list, this can take a moment...", + type: "channel_list_loading" + })); + }); + + irc.on("channel list", function(channels) { + Array.prototype.push.apply(network.chanCache, channels); + }); + + irc.on("channel list end", function() { + updateListStatus(new Msg({ + type: "channel_list", + channels: network.chanCache.slice(0, MAX_CHANS) + })); + + if (network.chanCache.length > MAX_CHANS) { + updateListStatus(new Msg({ + type: "channel_list_truncated", + text: "Channel list is too large: truncated to " + MAX_CHANS + " channels." + })); + } + + network.chanCache = []; + }); + + function updateListStatus(msg) { + var chan = network.getChannel("Channel List"); + if (typeof chan === "undefined") { + chan = new Chan({ + type: Chan.Type.SPECIAL, + name: "Channel List" + }); + network.channels.push(chan); + client.emit("join", { + network: network.id, + chan: chan + }); + } + + client.emit("msg", { + chan: chan.id, + msg: msg + }); + } +};