typing: Add support for empty topic name.

This commit is a part of the work to support empty string
as a topic name.

Previously, empty string was not a valid topic name.

Now, typing operation supports empty topic name.

Adds backward compatibility for:
-  `topic` field in the `typing` event type
This commit is contained in:
Prakhar Pratyush
2024-11-14 22:08:12 +05:30
committed by Tim Abbott
parent 27e95f7d33
commit 01f749c0b8
5 changed files with 88 additions and 6 deletions

View File

@@ -42,6 +42,7 @@ format used by the Zulip server that they are interacting with.
* `topic` field in the `delete_message` event type
* `orig_subject` and `subject` fields in the `update_message` event type
* `topic_name` field in the `user_topic` event type
* `topic` field in the `typing` event type
* [`GET /messages`](/api/get-messages),
[`GET /messages/{message_id}`](/api/get-message): Added `allow_empty_topic_name`

View File

@@ -2768,8 +2768,19 @@ paths:
Topic within the channel where the message is being typed.
**Changes**: New in Zulip 4.0 (feature level 58). Previously,
typing notifications were only for direct messages.
For clients that don't support the `empty_topic_name` [client capability][client-capabilities],
if the actual topic name is empty string, this field's value will instead
be the value of `realm_empty_topic_display_name` found in the
[`POST /register`](/api/register-queue) response.
**Changes**: Before 10.0 (feature level 334), `empty_topic_name`
client capability didn't exist and empty string as the topic name for
channel messages wasn't allowed.
New in Zulip 4.0 (feature level 58). Previously, typing notifications
were only for direct messages.
[client-capabilities]: /api/register-queue#parameter-client_capabilities
example:
{
"type": "typing",
@@ -20603,8 +20614,15 @@ paths:
Topic to which message is being typed. Required for the `"stream"` or `"channel"`
type. Ignored in the case of `"direct"` type.
**Changes**: New in Zulip 4.0 (feature level 58). Previously, typing
notifications were only for direct messages.
Note: When the value of `realm_empty_topic_display_name` found in
the [POST /register](/api/register-queue) response is used for this
parameter, it is interpreted as an empty string.
**Changes**: Before Zulip 10.0 (feature level 334), empty string
was not a valid topic name for channel messages.
New in Zulip 4.0 (feature level 58). Previously, typing notifications
were only for direct messages.
type: string
example: typing notifications
required:

View File

@@ -377,10 +377,11 @@ class EmptyTopicNameTest(ZulipTestCase):
apply_markdown=True,
client_type_name="website",
empty_topic_name=True,
event_types=["message", "update_message", "delete_message", "user_topic"],
event_types=["message", "update_message", "delete_message", "user_topic", "typing"],
last_connection_time=time.time(),
queue_timeout=600,
realm_id=hamlet.realm.id,
stream_typing_notifications=True,
user_profile_id=hamlet.id,
)
client = allocate_client_descriptor(queue_data)
@@ -454,6 +455,24 @@ class EmptyTopicNameTest(ZulipTestCase):
self.assertEqual(events[6]["topic_name"], "")
self.assertEqual(events[7]["topic_name"], "")
params = dict(
type="stream",
op="start",
stream_id=str(denmark.id),
topic="",
)
self.api_post(hamlet, "/api/v1/typing", params)
params = dict(
type="stream",
op="start",
stream_id=str(verona.id),
topic=Message.EMPTY_TOPIC_FALLBACK_NAME,
)
self.api_post(hamlet, "/api/v1/typing", params)
events = client.event_queue.contents()
self.assertEqual(events[8]["topic"], "")
self.assertEqual(events[9]["topic"], "")
def test_client_not_supports_empty_topic_name(self) -> None:
iago = self.example_user("iago")
hamlet = self.example_user("hamlet")
@@ -462,10 +481,11 @@ class EmptyTopicNameTest(ZulipTestCase):
apply_markdown=True,
client_type_name="zulip-mobile",
empty_topic_name=False,
event_types=["message", "update_message", "delete_message", "user_topic"],
event_types=["message", "update_message", "delete_message", "user_topic", "typing"],
last_connection_time=time.time(),
queue_timeout=600,
realm_id=hamlet.realm.id,
stream_typing_notifications=True,
user_profile_id=hamlet.id,
)
client = allocate_client_descriptor(queue_data)
@@ -539,6 +559,24 @@ class EmptyTopicNameTest(ZulipTestCase):
self.assertEqual(events[6]["topic_name"], Message.EMPTY_TOPIC_FALLBACK_NAME)
self.assertEqual(events[7]["topic_name"], Message.EMPTY_TOPIC_FALLBACK_NAME)
params = dict(
type="stream",
op="start",
stream_id=str(denmark.id),
topic="",
)
self.api_post(hamlet, "/api/v1/typing", params)
params = dict(
type="stream",
op="start",
stream_id=str(verona.id),
topic=Message.EMPTY_TOPIC_FALLBACK_NAME,
)
self.api_post(hamlet, "/api/v1/typing", params)
events = client.event_queue.contents()
self.assertEqual(events[8]["topic"], Message.EMPTY_TOPIC_FALLBACK_NAME)
self.assertEqual(events[9]["topic"], Message.EMPTY_TOPIC_FALLBACK_NAME)
def test_fetch_messages(self) -> None:
hamlet = self.example_user("hamlet")
self.login_user(hamlet)

