diff --git a/synapse/handlers/typing.py b/synapse/handlers/typing.py
index d88a53242c..912cfd708b 100644
--- a/synapse/handlers/typing.py
+++ b/synapse/handlers/typing.py
@@ -43,7 +43,8 @@ class TypingNotificationHandler(BaseHandler):
self.federation.register_edu_handler("m.typing", self._recv_edu)
- self._member_typing_until = {}
+ self._member_typing_until = {} # clock time we expect to stop
+ self._member_typing_timer = {} # deferreds to manage theabove
@defer.inlineCallbacks
def started_typing(self, target_user, auth_user, room_id, timeout):
@@ -58,7 +59,13 @@ class TypingNotificationHandler(BaseHandler):
was_present = member in self._member_typing_until
+ if member in self._member_typing_timer:
+ self.clock.cancel_call_later(self._member_typing_timer[member])
+
self._member_typing_until[member] = until
+ self._member_typing_timer[member] = self.clock.call_later(
+ timeout / 1000, lambda: self._stopped_typing(member)
+ )
if was_present:
# No point sending another notification
@@ -80,16 +87,25 @@ class TypingNotificationHandler(BaseHandler):
member = RoomMember(room_id=room_id, user=target_user)
+ yield self._stopped_typing(member)
+
+ @defer.inlineCallbacks
+ def _stopped_typing(self, member):
if member not in self._member_typing_until:
# No point
defer.returnValue(None)
yield self._push_update(
- room_id=room_id,
- user=target_user,
+ room_id=member.room_id,
+ user=member.user,
typing=False,
)
+ del self._member_typing_until[member]
+
+ self.clock.cancel_call_later(self._member_typing_timer[member])
+ del self._member_typing_timer[member]
+
@defer.inlineCallbacks
def _push_update(self, room_id, user, typing):
localusers = set()
diff --git a/tests/handlers/test_typing.py b/tests/handlers/test_typing.py
index 7e6ed9a42f..6b9e22d396 100644
--- a/tests/handlers/test_typing.py
+++ b/tests/handlers/test_typing.py
@@ -238,9 +238,11 @@ class TypingNotificationsTestCase(unittest.TestCase):
# Gut-wrenching
from synapse.handlers.typing import RoomMember
- self.handler._member_typing_until[
- RoomMember(self.room_id, self.u_apple)
- ] = 1002000
+ member = RoomMember(self.room_id, self.u_apple)
+ self.handler._member_typing_until[member] = 1002000
+ self.handler._member_typing_timer[member] = (
+ self.clock.call_later(1002, lambda: 0)
+ )
yield self.handler.stopped_typing(
target_user=self.u_apple,
@@ -256,3 +258,31 @@ class TypingNotificationsTestCase(unittest.TestCase):
])
yield put_json.await_calls()
+
+ @defer.inlineCallbacks
+ def test_typing_timeout(self):
+ self.room_members = [self.u_apple, self.u_banana]
+
+ yield self.handler.started_typing(
+ target_user=self.u_apple,
+ auth_user=self.u_apple,
+ room_id=self.room_id,
+ timeout=10000,
+ )
+
+ self.mock_update_client.assert_has_calls([
+ call(observer_user=self.u_banana,
+ observed_user=self.u_apple,
+ room_id=self.room_id,
+ typing=True),
+ ])
+ self.mock_update_client.reset_mock()
+
+ self.clock.advance_time(11)
+
+ self.mock_update_client.assert_has_calls([
+ call(observer_user=self.u_banana,
+ observed_user=self.u_apple,
+ room_id=self.room_id,
+ typing=False),
+ ])
|