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
This commit is contained in:
parent
4bd14bc51d
commit
4aca2eeae2
|
@ -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)
|
||||
|
|
|
@ -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'):
|
||||
|
|
|
@ -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
|
||||
|
||||
self._timeout_id = GLib.timeout_add_seconds(
|
||||
2, self._check_last_interaction)
|
||||
@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
|
||||
|
||||
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]:
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue