changeset 687:3845a086f0b3

browser_side: update ContactList, Chat, ContactsPanel, ContactBox, ContactLabel to update the display using listeners as it is done in quick_frontend: - Chat uses presence and avatar listener, remove unecessary methods for MUC and contact states - contact list doesn't add unecessary ContactBox (e.g for MUC bare entities) - nick, message alerts are handled directly in ContactLabel
author souliane <souliane@mailoo.org>
date Mon, 23 Mar 2015 09:35:46 +0100
parents 90a5a5af2550
children 6fc3ff3936ba
files src/browser/libervia_main.py src/browser/sat_browser/chat.py src/browser/sat_browser/contact_group.py src/browser/sat_browser/contact_list.py src/browser/sat_browser/contact_panel.py src/browser/sat_browser/contact_widget.py
diffstat 6 files changed, 100 insertions(+), 165 deletions(-) [+]
line wrap: on
line diff
--- a/src/browser/libervia_main.py	Fri Mar 27 00:15:42 2015 +0100
+++ b/src/browser/libervia_main.py	Mon Mar 23 09:35:46 2015 +0100
@@ -943,7 +943,7 @@
             _dialog.show()
 
     def _contactDeletedCb(self, entity):
-        self.contact_panel.removeContact(entity)
+        self.contact_panel.removeContactBox(entity)
 
     def _newContactCb(self, contact_jid, attributes, groups):
         self.contact_panel.updateContact(contact_jid, attributes, groups)
--- a/src/browser/sat_browser/chat.py	Fri Mar 27 00:15:42 2015 +0100
+++ b/src/browser/sat_browser/chat.py	Mon Mar 23 09:35:46 2015 +0100
@@ -105,6 +105,9 @@
             # FIXME: workaround for a pyjamas issue: calling hash on a class method always return a different value if that method is defined directly within the class (with the "def" keyword)
             self.presenceListener = self.onPresenceUpdate
             self.host.addListener('presence', self.presenceListener, [C.PROF_KEY_NONE])
+            self.avatarListener = self.onAvatarUpdate
+            host.addListener('avatar', self.avatarListener, [C.PROF_KEY_NONE])
+
         self._body.add(chat_area)
         self.content = AbsolutePanel()
         self.content.setStyleName('chatContent')
@@ -150,7 +153,7 @@
     #     _new_panel.refresh()
     #     return _new_panel
 
-    def refresh(self):
+    def refresh(self):  # FIXME: not needed anymore since unibox has been removed
         """Refresh the display of this widget. If the unibox is disabled,
         add a message box at the bottom of the panel"""
         # FIXME: must be checked
@@ -200,8 +203,21 @@
         @param profile: %(doc_profile)s
         """
         assert self.type == C.CHAT_GROUP
-        if entity.bare == self.target:
-            self.occupants_panel.setPresence(entity, show)
+        if entity.bare != self.target:
+            return
+        self.update(entity)
+
+    def onAvatarUpdate(self, entity, hash_, profile):
+        """Called on avatar update events
+
+        @param jid_: jid of the entity with updated avatar
+        @param hash_: hash of the avatar
+        @param profile: %(doc_profile)s
+        """
+        assert self.type == C.CHAT_GROUP
+        if entity.bare != self.target:
+            return
+        self.update(entity)
 
     def onQuit(self):
         libervia_widget.LiberviaWidget.onQuit(self)
@@ -209,28 +225,6 @@
             self.host.removeListener('presence', self.presenceListener)
             self.host.bridge.call('mucLeave', None, unicode(self.target.bare))
 
-    def setUserNick(self, nick):
-        """Set the nick of the user, usefull for e.g. change the color of the user"""
-        self.nick = nick
-
-    def addUser(self, nick):
-        """Add user if it is not in the group list"""
-        QuickChat.addUser(self, nick)
-        occupant_jid = jid.JID("%s/%s" % (unicode(self.target), nick))
-        self.occupants_panel.addContact(occupant_jid)
-
-    def removeUser(self, nick):
-        """Remove a user from the group list"""
-        QuickChat.removeUser(self, nick)
-        occupant_jid = jid.JID("%s/%s" % (unicode(self.target), nick))
-        self.occupants_panel.removeContact(occupant_jid)
-
-    def changeUserNick(self, old_nick, new_nick):
-        assert self.type == C.CHAT_GROUP
-        # self.occupants_panel.removeOccupant(old_nick)
-        # self.occupants_panel.addOccupant(new_nick)
-        self.printInfo(_("%(old_nick)s is now known as %(new_nick)s") % {'old_nick': old_nick, 'new_nick': new_nick})
-
     # def historyPrint(self, size=C.HISTORY_LIMIT_DEFAULT):
     #     """Print the initial history"""
     #     def getHistoryCB(history):
@@ -289,44 +283,36 @@
         """Refresh the title of this Chat dialog
 
         @param title (unicode): main title or None to use default
