1
0
mirror of https://github.com/moparisthebest/kaiwa synced 2024-11-25 18:52:20 -05:00

Make MUCs look nicer

This commit is contained in:
Lance Stout 2013-12-13 16:16:40 -08:00
parent 4f63da4d20
commit 2ca32ca3bd
13 changed files with 181 additions and 41 deletions

View File

@ -23,9 +23,9 @@ module.exports = HumanModel.define({
}, },
derived: { derived: {
mine: { mine: {
deps: ['from'], deps: ['from', '_mucMine'],
fn: function () { fn: function () {
return me.isMe(this.from); return this._mucMine || me.isMe(this.from);
} }
}, },
sender: { sender: {
@ -81,16 +81,12 @@ module.exports = HumanModel.define({
nick: { nick: {
deps: ['mine', 'type'], deps: ['mine', 'type'],
fn: function () { fn: function () {
if (this.mine) {
if (this.type === 'groupchat') {
return me.mucs.get(this.to.bare).nick;
} else {
return 'me';
}
}
if (this.type === 'groupchat') { if (this.type === 'groupchat') {
return this.from.resource; return this.from.resource;
} }
if (this.mine) {
return 'me';
}
return me.getContact(this.from.bare).displayName; return me.getContact(this.from.bare).displayName;
} }
}, },
@ -104,12 +100,20 @@ module.exports = HumanModel.define({
deps: ['edited', 'pending', 'body'], deps: ['edited', 'pending', 'body'],
cache: false, cache: false,
fn: function () { fn: function () {
return templates.includes.bareMessage({message: this}); if (this.type === 'groupchat') {
return templates.includes.mucBareMessage({message: this});
} else {
return templates.includes.bareMessage({message: this});
}
} }
}, },
templateHtml: { templateHtml: {
fn: function () { fn: function () {
return templates.includes.wrappedMessage({message: this}); if (this.type === 'groupchat') {
return templates.includes.mucWrappedMessage({message: this});
} else {
return templates.includes.wrappedMessage({message: this});
}
} }
}, },
classList: { classList: {
@ -128,6 +132,7 @@ module.exports = HumanModel.define({
}, },
session: { session: {
_created: 'date', _created: 'date',
_mucMine: 'bool',
receiptReceived: ['bool', true, false], receiptReceived: ['bool', true, false],
edited: ['bool', true, false], edited: ['bool', true, false],
delay: 'object' delay: 'object'
@ -160,6 +165,10 @@ module.exports = HumanModel.define({
app.storage.archive.add(data); app.storage.archive.add(data);
}, },
shouldGroupWith: function (previous) { shouldGroupWith: function (previous) {
return previous && previous.from.bare === this.from.bare; if (this.type === 'groupchat') {
return previous && previous.from.full === this.from.full;
} else {
return previous && previous.from.bare === this.from.bare;
}
} }
}); });

View File

@ -61,17 +61,24 @@ module.exports = HumanModel.define({
addMessage: function (message, notify) { addMessage: function (message, notify) {
message.owner = me.jid.bare; message.owner = me.jid.bare;
if (notify && (!this.activeContact || (this.activeContact && !app.hasFocus))) { if (notify && (!this.activeContact || (this.activeContact && !app.state.focused)) && message.from.resource !== this.nick) {
this.unreadCount++; this.unreadCount++;
app.notifications.create(this.displayName, { app.notifications.create(this.displayName, {
body: message.body, body: message.body,
icon: this.avatar, icon: this.avatar,
tag: this.id, tag: this.id,
onclick: _.bind(app.navigate, app, '/chat/' + this.jid) onclick: _.bind(app.navigate, app, '/groupchat/' + this.jid)
}); });
} }
message.acked = true;
this.messages.add(message); this.messages.add(message);
if (message.from.resource === this.nick) {
message = this.messages.get(message.id); // Grab the existing message object that was updated
message._mucMine = true;
this.lastSentMessage = message;
}
var newInteraction = new Date(message.created); var newInteraction = new Date(message.created);
if (!this.lastInteraction || this.lastInteraction < newInteraction) { if (!this.lastInteraction || this.lastInteraction < newInteraction) {

View File

@ -1,16 +1,27 @@
/*global $, app, me, client*/ /*global $, app, me, client*/
"use strict"; "use strict";
var _ = require('underscore');
var BasePage = require('./base'); var BasePage = require('./base');
var templates = require('../templates'); var templates = require('../templates');
var Message = require('../views/mucMessage'); var Message = require('../views/mucMessage');
var MessageModel = require('../models/message'); var MessageModel = require('../models/message');
var chatHelpers = require('../helpers/chatHelpers');
module.exports = BasePage.extend({ module.exports = BasePage.extend(chatHelpers).extend({
template: templates.pages.groupchat, template: templates.pages.groupchat,
initialize: function (spec) { initialize: function (spec) {
this.editMode = false; this.editMode = false;
this.listenTo(this, 'pageloaded', this.handlePageLoaded);
this.listenTo(this, 'pageunloaded', this.handlePageUnloaded);
this.listenTo(this.model.messages, 'change:body', this.refreshModel);
this.listenTo(this.model.messages, 'change:edited', this.refreshModel);
this.listenTo(this.model.messages, 'change:pending', this.refreshModel);
this.listenTo(this.model.messages, 'change:mine', this.refreshModel);
this.render(); this.render();
}, },
events: { events: {
@ -42,14 +53,51 @@ module.exports = BasePage.extend({
}); });
}, },
render: function () { render: function () {
if (this.rendered) return this;
this.rendered = true;
this.renderAndBind(); this.renderAndBind();
this.typingTimer = null; this.typingTimer = null;
this.$chatInput = this.$('.chatBox textarea'); this.$chatInput = this.$('.chatBox textarea');
this.$chatBox = this.$('.chatBox');
this.$messageList = this.$('.messages'); this.$messageList = this.$('.messages');
this.renderCollection(this.model.messages, Message, this.$('.messages')); this.$scrollContainer = this.$messageList;
this.listenTo(this.model.messages, 'add', this.handleChatAdded);
this.renderCollection();
$(window).on('resize', _.bind(this.handleWindowResize, this));
this.initializeScroll();
this.registerBindings(); this.registerBindings();
return this; return this;
}, },
renderCollection: function () {
var self = this;
var previous;
var bottom = this.isBottom() || this.$messageList.is(':empty');
this.model.messages.each(function (model, i) {
self.appendModel(model);
});
this.scrollIfPinned();
},
handleChatAdded: function (model) {
this.appendModel(model, true);
},
handlePageLoaded: function () {
this.scrollPageLoad();
this.handleWindowResize();
},
handlePageUnloaded: function () {
this.scrollPageUnload();
},
handleWindowResize: function () {
this.scrollIfPinned();
this.resizeInput();
},
handleKeyDown: function (e) { handleKeyDown: function (e) {
clearTimeout(this.typingTimer); clearTimeout(this.typingTimer);
if (e.which === 13 && !e.shiftKey) { if (e.which === 13 && !e.shiftKey) {
@ -140,7 +188,7 @@ module.exports = BasePage.extend({
var id = client.sendMessage(message); var id = client.sendMessage(message);
message.id = id; message.id = id;
message.from = me.jid; message.from = client.JID(this.model.jid.bare + '/' + this.model.nick);
if (this.editMode) { if (this.editMode) {
this.model.lastSentMessage.correct(message); this.model.lastSentMessage.correct(message);
@ -161,5 +209,27 @@ module.exports = BasePage.extend({
}, },
handleLeave: function () { handleLeave: function () {
this.model.leave(); this.model.leave();
},
appendModel: function (model, preload) {
var self = this;
var isGrouped = model.shouldGroupWith(this.lastModel);
var newEl, first, last;
if (isGrouped) {
newEl = $(model.partialTemplateHtml);
last = this.$messageList.find('li').last();
last.find('.messageWrapper').append(newEl);
last.addClass('chatGroup');
} else {
newEl = $(model.templateHtml);
this.$messageList.append(newEl);
}
this.lastModel = model;
this.scrollIfPinned();
},
refreshModel: function (model) {
var existing = this.$('#chat' + model.cid);
existing.replaceWith(model.partialTemplateHtml);
} }
}); });

View File

@ -101,6 +101,21 @@ exports.includes.messageGroup = function anonymous(locals) {
return buf.join(""); return buf.join("");
}; };
// mucBareMessage.jade compiled template
exports.includes.mucBareMessage = function anonymous(locals) {
var buf = [];
with (locals || {}) {
buf.push("<div" + jade.attrs({
id: "chat" + message.cid,
"class": "message" + " " + message.classList
}, {
"class": true,
id: true
}) + '><span class="timestamp">' + jade.escape(null == (jade.interp = message.formattedTime) ? "" : jade.interp) + '</span><p class="body">' + ((jade.interp = message.processedBody) == null ? "" : jade.interp) + "</p></div>");
}
return buf.join("");
};
// mucListItem.jade compiled template // mucListItem.jade compiled template
exports.includes.mucListItem = function anonymous(locals) { exports.includes.mucListItem = function anonymous(locals) {
var buf = []; var buf = [];
@ -115,11 +130,17 @@ exports.includes.mucListItem = function anonymous(locals) {
return buf.join(""); return buf.join("");
}; };
// mucMessage.jade compiled template // mucWrappedMessage.jade compiled template
exports.includes.mucMessage = function anonymous(locals) { exports.includes.mucWrappedMessage = function anonymous(locals) {
var buf = []; var buf = [];
with (locals || {}) { with (locals || {}) {
buf.push('<li><div class="message"><span class="timestamp">' + jade.escape(null == (jade.interp = message.created) ? "" : jade.interp) + '</span><p class="body">' + jade.escape(null == (jade.interp = message.body) ? "" : jade.interp) + '</p><span class="sender">' + jade.escape(null == (jade.interp = message.nick) ? "" : jade.interp) + "</span></div></li>"); buf.push('<li><div class="sender"><div class="name">' + jade.escape(null == (jade.interp = message.from.resource) ? "" : jade.interp) + '</div><a href="#" class="messageAvatar"><!--img(src=message.sender.avatar, alt=message.sender.displayName, data-placement="below")--></a></div><div class="messageWrapper"><div' + jade.attrs({
id: "chat" + message.cid,
"class": "message" + " " + message.classList
}, {
"class": true,
id: true
}) + '><span class="timestamp">' + jade.escape(null == (jade.interp = message.formattedTime) ? "" : jade.interp) + '</span><p class="body">' + ((jade.interp = message.processedBody) == null ? "" : jade.interp) + "</p></div></div></li>");
} }
return buf.join(""); return buf.join("");
}; };
@ -128,7 +149,7 @@ exports.includes.mucMessage = function anonymous(locals) {
exports.includes.wrappedMessage = function anonymous(locals) { exports.includes.wrappedMessage = function anonymous(locals) {
var buf = []; var buf = [];
with (locals || {}) { with (locals || {}) {
buf.push('<li><a href="#" class="messageAvatar"><img' + jade.attrs({ buf.push('<li><div class="sender"><a href="#" class="messageAvatar"><img' + jade.attrs({
src: message.sender.avatar, src: message.sender.avatar,
alt: message.sender.displayName, alt: message.sender.displayName,
"data-placement": "below" "data-placement": "below"
@ -136,7 +157,7 @@ exports.includes.wrappedMessage = function anonymous(locals) {
src: true, src: true,
alt: true, alt: true,
"data-placement": true "data-placement": true
}) + '/><span class="name">' + jade.escape(null == (jade.interp = message.sender.displayName + ": &nbsp;") ? "" : jade.interp) + '</span></a><div class="messageWrapper"><div' + jade.attrs({ }) + '/></a></div><div class="messageWrapper"><div' + jade.attrs({
id: "chat" + message.cid, id: "chat" + message.cid,
"class": "message" + " " + message.classList "class": "message" + " " + message.classList
}, { }, {

View File

@ -0,0 +1,3 @@
.message(id='chat'+message.cid, class=message.classList)
span.timestamp=message.formattedTime
p.body !{message.processedBody}

View File

@ -1,5 +0,0 @@
li
.message
span.timestamp=message.created
p.body=message.body
span.sender=message.nick

View File

@ -0,0 +1,7 @@
li
.sender
.name=message.from.resource
a.messageAvatar(href='#')
//img(src=message.sender.avatar, alt=message.sender.displayName, data-placement="below")
.messageWrapper
include mucBareMessage

View File

@ -1,6 +1,6 @@
li li
a.messageAvatar(href='#') .sender
img(src=message.sender.avatar, alt=message.sender.displayName, data-placement="below") a.messageAvatar(href='#')
span.name=message.sender.displayName + ": &nbsp;" img(src=message.sender.avatar, alt=message.sender.displayName, data-placement="below")
.messageWrapper .messageWrapper
include bareMessage include bareMessage

View File

@ -23,7 +23,7 @@
"templatizer": "0.1.2", "templatizer": "0.1.2",
"underscore": "1.5.1", "underscore": "1.5.1",
"raf-component": "1.1.1", "raf-component": "1.1.1",
"stanza.io": "2.8.1", "stanza.io": "2.9.0",
"notify.js": "0.0.3", "notify.js": "0.0.3",
"wildemitter": "0.0.5", "wildemitter": "0.0.5",
"attachmediastream": "1.0.1", "attachmediastream": "1.0.1",

View File

@ -990,6 +990,7 @@ button.secondary:hover:not(:disabled) {
position: relative; position: relative;
bottom: 75px; bottom: 75px;
width: 100%; width: 100%;
display: table;
-moz-box-sizing: border-box; -moz-box-sizing: border-box;
-webkit-box-sizing: border-box; -webkit-box-sizing: border-box;
box-sizing: border-box; box-sizing: border-box;
@ -1006,15 +1007,13 @@ button.secondary:hover:not(:disabled) {
-webkit-box-sizing: border-box; -webkit-box-sizing: border-box;
box-sizing: border-box; box-sizing: border-box;
border-bottom: 1px solid #eee; border-bottom: 1px solid #eee;
display: table-row;
} }
.messages li:last-of-type { .messages li:last-of-type {
border: none; border: none;
} }
.messages li .messageAvatar { .messages li .messageAvatar {
position: absolute; display: inline-block;
top: 50%;
left: 10px;
margin-top: -15px;
} }
.messages li .messageAvatar img { .messages li .messageAvatar img {
width: 30px; width: 30px;
@ -1031,10 +1030,25 @@ button.secondary:hover:not(:disabled) {
position: absolute; position: absolute;
width: 1px; width: 1px;
} }
.messages .sender {
display: table-cell;
font-size: 12px;
font-weight: bold;
color: #565656;
padding: 10px;
text-align: right;
background-color: #f7f7f7;
border-top: 1px solid #e1e1e1;
}
.messages .sender .name {
padding-left: 10px;
}
.messages .messageWrapper { .messages .messageWrapper {
padding-left: 50px; padding-left: 50px;
display: table-cell; display: table-cell;
vertical-align: middle; vertical-align: middle;
padding: 10px;
border-top: 1px solid #eee;
} }
.messages .message { .messages .message {
font-size: 12px; font-size: 12px;

View File

@ -108,6 +108,7 @@
position: relative position: relative
bottom: 75px bottom: 75px
width: 100% width: 100%
display: table
borderbox() borderbox()
li li
@ -120,15 +121,13 @@
display: table display: table
borderbox() borderbox()
border-bottom: 1px solid $gray-lighter border-bottom: 1px solid $gray-lighter
display: table-row
&:last-of-type &:last-of-type
border: none border: none
.messageAvatar .messageAvatar
position: absolute display: inline-block
top: 50%
left: 10px
margin-top: -15px
img img
avatar() avatar()
@ -138,10 +137,25 @@
position: absolute position: absolute
width: 1px width: 1px
.sender
display: table-cell
font-size: 12px
font-weight: bold
color: $gray
padding: 10px
text-align: right
background-color: #f7f7f7
border-top: 1px solid #e1e1e1
.name
padding-left: 10px
.messageWrapper .messageWrapper
padding-left: 50px padding-left: 50px
display: table-cell display: table-cell
vertical-align: middle vertical-align: middle
padding: 10px
border-top: 1px solid #eee
.message .message
font-size: $font-size-small font-size: $font-size-small

View File

@ -1,5 +1,5 @@
CACHE MANIFEST CACHE MANIFEST
# 0.0.1 1384923017253 # 0.0.1 1386980007554
CACHE: CACHE:
/app.js /app.js