diff --git a/assets/css/subway.css b/assets/css/subway.css index 432f838..21927e5 100644 --- a/assets/css/subway.css +++ b/assets/css/subway.css @@ -179,6 +179,15 @@ background: #F9F9F9; } +.joinpart{ + padding: 1% 0% 1% 1%; +} + +.joinpart img{ + padding-right: 1%; + vertical-align: text-bottom; +} + .chat_name{ display: table-cell; width: 14%; @@ -227,3 +236,32 @@ overflow-y: auto; height: 95%; } + +.userlist_user{ + border-bottom: 1px solid #CCCCCC; + display: table; + width: 100%; +} + +.userlist_user_activity{ + width: 13%; + display: table-cell; + padding: 2%; + background: #EFEFEF; + text-align: center; + border-right: 1px solid #DDDDDD; +} + +.userlist_user_info{ + display: table-cell; + width: 87%; + padding: 3% 0% 3% 5%; +} + +.userlist_user_name{ + font-weight: bold; +} + +.userlist_user_active{ + color: #666666; +} diff --git a/assets/images/active.png b/assets/images/active.png new file mode 100644 index 0000000..af0ee80 Binary files /dev/null and b/assets/images/active.png differ diff --git a/assets/images/idle.png b/assets/images/idle.png new file mode 100644 index 0000000..91630b6 Binary files /dev/null and b/assets/images/idle.png differ diff --git a/assets/images/join.png b/assets/images/join.png new file mode 100644 index 0000000..7f38880 Binary files /dev/null and b/assets/images/join.png differ diff --git a/assets/images/part.png b/assets/images/part.png new file mode 100644 index 0000000..bb5e0b1 Binary files /dev/null and b/assets/images/part.png differ diff --git a/assets/js/client.js b/assets/js/client.js index 725c967..dec5da7 100644 --- a/assets/js/client.js +++ b/assets/js/client.js @@ -56,7 +56,7 @@ $(function() { irc.chatWindows.add({name: data.channel}); } else { var channel = irc.chatWindows.getByName(data.channel); - channel.participants.add({nick: data.nick}); + channel.userList.add({nick: data.nick, role: data.role, idle:0, user_status: 'active', activity: 'Joined'}); var joinMessage = new Message({type: 'join', nick: data.nick}); joinMessage.setText(); channel.stream.add(joinMessage); @@ -69,13 +69,23 @@ $(function() { if (data.nick === irc.me.nick) { channel.part(); } else { - channel.participants.getByNick(data.nick).destroy(); + var user = channel.userList.getByNick(data.nick); + user.view.remove(); + user.destroy(); var partMessage = new Message({type: 'part', nick: data.nick}); partMessage.setText(); channel.stream.add(partMessage); } }); + irc.socket.on('names', function(data) { + var channel = irc.chatWindows.getByName(data.channel); + channel.userList = new UserList(channel); + $.each(data.nicks, function(nick, role){ + channel.userList.add(new User({nick: nick, role: role, idle:0, user_status: 'active', activity: 'Joined'})) + }); + }); + irc.socket.on('topic', function(data) { var channel = irc.chatWindows.getByName(data.channel); channel.set({topic: data.topic}); diff --git a/assets/js/collections.js b/assets/js/collections.js index 6105bf3..7203fd5 100644 --- a/assets/js/collections.js +++ b/assets/js/collections.js @@ -55,10 +55,24 @@ var WindowList = Backbone.Collection.extend({ var UserList = Backbone.Collection.extend({ model: User, + + initialize: function(channel) { + this.channel = channel; + this.view = new UserListView({collection:this}); + }, + getByNick: function(nick) { return this.detect(function(user) { return user.get('nick') == nick; }); + }, + + getUsers: function() { + var users = []; + for (var i=0; i' + this.get('nick') + ' joined the channel'; break; case 'part': - text = this.get('nick') + ' left the channel'; + text = '' + this.get('nick') + ' left the channel'; break; case 'nick': - text = this.get('oldNick') + ' is now known as ' + this.get('newNick'); + text = '' + this.get('oldNick') + ' is now known as ' + this.get('newNick'); break; } this.set({text: text}); @@ -136,8 +136,10 @@ var ChatWindow = Backbone.Model.extend({ }); var User = Backbone.Model.extend({ + initialize: function(){ + }, + defaults: { opStatus: '' } }); - diff --git a/assets/js/views/chat.js b/assets/js/views/chat.js index 167e9fa..e7b5ec5 100644 --- a/assets/js/views/chat.js +++ b/assets/js/views/chat.js @@ -57,21 +57,21 @@ var ChatView = Backbone.View.extend({ $(this).val(''); $('#chat_button').addClass('disabled'); } else if (event.keyCode == 9) { - console.log(event); + var channel = irc.chatWindows.getActive(); // Tab completion of user names - var sentence = $(this).val().split(' '); + var sentence = $('#chat_input').val().split(' '); var partialMatch = sentence.pop(); // TODO: Make this work (copy-paste from old code; it doesn't work) // All the below code is busted until this is resolved. - // channel = app.model.chatApp.channels.findChannel(app.activeChannel); - var users = channel.attributes.users; - for (user in users) { + var users = channel.userList.getUsers(); + for (var i=0; i 0 && user.search(partialMatch) === 0) { sentence.push(user); if (sentence.length === 1) { - $(this).val(sentence.join(' ') + ":"); + $('#chat_input').val(sentence.join(' ') + ":"); } else { - $(this).val(sentence.join(' ')); + $('#chat_input').val(sentence.join(' ')); } } } @@ -88,12 +88,24 @@ var ChatView = Backbone.View.extend({ addMessage: function(msg) { var $chatWindow = this.$('#chat-contents'); var view = new MessageView({model: msg}); + var sender = msg.get('sender'); + var type = msg.get('type'); + if (sender !== '' && type === 'message'){ + var user = this.model.userList.getByNick(sender); + user.set({idle: 0}); + user.view.addToIdle(); + } + $chatWindow.append(view.el); - if (msg.get('sender') === irc.me.nick) { + if (sender === irc.me.nick) { $(view.el).addClass('message-me'); } + if(type === 'join' || type === 'part'){ + $(view.el).addClass('joinpart'); + } + // Scroll down to show new message var chatWindowHeight = ($chatWindow[0].scrollHeight - 555); // If the window is large enough to be scrollable diff --git a/assets/js/views/user_list.js b/assets/js/views/user_list.js new file mode 100644 index 0000000..d10230a --- /dev/null +++ b/assets/js/views/user_list.js @@ -0,0 +1,47 @@ +var UserView = Backbone.View.extend({ + initialize: function(user) { + this.user = user; + this.setStatus(); + }, + + className: 'userlist_user', + + render: function() { + $(this.el).html(ich.userlist_user(this.user.model.attributes)); + return this; + }, + + addToIdle: function(){ + var idle_time = this.user.model.get('idle') + 1; + if (idle_time > 60) { + this.user.model.set({activity: 'idle', user_status: 'idle'}); + } else { + this.user.model.set({activity: 'Last active ' + idle_time + ' minutes ago', idle: idle_time}); + } + this.render(); + }, + + setStatus: function(){ + //One minute delays + var self = this; + var interval = 60000; + window.setInterval(function() { self.addToIdle() }, interval); + } +}); + +var UserListView = Backbone.View.extend({ + initialize: function() { + this.el = this.collection.channel.view.$('#user-list'); + this.collection.bind('add', this.add, this); + }, + + render: function() { + return this; + }, + + add: function(User) { + var userView = new UserView({model: User}); + User.view = userView; + $(this.el).append(userView.render().el); + } +}); diff --git a/views/templates.jade b/views/templates.jade index c32e24e..678479f 100644 --- a/views/templates.jade +++ b/views/templates.jade @@ -71,7 +71,11 @@ script(id="unread_mentions", type="text/html") span(class="unread_mentions", title="Mentions in channel") {{unread_mentions}} script(id="userlist_user", type="text/html") - li {{username}} + div(class="userlist_user_activity") + img(src="/assets/images/{{user_status}}.png") + div(class="userlist_user_info") + div(class="userlist_user_name") {{role}}{{nick}} + div(class="userlist_user_active") {{activity}} script(id="link", type="text/html") a(target="_blank", href="{{link}}") {{link}}