-        @param extra (dict{unicode: unicode}): extra info
+        @param suffix (unicode): extra title (e.g. for chat states) or None
         """
         if title is None:
             title = unicode(self.target.bare)
         if extra:
-            extra_title = ' '.join([u'({})'.format(value) for value in extra.values()])
-            title = '%s %s' % (title, extra_title)
+            title += ' %s' % extra
         libervia_widget.LiberviaWidget.setTitle(self, title)
 
-    def setConnected(self, jid_s, resource, availability, priority, statuses):
-        """Set connection status
-        @param jid_s (unicode): JID userhost as unicode
-        """
-        raise Exception("should not be there") # FIXME
-        assert(jid_s == self.target.bare)
-        if self.type != C.CHAT_GROUP:
-            return
-        box = self.occupants_panel.getOccupantBox(resource)
-        if box:
-            html_tools.setPresenceStyle(box, availability)
-
-    def setOccupantStates(self, occupant_jid, states):
-        """Set a MUC occupant's states.
+    def update(self, entity=None):
+        """Update one or all entities.
 
-        @param occupant_jid (jid.JID): occupant to update
-        @param states (dict{unicode: unicode}): new states
+        @param entity (jid.JID): entity to update
         """
-        self.occupants_panel.getContactBox(occupant_jid).updateStates(states)
-        if 'chat_state' in states.keys():  # start/stop sending "composing" state from now
-            self.chat_state_machine.started = not not states['chat_state']
+        states = self.getEntityStates(self.target)
+        if self.type == C.CHAT_ONE2ONE:  # only update the chat title
+            self.setTitle(extra=' '.join([u'({})'.format(value) for value in states.values()]))
+        else:
+            if entity is None:  # rebuild all the occupants list
+                nicks = list(self.occupants)
+                nicks.sort()
+                self.occupants_panel.setList([jid.newResource(self.target, nick) for nick in nicks])
+            else:  # add, remove or update only one occupant
+                contact_list = self.host.contact_lists[self.profile]
+                show = contact_list.getCache(entity, C.PRESENCE_SHOW)
+                if show == C.PRESENCE_UNAVAILABLE or show is None:
+                    self.occupants_panel.removeContactBox(entity)
+                else:
+                    box = self.occupants_panel.updateContactBox(entity)
+                    box.states.setHTML(u''.join(states.values()))
 
-    def setContactStates(self, contact_jid, states):
-        """Set a one2one contact's states.
-
-        @param contact_jid (jid.JID): contact
-        @param states (dict{unicode: unicode}): new states
-        """
-        self.setTitle(extra=states)
         if 'chat_state' in states.keys():  # start/stop sending "composing" state from now
             self.chat_state_machine.started = not not states['chat_state']
 
--- a/src/browser/sat_browser/contact_group.py	Fri Mar 27 00:15:42 2015 +0100
+++ b/src/browser/sat_browser/contact_group.py	Mon Mar 23 09:35:46 2015 +0100
@@ -224,12 +224,12 @@
             contacts = self.all_contacts
         for contact in contacts:
             if self.toggle.showAll:
-                self.contacts.getContactBox(contact).setVisible(True)
+                self.contacts.updateContactBox(contact).setVisible(True)
             else:
                 if contact in self.groups.items_remaining:
-                    self.contacts.getContactBox(contact).setVisible(True)
+                    self.contacts.updateContactBox(contact).setVisible(True)
                 else:
-                    self.contacts.getContactBox(contact).setVisible(False)
+                    self.contacts.updateContactBox(contact).setVisible(False)
 
     def __close(self):
         """Remove the widget from parent or close the popup."""
--- a/src/browser/sat_browser/contact_list.py	Fri Mar 27 00:15:42 2015 +0100
+++ b/src/browser/sat_browser/contact_list.py	Mon Mar 23 09:35:46 2015 +0100
@@ -176,9 +176,6 @@
 
         self._contacts_panel.setList(to_show)
 
-        for jid_ in self._alerts:
-            self._contacts_panel.getContactBox(jid_).setAlert(True)
-
     def remove(self, entity):
         # FIXME: SimplePanel and QuickContactList both have a 'remove' method
         QuickContactList.remove(self, entity)
@@ -339,10 +336,14 @@
         @param hash_: hash of the avatar
         @param profile: %(doc_profile)s
         """
-        self._contacts_panel.updateAvatar(jid_, self.host.getAvatarURL(jid_))
+        box = self._contacts_panel.getContactBox(jid_)
+        if box:
+            box.update()
 
     def onNickUpdate(self, jid_, new_nick, profile):
-        self._contacts_panel.updateNick(jid_, new_nick)
+        box = self._contacts_panel.getContactBox(jid_)
+        if box:
+            box.update()
 
     def hasVisibleMembers(self, group):
         """Tell if the given group actually has visible members
@@ -352,7 +353,7 @@
         """
         raise Exception  # FIXME: remove this method
         for jid_ in self.groups[group]:
-            if self._contacts_panel.getContactBox(jid_).isVisible():
+            if self._contacts_panel.updateContactBox(jid_).isVisible():
                 return True
         return False
 
@@ -372,7 +373,9 @@
 
     def onPresenceUpdate(self, entity, show, priority, statuses, profile):
         QuickContactList.onPresenceUpdate(self, entity, show, priority, statuses, profile)
-        self._contacts_panel.setPresence(entity, show)
+        box = self._contacts_panel.getContactBox(entity)
+        if box:  # box doesn't exist for MUC bare entity, don't create it
+            box.update()
 
     # def updateVisibility(self, jids, groups):
     #     """Set the widgets visibility for the given contacts and groups
--- a/src/browser/sat_browser/contact_panel.py	Fri Mar 27 00:15:42 2015 +0100
+++ b/src/browser/sat_browser/contact_panel.py	Mon Mar 23 09:35:46 2015 +0100
@@ -92,66 +92,43 @@
         self.clear()
         for contact_jid in jids:
             assert isinstance(contact_jid, jid.JID)
-            self.addContact(contact_jid)
+            self.updateContactBox(contact_jid)
 
     def getContactBox(self, contact_jid):
-        """Get a contact box for a contact, add it if it doesn't exist yet.
+        """Get the contact box for the given contact.
+
+        @param contact_jid (jid.JID): contact JID
+        @return: ContactBox or None
+        """
+        try:
+            return self._contacts[self._key(contact_jid)]
+        except KeyError:
+            return None
+
+    def updateContactBox(self, contact_jid):
+        """Add a contact or update it if it already exists.
 
         @param contact_jid (jid.JID): contact JID
         @return: ContactBox
         """
-        try:
-            return self._contacts[self._key(contact_jid)]
-        except KeyError:
-            box = contact_widget.ContactBox(self.host, contact_jid,
+        box = self.getContactBox(contact_jid)
+        if box:
+            box.update()
+        else:
+            entity = contact_jid.bare if self.merge_resources else contact_jid
+            box = contact_widget.ContactBox(self.host, entity,
                                             style_name=self.contacts_style,
                                             display=self.contacts_display,
                                             plugin_menu_context=self.contacts_menus)
             self._contacts[self._key(contact_jid)] = box
-            return box
-
-    def addContact(self, contact_jid):
-        """Add a contact to the list.
+            VerticalPanel.append(self, box)
+        return box
 
-        @param contact_jid (jid.JID): contact JID
-        """
-        box = self.getContactBox(contact_jid)
-        if box not in self.children:
-            VerticalPanel.append(self, box)
-
-    def removeContact(self, contact_jid):
-        """Remove a contact from the list.
+    def removeContactBox(self, contact_jid):
+        """Remove a contact.
 
         @param contact_jid (jid.JID): contact JID
         """
-        box = self._contacts.pop(self._key(contact_jid))
-        VerticalPanel.remove(self, box)
-
-    def updateAvatar(self, contact_jid, url):
-        """Update the avatar of the given contact
-
-        @param contact_jid (jid.JID): contact JID
-        @param url (unicode): image url
-        """
-        self.getContactBox(contact_jid).updateAvatar(url)
-
-    def updateNick(self, contact_jid, new_nick):
-        """Update the avatar of the given contact.
-
-        @param contact_jid (jid.JID): contact JID
-        @param new_nick (unicode): new nick of the contact
-        """
-        self.getContactBox(contact_jid).updateNick(new_nick)
-
-    def setPresence(self, entity, show):
-        """Update entity's presence.
-
-        @param entity(jid.JID): entity updated
-        @param show: availability
-        """
-        if self.merge_resources:  # we use cache to have the show information of main resource only
-            clist = self.host.contact_list
-            show = clist.getCache(entity.bare, C.PRESENCE_SHOW)
-            if show is None:
-                show = C.PRESENCE_UNAVAILABLE
-        html_tools.setPresenceStyle(self.getContactBox(entity).label, show)
+        box = self._contacts.pop(self._key(contact_jid), None)
+        if box:
+            VerticalPanel.remove(self, box)
--- a/src/browser/sat_browser/contact_widget.py	Fri Mar 27 00:15:42 2015 +0100
+++ b/src/browser/sat_browser/contact_widget.py	Mon Mar 23 09:35:46 2015 +0100
@@ -50,21 +50,21 @@
         HTML.__init__(self)
         self.host = host
         self.jid = jid_
-        if "nick" in display:
-            self.nick = self.host.contact_lists[C.PROF_KEY_NONE].getCache(self.jid, "nick")
         self.display = display
         self.alert = False
-        self.refresh()
         self.setStyleName('contactLabel')
 
-    def refresh(self):
-        alert_html = "<strong>(*)</strong>&nbsp;" if self.alert else ""
+    def update(self):
+        clist = self.host.contact_list
+        alert_html = "<strong>(*)</strong>&nbsp;" if self.jid in clist._alerts else ""
+
         contact_raw = None
         for disp in self.display:
             if disp == "jid":
                 contact_raw = unicode(self.jid)
             elif disp == "nick":
-                contact_raw = self.nick
+                clist = self.host.contact_list
+                contact_raw = html_tools.html_sanitize(clist.getCache(self.jid, "nick"))
             elif disp == "bare":
                 contact_raw = unicode(self.jid.bare)
             elif disp == "resource":
@@ -77,26 +77,11 @@
             log.error(u"Could not find a contact display for jid {jid} (display: {display})".format(jid=self.jid, display=self.display))
             contact_raw = "UNNAMED"
         contact_html = html_tools.html_sanitize(contact_raw)
+
         html = "%(alert)s%(contact)s" % {'alert': alert_html,
                                          'contact': contact_html}
         self.setHTML(html)
 
-    def updateNick(self, new_nick):
-        """Change the current nick
-
-        @param new_nick(unicode): new nick to use
-        """
-        self.nick = new_nick
-        self.refresh()
-
-    def setAlert(self, alert):
-        """Show a visual indicator
-
-        @param alert: True if alert must be shown
-        """
-        self.alert = alert
-        self.refresh()
-
 
 class ContactMenuBar(base_widget.WidgetMenuBar):
 
@@ -132,51 +117,35 @@
         self.jid = jid_
         self.label = ContactLabel(host, self.jid, display=display)
         self.avatar = ContactMenuBar(self, host) if plugin_menu_context else Image()
-        self.states = HTML("")
+        self.states = HTML()
         try:  # FIXME: dirty hack to force using an Image when the menu is actually empty
             self.avatar.items[0]
         except IndexError:
             self.avatar = Image()
-        self.updateAvatar(host.getAvatarURL(self.jid.bare))
         self.add(self.avatar)
         self.add(self.label)
         self.add(self.states)
+        self.update()
         self.addClickListener(self)
 
-    def setAlert(self, alert):
-        """Show a visual indicator.
-
-        @param alert (bool): True if alert indicator show be shown
-        """
-        self.label.setAlert(alert)
+    def update(self):
+        """Update the display.
 
-    def updateAvatar(self, url):
-        """Update the avatar.
-
-        @param url (unicode): image url
+        @param with_bare (bool): if True, ignore the resource and update with bare information.
         """
-        self.avatar.setUrl(url)
+        self.avatar.setUrl(self.host.getAvatarURL(self.jid))
 
-    def updateNick(self, new_nick):
-        """Update the nickname.
-
-        @param new_nick (unicode): new nickname to use
-        """
-        self.label.updateNick(html_tools.html_sanitize(new_nick))
-
-    def updateStates(self, states):
-        """Update the states.
-
-        @param states (dict{unicode: unicode}): new states
-        """
-        self.states.setHTML(u''.join(states.values()))
+        self.label.update()
+        clist = self.host.contact_list
+        show = clist.getCache(self.jid, C.PRESENCE_SHOW)
+        if show is None:
+            show = C.PRESENCE_UNAVAILABLE
+        html_tools.setPresenceStyle(self.label, show)
 
     def onClick(self, sender):
         try:
-            self.parent.onClick(self.jid.bare)
+            self.parent.onClick(self.jid)
         except (AttributeError, TypeError):
             pass
-        else:
-            self.setAlert(False)
 
 quick_menus.QuickMenusManager.addDataCollector(C.MENU_JID_CONTEXT, lambda caller, dummy: {'jid': unicode(caller.jid.bare)})