Commit 450ce04f authored by JC Brand's avatar JC Brand

Refactor chat rooms.

When re-attaching to an existing session, chat rooms are fetched from
sessionStorage and we join them again.

However, unless we send a presence of type unavailable before reloading the
page, from the XMPP server's perspective we never left the chat room.

It therefore doesn't send us again the room occupants or room messages.

To send a presence of type unavailable is a hack and not desireable. Rather, we
want to stay in the room and just re-attach to it upon page reload.
In order to do this, we need some new functionality.

* Refactor the chat room sidebar into a new Backbone Model/View combo. (done).
* Store/fetch room occupants in/from sessionStorage (done).
* Store/fetch room messages in/from sessionStorage (not yet done).
* Instead of re-joining a chat room which we never left, just register the event handlers again. (not yet done).
parent e59071ae
......@@ -2026,6 +2026,123 @@
}
});
this.ChatRoomOccupant = Backbone.Model;
this.ChatRoomOccupantView = Backbone.View.extend({
tagName: 'li',
initialize: function () {
this.model.on('change', this.render, this);
},
render: function () {
var $new = converse.templates.occupant(
_.extend(
this.model.toJSON(), {
'desc_moderator': __('This user is a moderator'),
'desc_participant': __('This user can send messages in this room'),
'desc_visitor': __('This user can NOT send messages in this room')
})
);
this.$el.replaceWith($new);
this.setElement($new, true);
return this;
}
});
this.ChatRoomOccupants = Backbone.Collection.extend({
model: converse.ChatRoomOccupant,
initialize: function (options) {
this.browserStorage = new Backbone.BrowserStorage[converse.storage](
b64_sha1('converse.occupants'+converse.bare_jid+options.nick));
},
});
this.ChatRoomOccupantsView = Backbone.Overview.extend({
tagName: 'div',
className: 'participants',
initialize: function () {
this.model.on("add", this.onOccupantAdded, this);
},
render: function () {
this.$el.html(
converse.templates.chatroom_sidebar({
'label_invitation': __('Invite...'),
'label_occupants': __('Occupants')
})
);
return this.initInviteWidget();
},
onOccupantAdded: function (item) {
var view = this.get(item.get('id'));
if (!view) {
view = this.add(item.get('id'), new converse.ChatRoomOccupantView({model: item}));
} else {
delete view.model; // Remove ref to old model to help garbage collection
view.model = item;
view.initialize();
}
this.$('.participant-list').append(view.render().$el);
},
onChatRoomRoster: function (roster, room) {
var roster_size = _.size(roster),
$participant_list = this.$('.participant-list'),
participants = [],
keys = _.keys(roster),
occupant, attrs, i, nick;
// XXX: this.$('.participant-list').empty();
for (i=0; i<roster_size; i++) {
nick = Strophe.unescapeNode(keys[i]);
attrs = {
'id': nick,
'role': roster[keys[i]].role,
'nick': nick
};
occupant = this.model.get(nick);
if (occupant) {
occupant.save(attrs);
} else {
this.model.create(attrs);
}
}
return true;
},
initInviteWidget: function () {
var $el = this.$('input.invited-contact');
$el.typeahead({
minLength: 1,
highlight: true
}, {
name: 'contacts-dataset',
source: function (q, cb) {
var results = [];
_.each(converse.roster.filter(contains(['fullname', 'jid'], q)), function (n) {
results.push({value: n.get('fullname'), jid: n.get('jid')});
});
cb(results);
},
templates: {
suggestion: _.template('<p data-jid="{{jid}}">{{value}}</p>')
}
});
$el.on('typeahead:selected', $.proxy(function (ev, suggestion, dname) {
var reason = prompt(
__(___('You are about to invite %1$s to the chat room "%2$s". '), suggestion.value, this.model.get('id')) +
__("You may optionally include a message, explaining the reason for the invitation.")
);
if (reason !== null) {
converse.connection.muc.rooms[this.model.get('id')].directInvite(suggestion.jid, reason);
converse.emit('roomInviteSent', this, suggestion.jid, reason);
}
$(ev.target).typeahead('val', '');
}, this));
return this;
},
});
this.ChatRoomView = converse.ChatBoxView.extend({
length: 300,
tagName: 'div',
......@@ -2044,7 +2161,6 @@
is_chatroom: true,
initialize: function () {
this.connect(null);
this.model.messages.on('add', this.onMessageAdded, this);
this.model.on('change:minimized', function (item) {
if (item.get('minimized')) {
......@@ -2062,8 +2178,16 @@
undefined);
},
this);
this.occupantsview = new converse.ChatRoomOccupantsView({
model: new converse.ChatRoomOccupants({nick: this.model.get('nick')})
});
this.render();
this.occupantsview.model.fetch({add:true});
this.connect(null);
this.$el.insertAfter(converse.chatboxviews.get("controlbox").$el);
this.render().model.messages.fetch({add: true});
this.model.messages.fetch({add: true});
if (this.model.get('minimized')) {
this.hide();
} else {
......@@ -2074,10 +2198,26 @@
render: function () {
this.$el.attr('id', this.model.get('box_id'))
.html(converse.templates.chatroom(this.model.toJSON()));
converse.emit('chatRoomOpened', this);
this.renderChatArea();
setTimeout(function () {
converse.refreshWebkit();
}, 50);
converse.emit('chatRoomOpened', this);
return this;
},
renderChatArea: function () {
if (!this.$('.chat-area').length) {
this.$('.chat-body').empty()
.append(
converse.templates.chatarea({
'show_toolbar': converse.show_toolbar,
'label_message': __('Message'),
}))
.append(this.occupantsview.render().$el);
this.renderToolbar();
}
return this;
},
......@@ -2144,52 +2284,6 @@
}
},
initInviteWidget: function () {
var $el = this.$('input.invited-contact');
$el.typeahead({
minLength: 1,
highlight: true
}, {
name: 'contacts-dataset',
source: function (q, cb) {
var results = [];
_.each(converse.roster.filter(contains(['fullname', 'jid'], q)), function (n) {
results.push({value: n.get('fullname'), jid: n.get('jid')});
});
cb(results);
},
templates: {
suggestion: _.template('<p data-jid="{{jid}}">{{value}}</p>')
}
});
$el.on('typeahead:selected', $.proxy(function (ev, suggestion, dname) {
var reason = prompt(
__(___('You are about to invite %1$s to the chat room "%2$s". '), suggestion.value, this.model.get('id')) +
__("You may optionally include a message, explaining the reason for the invitation.")
);
if (reason !== null) {
converse.connection.muc.rooms[this.model.get('id')].directInvite(suggestion.jid, reason);
converse.emit('roomInviteSent', this, suggestion.jid, reason);
}
$(ev.target).typeahead('val', '');
}, this));
return this;
},
renderChatArea: function () {
if (!this.$el.find('.chat-area').length) {
this.$el.find('.chat-body').empty().append(
converse.templates.chatarea({
'show_toolbar': converse.show_toolbar,
'label_message': __('Message'),
'label_invitation': __('Invite...')
})
);
this.initInviteWidget().renderToolbar();
}
return this;
},
connect: function (password) {
if (_.has(converse.connection.muc.rooms, this.model.get('jid'))) {
// If the room exists, it already has event listeners, so we
......@@ -2485,7 +2579,6 @@
this.model.set('connected', false);
return;
}
this.renderChatArea();
$chat_content = this.$el.find('.chat-content');
for (i=0; i<msgs.length; i++) {
$chat_content.append(converse.templates.info({message: msgs[i]}));
......@@ -2599,24 +2692,7 @@
},
onChatRoomRoster: function (roster, room) {
this.renderChatArea();
var controlboxview = converse.chatboxviews.get('controlbox'),
roster_size = _.size(roster),
$participant_list = this.$el.find('.participant-list'),
participants = [], keys = _.keys(roster), i;
this.$el.find('.participant-list').empty();
for (i=0; i<roster_size; i++) {
participants.push(
converse.templates.occupant({
'role': roster[keys[i]].role,
'nick': Strophe.unescapeNode(keys[i]),
'desc_moderator': __('This user is a moderator'),
'desc_participant': __('This user can send messages in this room'),
'desc_visitor': __('This user can NOT send messages in this room')
}));
}
$participant_list.append(participants.join(""));
return true;
return this.occupantsview.onChatRoomRoster(roster, room);
}
});
......@@ -2772,7 +2848,7 @@
this.ChatBoxViews = Backbone.Overview.extend({
initialize: function () {
this.model.on("add", this.onChatAdded, this);
this.model.on("add", this.onChatBoxAdded, this);
this.model.on("change:minimized", function (item) {
if (item.get('minimized') === false) {
this.trimChats(this.get(item.get('id')));
......@@ -2799,7 +2875,7 @@
}
},
onChatAdded: function (item) {
onChatBoxAdded: function (item) {
var view = this.get(item.get('id'));
if (!view) {
if (item.get('chatroom')) {
......
......@@ -67,6 +67,7 @@ config = {
"chatarea": "src/templates/chatarea",
"chatbox": "src/templates/chatbox",
"chatroom": "src/templates/chatroom",
"chatroom_sidebar": "src/templates/chatroom_sidebar",
"chatrooms_tab": "src/templates/chatrooms_tab",
"chats_panel": "src/templates/chats_panel",
"choose_status": "src/templates/choose_status",
......
......@@ -7,6 +7,7 @@ define("converse-templates", [
"tpl!chatarea",
"tpl!chatbox",
"tpl!chatroom",
"tpl!chatroom_sidebar",
"tpl!chatrooms_tab",
"tpl!chats_panel",
"tpl!choose_status",
......@@ -50,38 +51,39 @@ define("converse-templates", [
chatarea: arguments[5],
chatbox: arguments[6],
chatroom: arguments[7],
chatrooms_tab: arguments[8],
chats_panel: arguments[9],
choose_status: arguments[10],
contacts_panel: arguments[11],
contacts_tab: arguments[12],
controlbox: arguments[13],
controlbox_toggle: arguments[14],
field: arguments[15],
form_checkbox: arguments[16],
form_input: arguments[17],
form_select: arguments[18],
group_header: arguments[19],
info: arguments[20],
login_panel: arguments[21],
login_tab: arguments[22],
message: arguments[23],
new_day: arguments[24],
occupant: arguments[25],
pending_contact: arguments[26],
pending_contacts: arguments[27],
requesting_contact: arguments[28],
requesting_contacts: arguments[29],
room_description: arguments[30],
room_item: arguments[31],
room_panel: arguments[32],
roster: arguments[33],
roster_item: arguments[34],
select_option: arguments[35],
search_contact: arguments[36],
status_option: arguments[37],
toggle_chats: arguments[38],
toolbar: arguments[39],
trimmed_chat: arguments[40]
chatroom_sidebar: arguments[8],
chatrooms_tab: arguments[9],
chats_panel: arguments[10],
choose_status: arguments[11],
contacts_panel: arguments[12],
contacts_tab: arguments[13],
controlbox: arguments[14],
controlbox_toggle: arguments[15],
field: arguments[16],
form_checkbox: arguments[17],
form_input: arguments[18],
form_select: arguments[19],
group_header: arguments[20],
info: arguments[21],
login_panel: arguments[22],
login_tab: arguments[23],
message: arguments[24],
new_day: arguments[25],
occupant: arguments[26],
pending_contact: arguments[27],
pending_contacts: arguments[28],
requesting_contact: arguments[29],
requesting_contacts: arguments[30],
room_description: arguments[31],
room_item: arguments[32],
room_panel: arguments[33],
roster: arguments[34],
roster_item: arguments[35],
select_option: arguments[36],
search_contact: arguments[37],
status_option: arguments[38],
toggle_chats: arguments[39],
toolbar: arguments[40],
trimmed_chat: arguments[41]
};
});
......@@ -8,10 +8,3 @@
placeholder="{{label_message}}"/>
</form>
</div>
<div class="participants">
<form class="room-invite">
<input class="invited-contact" placeholder="{{label_invitation}}" type="text"/>
</form>
<label>Participants:</label>
<ul class="participant-list"></ul>
</div>
<!-- <div class="participants"> -->
<form class="room-invite">
<input class="invited-contact" placeholder="{{label_invitation}}" type="text"/>
</form>
<label>{{label_occupants}}:</label>
<ul class="participant-list"></ul>
<!-- </div> -->
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment