changeset 2007:19b9d3f8a6c7

plugin XEP-0085, quick_frontends, primitivus: chat states are working again
author Goffi <goffi@goffi.org>
date Sun, 17 Jul 2016 16:47:33 +0200
parents 0ddf3edf643a
children 8a749ec21c50
files frontends/src/primitivus/chat.py frontends/src/quick_frontend/constants.py frontends/src/quick_frontend/quick_app.py frontends/src/quick_frontend/quick_chat.py frontends/src/quick_frontend/quick_widgets.py src/core/sat_main.py src/plugins/plugin_xep_0085.py
diffstat 7 files changed, 59 insertions(+), 24 deletions(-) [+]
line wrap: on
line diff
--- a/frontends/src/primitivus/chat.py	Fri Jul 15 22:13:09 2016 +0200
+++ b/frontends/src/primitivus/chat.py	Sun Jul 17 16:47:33 2016 +0200
@@ -152,6 +152,10 @@
         return self.occupant_data.nick.lower() < other.occupant_data.nick.lower()
 
     @property
+    def markup(self):
+        return self._generateMarkup()
+
+    @property
     def parent(self):
         return self.mess_data.parent
 
@@ -159,6 +163,10 @@
     def nick(self):
         return self.occupant_data.nick
 
+    def redraw(self):
+        self._w.set_text(self.markup)
+        self.occupant_data.parent.host.redraw()  # FIXME: should not be necessary
+
     def selectable(self):
         return True
 
@@ -181,13 +189,19 @@
         #       should be more intuitive and themable
         o = self.occupant_data
         markup = []
-        markup.append(('info_msg', '{}{} '.format(
+        markup.append(('info_msg', u'{}{} '.format(
             o.role[0].upper(),
             o.affiliation[0].upper(),
             )))
         markup.append(o.nick)
+        if o.state is not None:
+            markup.append(u' {}'.format(C.CHAT_STATE_ICON[o.state]))
         return markup
 
+    # events
+    def updated(self, attributes):
+        self.redraw()
+
 
 class OccupantsWidget(urwid.WidgetWrap):
 
@@ -572,6 +586,11 @@
         if self.type == C.CHAT_GROUP:
             self.host.removeListener('presence', self.presenceListener)
 
+    def onChatState(self, from_jid, state, profile):
+        super(Chat, self).onChatState(from_jid, state, profile)
+        if self.type == C.CHAT_ONE2ONE:
+            self.title_dynamic = C.CHAT_STATE_ICON[state]
+            self.host.redraw()  # FIXME: should not be necessary
 
 quick_widgets.register(quick_chat.QuickChat, Chat)
 quick_widgets.register(quick_games.Tarot, game_tarot.TarotGame)
--- a/frontends/src/quick_frontend/constants.py	Fri Jul 15 22:13:09 2016 +0200
+++ b/frontends/src/quick_frontend/constants.py	Sun Jul 17 16:47:33 2016 +0200
@@ -55,12 +55,13 @@
     # Chats
     CHAT_ONE2ONE = 'one2one'
     CHAT_GROUP = 'group'
-    USER_CHAT_STATES = {
+    CHAT_STATE_ICON = {
+        "": u" ",
         "active": u'✔',
         "inactive": u'☄',
         "gone": u'✈',
         "composing": u'✎',
-        "paused": u"⦷"
+        "paused": u"…"
     }
 
     # Blogs
--- a/frontends/src/quick_frontend/quick_app.py	Fri Jul 15 22:13:09 2016 +0200
+++ b/frontends/src/quick_frontend/quick_app.py	Sun Jul 17 16:47:33 2016 +0200
@@ -569,20 +569,9 @@
         @param state (unicode): new state
         @param profile (unicode): current profile
         """
-        # log.debug(_(u"Received new chat state {} from {} [{}]").format(state, from_jid_s, profile))
-        # from_jid = jid.JID(from_jid_s) if from_jid_s != C.ENTITY_ALL else C.ENTITY_ALL
-        # contact_list = self.contact_lists[profile]
-        # for widget in self.widgets.getWidgets(quick_chat.QuickChat):
-        #     if profile != widget.profile:
-        #         continue
-        #     to_display = C.USER_CHAT_STATES[state] if (state and widget.type == C.CHAT_GROUP) else state
-        #     if widget.type == C.CHAT_GROUP and from_jid_s == C.ENTITY_ALL:
-        #         for occupant in [jid.newResource(widget.target, nick) for nick in widget.occupants]:
-        #             contact_list.setCache(occupant, 'chat_state', to_display)
-        #             widget.update(occupant)
-        #     elif from_jid.bare == widget.target.bare:  # roster contact or MUC occupant
-        #         contact_list.setCache(from_jid, 'chat_state', to_display)
-        #         widget.update(from_jid)
+        from_jid = jid.JID(from_jid_s)
+        for widget in self.widgets.getWidgets(quick_chat.QuickChat, profiles=(profile,)):
+            widget.onChatState(from_jid, state, profile)
 
     def notify(self, type_, entity=None, message=None, subject=None, callback=None, cb_args=None, widget=None, profile=C.PROF_KEY_NONE):
         """Trigger an event notification
--- a/frontends/src/quick_frontend/quick_chat.py	Fri Jul 15 22:13:09 2016 +0200
+++ b/frontends/src/quick_frontend/quick_chat.py	Sun Jul 17 16:47:33 2016 +0200
@@ -126,11 +126,22 @@
         self.affiliation = data['affiliation']
         self.role = data['role']
         self.widgets = set()  # widgets linked to this occupant
+        self._state = None
 
     @property
     def host(self):
         return self.parent.host
 
+    @property
+    def state(self):
+        return self._state
+
+    @state.setter
+    def state(self, new_state):
+        self._state = new_state
+        for w in self.widgets:
+            w.updated(["state"])
+
 
 class QuickChat(quick_widgets.QuickWidget):
 
@@ -407,5 +418,17 @@
         """
         raise NotImplementedError
 
+    ## events ##
+
+    def onChatState(self, from_jid, state, profile):
+        """A chat state has been received"""
+        if self.type == C.CHAT_GROUP:
+            nick = from_jid.resource
+            try:
+                self.occupants[nick].state = state
+            except KeyError:
+                log.warning(u"{nick} not found in {room}, ignoring new chat state".format(
+                    nick=nick, room=self.target.bare))
+
 
 quick_widgets.register(QuickChat)
--- a/frontends/src/quick_frontend/quick_widgets.py	Fri Jul 15 22:13:09 2016 +0200
+++ b/frontends/src/quick_frontend/quick_widgets.py	Sun Jul 17 16:47:33 2016 +0200
@@ -79,19 +79,22 @@
             raise exceptions.InternalError("There is not class registered for {}".format(class_))
         return cls
 
-    def getWidgets(self, class_):
-        """Get all subclassed widgets
+    def getWidgets(self, class_, profiles=None):
+        """Get all subclassed widgets instances
 
         @param class_: subclass of QuickWidget, same parameter as used in [getOrCreateWidget]
+        @param profiles(iterable, None): if not None, filter on instances linked to these profiles
         @return: iterator on widgets
         """
         class_ = self.getRealClass(class_)
         try:
             widgets_map = self._widgets[class_.__name__]
         except KeyError:
-            return iter([])
+            return
         else:
-            return widgets_map.itervalues()
+            for w in widgets_map.itervalues():
+                if profiles is None or w.profiles.intersection(profiles):
+                    yield w
 
     def getWidget(self, class_, target=None, profiles=None):
         """Get a widget without creating it if it doesn't exist.
--- a/src/core/sat_main.py	Fri Jul 15 22:13:09 2016 +0200
+++ b/src/core/sat_main.py	Sun Jul 17 16:47:33 2016 +0200
@@ -520,7 +520,7 @@
     ## XMPP methods ##
 
     def getWaitingConf(self, profile_key=None):
-        assert(profile_key)
+        assert profile_key
         client = self.getClient(profile_key)
         ret = []
         for conf_id in client._waiting_conf:
--- a/src/plugins/plugin_xep_0085.py	Fri Jul 15 22:13:09 2016 +0200
+++ b/src/plugins/plugin_xep_0085.py	Sun Jul 17 16:47:33 2016 +0200
@@ -96,7 +96,7 @@
     def __init__(self, host):
         log.info(_("Chat State Notifications plugin initialization"))
         self.host = host
-        self.map = {}
+        self.map = {}  # FIXME: would be better to use client instead of mapping profile to data
 
         # parameter value is retrieved before each use
         host.memory.updateParams(self.params)
@@ -141,7 +141,7 @@
             self.host.memory.updateEntityData(entity_jid, ENTITY_KEY, value, profile_key=profile)
         if not value or value == DELETE_VALUE:
             # reinit chat state UI for this or these contact(s)
-            self.host.bridge.chatStateReceived(unicode(entity_jid), "", profile)
+            self.host.bridge.chatStateReceived(entity_jid.full(), "", profile)
 
     def paramUpdateTrigger(self, name, value, category, type_, profile):
         """Reset all the existing chat state entity data associated with this profile after a parameter modification.