Skip to content

Commit 73b0f51

Browse files
committed
boxes: Configure private_box_view to send typing status.
This commit captures the user's typing status using urwid's connect_signal(), observing for changes in the msg_write_box. The msg_write_box is connected to functions that send start and stop events with a limit on how frequently the functions can be called. The corresponding wait periods after the user starts and stops typing are used to restrict calling the function excessively. Fixes #593.
1 parent c121497 commit 73b0f51

File tree

1 file changed

+41
-3
lines changed

1 file changed

+41
-3
lines changed

zulipterminal/ui_tools/boxes.py

Lines changed: 41 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
import re
22
import unicodedata
33
from collections import OrderedDict, defaultdict
4-
from datetime import date, datetime
4+
from datetime import date, datetime, timedelta
55
from sys import platform
6-
from time import ctime, time
6+
from time import ctime, sleep, time
77
from typing import Any, Callable, Dict, List, Optional, Tuple, Union
88
from urllib.parse import urljoin, urlparse
99

@@ -22,7 +22,7 @@
2222
STREAM_TOPIC_SEPARATOR, TIME_MENTION_MARKER,
2323
)
2424
from zulipterminal.helper import (
25-
Message, format_string, match_emoji, match_group, match_stream,
25+
Message, asynch, format_string, match_emoji, match_group, match_stream,
2626
match_topics, match_user,
2727
)
2828
from zulipterminal.ui_tools.buttons import EditModeButton
@@ -40,6 +40,8 @@ def __init__(self, view: Any) -> None:
4040
self.stream_id = None # type: Optional[int]
4141
self.recipient_user_ids = [] # type: List[int]
4242
self.msg_body_edit_enabled = True
43+
self.send_next_typing_update = datetime.now()
44+
self.last_key_update = datetime.now()
4345
self.FOCUS_CONTAINER_HEADER = 0
4446
self.FOCUS_HEADER_BOX_RECIPIENT = 0
4547
self.FOCUS_HEADER_BOX_STREAM = 0
@@ -57,13 +59,19 @@ def main_view(self, new: bool) -> Any:
5759
def set_editor_mode(self) -> None:
5860
self.view.controller.enter_editor_mode_with(self)
5961

62+
def send_stop_typing_status(self) -> None:
63+
self.model.send_typing_status_by_user_ids(self.recipient_user_ids,
64+
status='stop')
65+
self.send_next_typing_update = datetime.now()
66+
6067
def private_box_view(self, button: Any=None, email: str='',
6168
recipient_user_ids: Optional[List[int]]=None) -> None:
6269
self.set_editor_mode()
6370
if recipient_user_ids:
6471
self.recipient_user_ids = recipient_user_ids
6572
if email == '' and button is not None:
6673
email = button.email
74+
self.send_next_typing_update = datetime.now()
6775
self.to_write_box = ReadlineEdit("To: ", edit_text=email)
6876
self.msg_write_box = ReadlineEdit(multiline=True)
6977
self.msg_write_box.enable_autocomplete(
@@ -83,6 +91,33 @@ def private_box_view(self, button: Any=None, email: str='',
8391
]
8492
self.focus_position = self.FOCUS_CONTAINER_MESSAGE
8593

94+
# Typing status is sent in regular intervals to limit the number of
95+
# notifications sent. Idleness should also prompt a notification.
96+
# Refer to https://zulip.com/api/set-typing-status for the protocol
97+
# on typing notifications sent by clients.
98+
TYPING_STARTED_WAIT_PERIOD = 10
99+
TYPING_STOPPED_WAIT_PERIOD = 5
100+
101+
start_period_delta = timedelta(seconds=TYPING_STARTED_WAIT_PERIOD)
102+
stop_period_delta = timedelta(seconds=TYPING_STOPPED_WAIT_PERIOD)
103+
104+
def on_type_send_status(edit: object, new_edit_text: str) -> None:
105+
if new_edit_text:
106+
self.last_key_update = datetime.now()
107+
if self.last_key_update > self.send_next_typing_update:
108+
self.model.send_typing_status_by_user_ids(
109+
self.recipient_user_ids, status='start')
110+
self.send_next_typing_update += start_period_delta
111+
112+
@asynch
113+
def on_idle_send_status(*args: object) -> None:
114+
sleep(TYPING_STOPPED_WAIT_PERIOD)
115+
if datetime.now() > self.last_key_update + stop_period_delta:
116+
self.send_stop_typing_status()
117+
118+
urwid.connect_signal(self.msg_write_box, 'change', on_type_send_status)
119+
urwid.connect_signal(self.msg_write_box, 'change', on_idle_send_status)
120+
86121
def stream_box_view(self, stream_id: int, caption: str='', title: str='',
87122
) -> None:
88123
self.set_editor_mode()
@@ -343,6 +378,7 @@ def keypress(self, size: urwid_Size, key: str) -> Optional[str]:
343378
content=self.msg_write_box.edit_text
344379
)
345380
else:
381+
self.send_stop_typing_status()
346382
if self.msg_edit_id:
347383
success = self.model.update_private_message(
348384
content=self.msg_write_box.edit_text,
@@ -355,12 +391,14 @@ def keypress(self, size: urwid_Size, key: str) -> Optional[str]:
355391
)
356392
if success:
357393
self.msg_write_box.edit_text = ''
394+
self.send_stop_typing_status()
358395
if self.msg_edit_id:
359396
self.msg_edit_id = None
360397
self.keypress(size, 'esc')
361398
elif is_command_key('GO_BACK', key):
362399
self.msg_edit_id = None
363400
self.msg_body_edit_enabled = True
401+
self.send_stop_typing_status()
364402
self.view.controller.exit_editor_mode()
365403
self.main_view(False)
366404
self.view.middle_column.set_focus('body')

0 commit comments

Comments
 (0)