push_notifications: Show EMPTY_TOPIC_FALLBACK_NAME for topic="".

This commit adds support to display `Message.EMPTY_TOPIC_FALLBACK_NAME`
value (translated) in the push notifications for topics having the
actual value of empty string.

Fixes part of #32996.
This commit is contained in:
Prakhar Pratyush
2025-02-05 13:04:03 +05:30
committed by Tim Abbott
parent d3b601ef3b
commit 23f16885d5
2 changed files with 34 additions and 8 deletions

View File

@@ -47,6 +47,7 @@ from zerver.lib.remote_server import (
from zerver.lib.soft_deactivation import soft_reactivate_if_personal_notification from zerver.lib.soft_deactivation import soft_reactivate_if_personal_notification
from zerver.lib.tex import change_katex_to_raw_latex from zerver.lib.tex import change_katex_to_raw_latex
from zerver.lib.timestamp import datetime_to_timestamp from zerver.lib.timestamp import datetime_to_timestamp
from zerver.lib.topic import get_topic_display_name
from zerver.lib.url_decoding import is_same_server_message_link from zerver.lib.url_decoding import is_same_server_message_link
from zerver.lib.users import check_can_access_user from zerver.lib.users import check_can_access_user
from zerver.models import ( from zerver.models import (
@@ -992,7 +993,7 @@ def get_message_payload(
data["recipient_type"] = "stream" data["recipient_type"] = "stream"
data["stream"] = get_message_stream_name_from_database(message) data["stream"] = get_message_stream_name_from_database(message)
data["stream_id"] = message.recipient.type_id data["stream_id"] = message.recipient.type_id
data["topic"] = message.topic_name() data["topic"] = get_topic_display_name(message.topic_name(), user_profile.default_language)
elif message.recipient.type == Recipient.DIRECT_MESSAGE_GROUP: elif message.recipient.type == Recipient.DIRECT_MESSAGE_GROUP:
data["recipient_type"] = "private" data["recipient_type"] = "private"
data["pm_users"] = direct_message_group_users(message.recipient.id) data["pm_users"] = direct_message_group_users(message.recipient.id)
@@ -1002,7 +1003,7 @@ def get_message_payload(
return data return data
def get_apns_alert_title(message: Message) -> str: def get_apns_alert_title(message: Message, language: str) -> str:
""" """
On an iOS notification, this is the first bolded line. On an iOS notification, this is the first bolded line.
""" """
@@ -1012,7 +1013,8 @@ def get_apns_alert_title(message: Message) -> str:
return ", ".join(sorted(r["full_name"] for r in recipients)) return ", ".join(sorted(r["full_name"] for r in recipients))
elif message.is_stream_message(): elif message.is_stream_message():
stream_name = get_message_stream_name_from_database(message) stream_name = get_message_stream_name_from_database(message)
return f"#{stream_name} > {message.topic_name()}" topic_display_name = get_topic_display_name(message.topic_name(), language)
return f"#{stream_name} > {topic_display_name}"
# For 1:1 direct messages, we just show the sender name. # For 1:1 direct messages, we just show the sender name.
return message.sender.full_name return message.sender.full_name
@@ -1105,7 +1107,7 @@ def get_message_payload_apns(
content, _ = truncate_content(get_mobile_push_content(message.rendered_content)) content, _ = truncate_content(get_mobile_push_content(message.rendered_content))
apns_data = { apns_data = {
"alert": { "alert": {
"title": get_apns_alert_title(message), "title": get_apns_alert_title(message, user_profile.default_language),
"subtitle": get_apns_alert_subtitle( "subtitle": get_apns_alert_subtitle(
message, trigger, user_profile, mentioned_user_group_name, can_access_sender message, trigger, user_profile, mentioned_user_group_name, can_access_sender
), ),

View File

@@ -4196,13 +4196,21 @@ class TestGetAPNsPayload(PushNotificationTest):
self.assertDictEqual(payload, expected) self.assertDictEqual(payload, expected)
mock_push_notifications.assert_called() mock_push_notifications.assert_called()
def _test_get_message_payload_apns_stream_message(self, trigger: str) -> None: def _test_get_message_payload_apns_stream_message(
self, trigger: str, empty_string_topic: bool = False
) -> None:
stream = Stream.objects.filter(name="Verona").get() stream = Stream.objects.filter(name="Verona").get()
message = self.get_message(Recipient.STREAM, stream.id, stream.realm_id) message = self.get_message(Recipient.STREAM, stream.id, stream.realm_id)
topic_display_name = message.topic_name()
if empty_string_topic:
message.set_topic_name("")
message.save()
topic_display_name = Message.EMPTY_TOPIC_FALLBACK_NAME
payload = get_message_payload_apns(self.sender, message, trigger) payload = get_message_payload_apns(self.sender, message, trigger)
expected = { expected = {
"alert": { "alert": {
"title": "#Verona > Test topic", "title": f"#Verona > {topic_display_name}",
"subtitle": "King Hamlet:", "subtitle": "King Hamlet:",
"body": message.content, "body": message.content,
}, },
@@ -4216,7 +4224,7 @@ class TestGetAPNsPayload(PushNotificationTest):
"sender_id": self.sender.id, "sender_id": self.sender.id,
"stream": stream.name, "stream": stream.name,
"stream_id": stream.id, "stream_id": stream.id,
"topic": message.topic_name(), "topic": topic_display_name,
"server": settings.EXTERNAL_HOST, "server": settings.EXTERNAL_HOST,
"realm_id": self.sender.realm.id, "realm_id": self.sender.realm.id,
"realm_name": self.sender.realm.name, "realm_name": self.sender.realm.name,
@@ -4235,6 +4243,11 @@ class TestGetAPNsPayload(PushNotificationTest):
def test_get_message_payload_apns_followed_topic_message(self) -> None: def test_get_message_payload_apns_followed_topic_message(self) -> None:
self._test_get_message_payload_apns_stream_message(NotificationTriggers.FOLLOWED_TOPIC_PUSH) self._test_get_message_payload_apns_stream_message(NotificationTriggers.FOLLOWED_TOPIC_PUSH)
def test_get_message_payload_apns_empty_string_topic(self) -> None:
self._test_get_message_payload_apns_stream_message(
NotificationTriggers.STREAM_PUSH, empty_string_topic=True
)
def test_get_message_payload_apns_stream_mention(self) -> None: def test_get_message_payload_apns_stream_mention(self) -> None:
user_profile = self.example_user("othello") user_profile = self.example_user("othello")
stream = Stream.objects.filter(name="Verona").get() stream = Stream.objects.filter(name="Verona").get()
@@ -4469,9 +4482,11 @@ class TestGetGCMPayload(PushNotificationTest):
truncate_content: bool = False, truncate_content: bool = False,
mentioned_user_group_id: int | None = None, mentioned_user_group_id: int | None = None,
mentioned_user_group_name: str | None = None, mentioned_user_group_name: str | None = None,
empty_string_topic: bool = False,
) -> None: ) -> None:
stream = Stream.objects.filter(name="Verona").get() stream = Stream.objects.filter(name="Verona").get()
message = self.get_message(Recipient.STREAM, stream.id, stream.realm_id) message = self.get_message(Recipient.STREAM, stream.id, stream.realm_id)
content = message.content content = message.content
if truncate_content: if truncate_content:
message.content = "a" * 210 message.content = "a" * 210
@@ -4479,6 +4494,12 @@ class TestGetGCMPayload(PushNotificationTest):
message.save() message.save()
content = "a" * 200 + "" content = "a" * 200 + ""
topic_display_name = message.topic_name()
if empty_string_topic:
message.set_topic_name("")
message.save()
topic_display_name = Message.EMPTY_TOPIC_FALLBACK_NAME
hamlet = self.example_user("hamlet") hamlet = self.example_user("hamlet")
payload, gcm_options = get_message_payload_gcm( payload, gcm_options = get_message_payload_gcm(
hamlet, message, mentioned_user_group_id, mentioned_user_group_name hamlet, message, mentioned_user_group_id, mentioned_user_group_name
@@ -4502,7 +4523,7 @@ class TestGetGCMPayload(PushNotificationTest):
"recipient_type": "stream", "recipient_type": "stream",
"stream": stream.name, "stream": stream.name,
"stream_id": stream.id, "stream_id": stream.id,
"topic": message.topic_name(), "topic": topic_display_name,
} }
if mentioned_user_group_id is not None: if mentioned_user_group_id is not None:
@@ -4533,6 +4554,9 @@ class TestGetGCMPayload(PushNotificationTest):
mentioned_user_group_name="mobile_team", mentioned_user_group_name="mobile_team",
) )
def test_get_message_payload_gcm_empty_string_topic(self) -> None:
self._test_get_message_payload_gcm_stream_message(empty_string_topic=True)
def test_get_message_payload_gcm_direct_message(self) -> None: def test_get_message_payload_gcm_direct_message(self) -> None:
message = self.get_message( message = self.get_message(
Recipient.PERSONAL, Recipient.PERSONAL,