# HG changeset patch # User Goffi # Date 1468766853 -7200 # Node ID 19b9d3f8a6c7682137c0b78876e9bd7a207e39cd # Parent 0ddf3edf643a2c10e79ebc0ae7878d698264a05d plugin XEP-0085, quick_frontends, primitivus: chat states are working again diff -r 0ddf3edf643a -r 19b9d3f8a6c7 frontends/src/primitivus/chat.py --- 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) diff -r 0ddf3edf643a -r 19b9d3f8a6c7 frontends/src/quick_frontend/constants.py --- 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 diff -r 0ddf3edf643a -r 19b9d3f8a6c7 frontends/src/quick_frontend/quick_app.py --- 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 diff -r 0ddf3edf643a -r 19b9d3f8a6c7 frontends/src/quick_frontend/quick_chat.py --- 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) diff -r 0ddf3edf643a -r 19b9d3f8a6c7 frontends/src/quick_frontend/quick_widgets.py --- 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. diff -r 0ddf3edf643a -r 19b9d3f8a6c7 src/core/sat_main.py --- 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: diff -r 0ddf3edf643a -r 19b9d3f8a6c7 src/plugins/plugin_xep_0085.py --- 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.