email-notifs: Use bracketed prefix to indicate a resolved topic.

Some email clients (notably, Gmail Web) support automatically threading
emails together if recipients and subjects match[1]. Manual testing
indicated that prefixing a subject with "[bracketed content]" does not
break this threading behavior, but the added checkmark in a resolved
topic's title does. Before sending an email notification, determine
whether the topic is resolved, and pass this information to the Jinja
template to properly format a threadable email subject.

Fixes: #22538

[1]: https://support.google.com/mail/answer/5900
This commit is contained in:
Josh Klar 2022-10-27 15:25:31 -07:00 committed by Tim Abbott
parent 69c042bff6
commit c15d066bf5
4 changed files with 35 additions and 2 deletions

View File

@ -1,7 +1,17 @@
{% if show_message_content %}
{% if group_pm %} {% trans %}Group PMs with {{ huddle_display_name }}{% endtrans %}
{% elif private_message %} {% trans %}PMs with {{ sender_str }}{% endtrans %}
{% elif stream_email_notify or mention %} #{{ stream_name }} > {{ topic_name }}
{% elif stream_email_notify or mention %}
{#
Some email clients, like Gmail Web (as of 2022), will auto-thread
emails that share a subject and recipients, but will disregard
[Bracketed Prefixes] in the style of old-school mailing lists. We take
advantage of this to retain thread continuity in email notifications
even after a topic is resolved.
#}
{% if topic_resolved %}{% trans %}[resolved] #{{ stream_name }} > {{ topic_name }}{% endtrans %}
{% else %}#{{ stream_name }} > {{ topic_name }}
{% endif %}
{% endif %}
{% else %}
{% trans %}New messages{% endtrans %}

View File

@ -27,6 +27,7 @@ from zerver.lib.notification_data import get_mentioned_user_group_name
from zerver.lib.queue import queue_json_publish
from zerver.lib.send_email import FromAddress, send_future_email
from zerver.lib.soft_deactivation import soft_reactivate_if_personal_notification
from zerver.lib.topic import get_topic_resolution_and_bare_name
from zerver.lib.types import DisplayRecipientT
from zerver.lib.url_encoding import (
huddle_narrow_url,
@ -481,10 +482,11 @@ def do_send_missedmessage_events_reply_in_zulip(
)
message = missed_messages[0]["message"]
stream = Stream.objects.only("id", "name").get(id=message.recipient.type_id)
topic_name = message.topic_name()
topic_resolved, topic_name = get_topic_resolution_and_bare_name(message.topic_name())
context.update(
stream_name=stream.name,
topic_name=topic_name,
topic_resolved=topic_resolved,
)
else:
raise AssertionError("Invalid messages!")

View File

@ -270,3 +270,17 @@ def get_topic_history_for_stream(
cursor.close()
return generate_topic_history_from_db_rows(rows)
def get_topic_resolution_and_bare_name(stored_name: str) -> Tuple[bool, str]:
"""
Resolved topics are denoted only by a title change, not by a boolean toggle in a database column. This
method inspects the topic name and returns a tuple of:
- Whether the topic has been resolved
- The topic name with the resolution prefix, if present in stored_name, removed
"""
if stored_name.startswith(RESOLVED_TOPIC_PREFIX):
return (True, stored_name[len(RESOLVED_TOPIC_PREFIX) :])
return (False, stored_name)

View File

@ -1053,6 +1053,13 @@ class TestMissedMessages(ZulipTestCase):
def test_extra_context_in_missed_stream_messages_email_notify(self) -> None:
self._extra_context_in_missed_stream_messages_email_notify(False)
@override_settings(SEND_MISSED_MESSAGE_EMAILS_AS_USER=True)
def test_resolved_topic_missed_stream_messages_thread_friendly_as_user(self) -> None:
self._resolved_topic_missed_stream_messages_thread_friendly(True)
def test_resolved_topic_missed_stream_messages_thread_friendly(self) -> None:
self._resolved_topic_missed_stream_messages_thread_friendly(False)
@override_settings(EMAIL_GATEWAY_PATTERN="")
def test_reply_warning_in_missed_personal_messages(self) -> None:
self._reply_warning_in_missed_personal_messages(False)