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/4aca2eeae2e38a49916e81b65e49951197f...