View File

@@ -1642,6 +1642,27 @@ def process_user_topic_event(event: Mapping[str, Any], users: Iterable[int]) ->
client.add_event(empty_topic_name_fallback_event)
def process_stream_typing_notification_event(
event: Mapping[str, Any], users: Iterable[int]
) -> None:
empty_topic_name_fallback_event: Mapping[str, Any] | dict[str, Any]
if event.get("topic") == "":
empty_topic_name_fallback_event = dict(event)
empty_topic_name_fallback_event["topic"] = Message.EMPTY_TOPIC_FALLBACK_NAME
else:
empty_topic_name_fallback_event = event
for user_profile_id in users:
for client in get_client_descriptors_for_user(user_profile_id):
if not client.accepts_event(event):
continue
if client.empty_topic_name:
client.add_event(event)
else:
client.add_event(empty_topic_name_fallback_event)
def process_notification(notice: Mapping[str, Any]) -> None:
event: Mapping[str, Any] = notice["event"]
users: list[int] | list[Mapping[str, Any]] = notice["users"]
@@ -1672,6 +1693,8 @@ def process_notification(notice: Mapping[str, Any]) -> None:
process_user_group_name_update_event(event, cast(list[int], users))
elif event["type"] == "user_topic":
process_user_topic_event(event, cast(list[int], users))
elif event["type"] == "typing" and event["message_type"] == "stream":
process_stream_typing_notification_event(event, cast(list[int], users))
elif event["type"] == "cleanup_queue":
# cleanup_event_queue may generate this event to forward cleanup
# requests to the right shard.

View File

@@ -8,6 +8,7 @@ from zerver.actions.typing import check_send_typing_notification, do_send_stream
from zerver.lib.exceptions import JsonableError
from zerver.lib.response import json_success
from zerver.lib.streams import access_stream_by_id_for_message, access_stream_for_send_message
from zerver.lib.topic import maybe_rename_general_chat_to_empty_topic
from zerver.lib.typed_endpoint import ApiParamConfig, OptionalTopic, typed_endpoint
from zerver.models import UserProfile
@@ -44,6 +45,7 @@ def send_notification_backend(
# permission to send messages to it.
stream = access_stream_by_id_for_message(user_profile, stream_id)[0]
access_stream_for_send_message(user_profile, stream, forwarder_user_profile=None)
topic = maybe_rename_general_chat_to_empty_topic(topic)
do_send_stream_typing_notification(user_profile, operator, stream, topic)
else:
if notification_to is None: