Преглед изворни кода

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).
JC Brand пре 10 година
родитељ
комит
450ce04fd9
5 измењених фајлова са 189 додато и 110 уклоњено
  1. 146 70
      converse.js
  2. 1 0
      main.js
  3. 35 33
      src/templates.js
  4. 0 7
      src/templates/chatarea.html
  5. 7 0
      src/templates/chatroom_sidebar.html

+ 146 - 70
converse.js

@@ -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({
         this.ChatRoomView = converse.ChatBoxView.extend({
             length: 300,
             length: 300,
             tagName: 'div',
             tagName: 'div',
@@ -2044,7 +2161,6 @@
             is_chatroom: true,
             is_chatroom: true,
 
 
             initialize: function () {
             initialize: function () {
-                this.connect(null);
                 this.model.messages.on('add', this.onMessageAdded, this);
                 this.model.messages.on('add', this.onMessageAdded, this);
                 this.model.on('change:minimized', function (item) {
                 this.model.on('change:minimized', function (item) {
                     if (item.get('minimized')) {
                     if (item.get('minimized')) {
@@ -2062,8 +2178,16 @@
                         undefined);
                         undefined);
                 },
                 },
                 this);
                 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.$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')) {
                 if (this.model.get('minimized')) {
                     this.hide();
                     this.hide();
                 } else {
                 } else {
@@ -2074,10 +2198,26 @@
             render: function () {
             render: function () {
                 this.$el.attr('id', this.model.get('box_id'))
                 this.$el.attr('id', this.model.get('box_id'))
                         .html(converse.templates.chatroom(this.model.toJSON()));
                         .html(converse.templates.chatroom(this.model.toJSON()));
-                converse.emit('chatRoomOpened', this);
+
+                this.renderChatArea();
                 setTimeout(function () {
                 setTimeout(function () {
                     converse.refreshWebkit();
                     converse.refreshWebkit();
                 }, 50);
                 }, 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;
                 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) {
             connect: function (password) {
                 if (_.has(converse.connection.muc.rooms, this.model.get('jid'))) {
                 if (_.has(converse.connection.muc.rooms, this.model.get('jid'))) {
                     // If the room exists, it already has event listeners, so we
                     // If the room exists, it already has event listeners, so we
@@ -2485,7 +2579,6 @@
                     this.model.set('connected', false);
                     this.model.set('connected', false);
                     return;
                     return;
                 }
                 }
-                this.renderChatArea();
                 $chat_content = this.$el.find('.chat-content');
                 $chat_content = this.$el.find('.chat-content');
                 for (i=0; i<msgs.length; i++) {
                 for (i=0; i<msgs.length; i++) {
                     $chat_content.append(converse.templates.info({message: msgs[i]}));
                     $chat_content.append(converse.templates.info({message: msgs[i]}));
@@ -2599,24 +2692,7 @@
             },
             },
 
 
             onChatRoomRoster: function (roster, room) {
             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({
         this.ChatBoxViews = Backbone.Overview.extend({
 
 
             initialize: function () {
             initialize: function () {
-                this.model.on("add", this.onChatAdded, this);
+                this.model.on("add", this.onChatBoxAdded, this);
                 this.model.on("change:minimized", function (item) {
                 this.model.on("change:minimized", function (item) {
                     if (item.get('minimized') === false) {
                     if (item.get('minimized') === false) {
                          this.trimChats(this.get(item.get('id')));
                          this.trimChats(this.get(item.get('id')));
@@ -2799,7 +2875,7 @@
                 }
                 }
             },
             },
 
 
-            onChatAdded: function (item) {
+            onChatBoxAdded: function (item) {
                 var view = this.get(item.get('id'));
                 var view = this.get(item.get('id'));
                 if (!view) {
                 if (!view) {
                     if (item.get('chatroom')) {
                     if (item.get('chatroom')) {

+ 1 - 0
main.js

@@ -67,6 +67,7 @@ config = {
         "chatarea":                 "src/templates/chatarea",
         "chatarea":                 "src/templates/chatarea",
         "chatbox":                  "src/templates/chatbox",
         "chatbox":                  "src/templates/chatbox",
         "chatroom":                 "src/templates/chatroom",
         "chatroom":                 "src/templates/chatroom",
+        "chatroom_sidebar":         "src/templates/chatroom_sidebar",
         "chatrooms_tab":            "src/templates/chatrooms_tab",
         "chatrooms_tab":            "src/templates/chatrooms_tab",
         "chats_panel":              "src/templates/chats_panel",
         "chats_panel":              "src/templates/chats_panel",
         "choose_status":            "src/templates/choose_status",
         "choose_status":            "src/templates/choose_status",

+ 35 - 33
src/templates.js

@@ -7,6 +7,7 @@ define("converse-templates", [
     "tpl!chatarea",
     "tpl!chatarea",
     "tpl!chatbox",
     "tpl!chatbox",
     "tpl!chatroom",
     "tpl!chatroom",
+    "tpl!chatroom_sidebar",
     "tpl!chatrooms_tab",
     "tpl!chatrooms_tab",
     "tpl!chats_panel",
     "tpl!chats_panel",
     "tpl!choose_status",
     "tpl!choose_status",
@@ -50,38 +51,39 @@ define("converse-templates", [
         chatarea:               arguments[5],
         chatarea:               arguments[5],
         chatbox:                arguments[6],
         chatbox:                arguments[6],
         chatroom:               arguments[7],
         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]
     };
     };
 });
 });

+ 0 - 7
src/templates/chatarea.html

@@ -8,10 +8,3 @@
             placeholder="{{label_message}}"/>
             placeholder="{{label_message}}"/>
     </form>
     </form>
 </div>
 </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>

+ 7 - 0
src/templates/chatroom_sidebar.html

@@ -0,0 +1,7 @@
+<!-- <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> -->