[Git][gajim/gajim][master] Dont send chatstates when cycling MUC nicks

Philipp Hörist gitlab at dev.gajim.org
Fri Jan 4 15:24:28 CET 2019


Philipp Hörist pushed to branch master at gajim / gajim


Commits:
4aca2eea by Philipp Hörist at 2019-01-04T14:21:55Z
Dont send chatstates when cycling MUC nicks

- Add ability to enable/disable the whole module so it doesnt try to send chatstates when we are offline

- - - - -


4 changed files:

- gajim/chat_control_base.py
- gajim/common/connection.py
- gajim/common/modules/chatstates.py
- gajim/groupchat_control.py


Changes:

=====================================
gajim/chat_control_base.py
=====================================
@@ -823,8 +823,8 @@ class ChatControlBase(MessageControl, ChatCommandProcessor, CommandTools):
         con = app.connections[self.account]
         con.get_module('Chatstate').set_keyboard_activity(self.contact)
         if not textview.has_text():
-            con.get_module('Chatstate').set_chatstate(self.contact,
-                                                      Chatstate.ACTIVE)
+            con.get_module('Chatstate').set_chatstate_delayed(self.contact,
+                                                              Chatstate.ACTIVE)
             return
         con.get_module('Chatstate').set_chatstate(self.contact,
                                                   Chatstate.COMPOSING)


=====================================
gajim/common/connection.py
=====================================
@@ -622,6 +622,7 @@ class Connection(CommonConnection, ConnectionHandlers):
         self.get_module('Ping').remove_timeout()
         if self.connection is None:
             if not reconnect:
+                self.get_module('Chatstate').enabled = False
                 self._sm_resume_data = {}
             self._disconnect()
             app.nec.push_incoming_event(OurShowEvent(
@@ -668,6 +669,7 @@ class Connection(CommonConnection, ConnectionHandlers):
             self._set_reconnect_timer()
 
         else:
+            self.get_module('Chatstate').enabled = False
             self._sm_resume_data = {}
             self._disconnect()
             app.nec.push_incoming_event(OurShowEvent(
@@ -1388,6 +1390,7 @@ class Connection(CommonConnection, ConnectionHandlers):
         # state of all contacts
         app.nec.push_incoming_event(OurShowEvent(
             None, conn=self, show='offline'))
+        self.get_module('Chatstate').enabled = False
 
     def _on_resume_successful(self):
         # Connection was successful, reset sm resume data
@@ -1422,6 +1425,7 @@ class Connection(CommonConnection, ConnectionHandlers):
         self.retrycount = 0
         self._discover_server()
         self._set_send_timeouts()
+        self.get_module('Chatstate').enabled = True
 
     def _set_send_timeouts(self):
         if app.config.get_per('accounts', self.name, 'keep_alives_enabled'):


=====================================
gajim/common/modules/chatstates.py
=====================================
@@ -16,11 +16,13 @@
 
 from typing import Any
 from typing import Dict  # pylint: disable=unused-import
+from typing import List  # pylint: disable=unused-import
 from typing import Optional
 from typing import Tuple
 
 import time
 import logging
+from functools import wraps
 
 import nbxmpp
 from gi.repository import GLib
@@ -41,6 +43,15 @@ INACTIVE_AFTER = 60
 PAUSED_AFTER = 5
 
 
+def ensure_enabled(func):
+    @wraps(func)
+    def func_wrapper(self, *args, **kwargs):
+        if not self.enabled:
+            return
+        return func(self, *args, **kwargs)
+    return func_wrapper
+
+
 def parse_chatstate(stanza: nbxmpp.Message) -> Optional[str]:
     if parse_delay(stanza) is not None:
         return None
@@ -60,13 +71,39 @@ class Chatstate:
         self.handlers = [
             ('presence', self._presence_received),
         ]
+
+        # Our current chatstate with a specific contact
         self._chatstates = {}  # type: Dict[str, State]
+
         self._last_keyboard_activity = {}  # type: Dict[str, float]
         self._last_mouse_activity = {}  # type: Dict[str, float]
+        self._timeout_id = None
+        self._delay_timeout_ids = {}  # type: Dict[str, str]
+        self._blocked = []  # type: List[str]
+        self._enabled = False
+
+    @property
+    def enabled(self):
+        return self._enabled
+
+    @enabled.setter
+    def enabled(self, value):
+        if self._enabled == value:
+            return
+        log.info('Chatstate module %s', 'enabled' if value else 'disabled')
+        self._enabled = value
 
-        self._timeout_id = GLib.timeout_add_seconds(
-            2, self._check_last_interaction)
+        if value:
+            self._timeout_id = GLib.timeout_add_seconds(
+                2, self._check_last_interaction)
+        else:
+            self.cleanup()
+            self._chatstates = {}
+            self._last_keyboard_activity = {}
+            self._last_mouse_activity = {}
+            self._blocked = []
 
+    @ensure_enabled
     def _presence_received(self,
                            _con: ConnectionT,
                            stanza: nbxmpp.Presence) -> None:
@@ -135,6 +172,7 @@ class Chatstate:
                          account=self._account,
                          contact=contact))
 
+    @ensure_enabled
     def _check_last_interaction(self) -> GLib.SOURCE_CONTINUE:
         setting = app.config.get('outgoing_chat_state_notifications')
         if setting in ('composing_only', 'disabled'):
@@ -183,6 +221,7 @@ class Chatstate:
 
         return GLib.SOURCE_CONTINUE
 
+    @ensure_enabled
     def set_active(self, jid: str) -> None:
         self._last_mouse_activity[jid] = time.time()
         setting = app.config.get('outgoing_chat_state_notifications')
@@ -207,7 +246,36 @@ class Chatstate:
         self.set_active(contact.jid)
         return 'active'
 
+    @ensure_enabled
+    def block_chatstates(self, contact: ContactT, block: bool) -> None:
+        # Block sending chatstates to a contact
+        # Used for example if we cycle through the MUC nick list, which
+        # produces a lot of text-changed signals from the textview. This
+        # Would lead to sending ACTIVE -> COMPOSING -> ACTIVE ...
+        if block:
+            self._blocked.append(contact.jid)
+        else:
+            self._blocked.remove(contact.jid)
+
+    @ensure_enabled
+    def set_chatstate_delayed(self, contact: ContactT, state: State) -> None:
+        # Used when we go from Composing -> Active after deleting all text
+        # from the Textview. We delay the Active state because maybe the
+        # User starts writing again.
+        self.remove_delay_timeout(contact)
+        self._delay_timeout_ids[contact.jid] = GLib.timeout_add_seconds(
+            2, self.set_chatstate, contact, state)
+
+    @ensure_enabled
     def set_chatstate(self, contact: ContactT, state: State) -> None:
+        # Dont send chatstates to ourself
+        if self._con.get_own_jid().bareMatch(contact.jid):
+            return
+
+        if contact.jid in self._blocked:
+            return
+
+        self.remove_delay_timeout(contact)
         current_state = self._chatstates.get(contact.jid)
         setting = app.config.get('outgoing_chat_state_notifications')
         if setting == 'disabled':
@@ -255,10 +323,6 @@ class Chatstate:
         if current_state == state:
             return
 
-        # Dont send chatstates to ourself
-        if self._con.get_own_jid().bareMatch(contact.jid):
-            return
-
         log.info('Send: %-10s - %s', state, contact.jid)
 
         event_attrs = {'account': self._account,
@@ -275,6 +339,7 @@ class Chatstate:
 
         self._chatstates[contact.jid] = state
 
+    @ensure_enabled
     def set_mouse_activity(self, contact: ContactT) -> None:
         self._last_mouse_activity[contact.jid] = time.time()
         setting = app.config.get('outgoing_chat_state_notifications')
@@ -283,11 +348,25 @@ class Chatstate:
         if self._chatstates.get(contact.jid) == State.INACTIVE:
             self.set_chatstate(contact, State.ACTIVE)
 
+    @ensure_enabled
     def set_keyboard_activity(self, contact: ContactT) -> None:
         self._last_keyboard_activity[contact.jid] = time.time()
 
+    def remove_delay_timeout(self, contact):
+        timeout = self._delay_timeout_ids.get(contact.jid)
+        if timeout is not None:
+            GLib.source_remove(timeout)
+            del self._delay_timeout_ids[contact.jid]
+
+    def remove_all_delay_timeouts(self):
+        for timeout in self._delay_timeout_ids.values():
+            GLib.source_remove(timeout)
+        self._delay_timeout_ids = {}
+
     def cleanup(self):
-        GLib.source_remove(self._timeout_id)
+        self.remove_all_delay_timeouts()
+        if self._timeout_id is not None:
+            GLib.source_remove(self._timeout_id)
 
 
 def get_instance(*args: Any, **kwargs: Any) -> Tuple[Chatstate, str]:


=====================================
gajim/groupchat_control.py
=====================================
@@ -1620,6 +1620,9 @@ class GroupchatControl(ChatControlBase):
         self.is_connected = False
         ChatControlBase.got_disconnected(self)
 
+        con = app.connections[self.account]
+        con.get_module('Chatstate').remove_delay_timeout(self.contact)
+
         contact = app.contacts.get_groupchat_contact(self.account,
                                                      self.room_jid)
         if contact is not None:
@@ -2550,6 +2553,9 @@ class GroupchatControl(ChatControlBase):
                 else:
                     start_iter.backward_chars(len(begin))
 
+                con = app.connections[self.account]
+                con.get_module('Chatstate').block_chatstates(self.contact, True)
+
                 message_buffer.delete(start_iter, end_iter)
                 # get a shell-like completion
                 # if there's more than one nick for this completion, complete
@@ -2579,6 +2585,9 @@ class GroupchatControl(ChatControlBase):
                 else:
                     completion = self.nick_hits[0]
                 message_buffer.insert_at_cursor(completion + add)
+
+                con.get_module('Chatstate').block_chatstates(self.contact, False)
+
                 self.last_key_tabs = True
                 return True
             self.last_key_tabs = False



View it on GitLab: https://dev.gajim.org/gajim/gajim/commit/4aca2eeae2e38a49916e81b65e49951197f7801e

-- 
View it on GitLab: https://dev.gajim.org/gajim/gajim/commit/4aca2eeae2e38a49916e81b65e49951197f7801e
You're receiving this email because of your account on dev.gajim.org.
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.gajim.org/pipermail/commits/attachments/20190104/6297bed4/attachment-0001.html>


More information about the Commits mailing list