discourse/plugins/chat/app/views/user_notifications/chat_summary.html.erb
Régis Hanol 71391cd40d PERF: fix performance of chat email notifications
When chat is enabled, there's a scheduled job that runs every 5 minutes to check whether we need to send a "chat summary" email to users with unread chat messages or mentions.

On Discourse with a large number of users, the query used wasn't optimal and sometimes taking minutes. Which isn't good when the query is called every 5 minutes 😬

This PR reworks the query in `Chat::Mailer.send_unread_mentions_summary`.

Instead of starting from the `users` table, it starts from the `user_chat_channel_memberships` table which is the main piece tying everything together.

The new query is mostly similar to the previous one, with some bug fixes (like ensuring the user has `allow_private_messages` enabled for direct messages) and is also slightly simpler since it doesn't keep track of the `memberships_with_unread_messages` anymore. That part has been moved to the `user_notifications.chat_summary` email method.

The `UserEmailExtension` has been deleted since that was using to N+1 update the `user_chat_channel_memberships.last_unread_mention_when_emailed_it`(quite a mouthful 😛) but that's now done directly in the `user_notifications.chat_summary` email method.

The "plat de résistance" of that PR - the `user_notifications.chat_summary` method has been re-worked for improved performances 🚀

Instead of doing everything in one query, it does 4 tiny ones.

- One to retrieve the list of unread mentions (@something) in "category" channels
- One to retrieve the list of unread messages in "direct message" channels (aka. 1-1 and group discussions)
- One to load all the chat messages for each "category" channels from the last unread mention
- One to load all the chat messages for each "direct message" channels from the last unread message

All the specs for both `Chat::Mailer` and `UserNotification.chat_summary` have been rewriten for easier comprehension and faster execution (mostly by not using chat services which makes the specs go 10x slower...)

Internal ref - t/129848
2024-06-10 14:25:06 +02:00

93 lines
4.3 KiB
Plaintext

<div class="summary-email">
<table class="chat-summary-header text-header with-dir" style="background-color:#<%= @header_bgcolor -%>;width:100%;min-width:100%;">
<tr>
<td align="center" style="text-align: center;padding: 20px 0; font-family:Arial,sans-serif;">
<a href="<%= Discourse.base_url %>" style="color:#<%= @header_color -%>;font-size:22px;text-decoration:none;">
<%- if logo_url.blank? %>
<%= SiteSetting.title %>
<%- else %>
<img src="<%= logo_url %>" height="40" style="clear:both;display:block;height:40px;margin:auto;max-width:100%;outline:0;text-decoration:none;" alt="<%= SiteSetting.title %>">
<%- end %>
</a>
</td>
</tr>
<tr>
<td align="center" style="font-weight:bold;font-size:22px;color:#<%= @header_color -%>">
<%= I18n.t("user_notifications.chat_summary.description", count: @count) %>
</td>
</tr>
</table>
<%- [@grouped_channels, @grouped_dms].each do |grouped_messages| %>
<%- grouped_messages.each do |channel, messages| %>
<%- other_messages_count = messages.size - 2 %>
<table class="chat-summary-content" style="padding:1em;margin-top:20px;width:100%;min-width:100%;background-color:#f7f7f7;">
<tbody>
<tr>
<td colspan="100%">
<h5 style="margin:0.5em 0 0.5em 0;font-size:0.9em;">
<%- if SiteSetting.private_email %>
<%= I18n.t("system_messages.private_channel_title", id: channel.id) %>
<%- else %>
<%= channel.title(@user) %>
<%- end %>
</h5>
</td>
</tr>
<%- unless SiteSetting.private_email %>
<%- messages.take(2).each do |chat_message| %>
<%- sender = chat_message.user %>
<tr class="message-row">
<td style="white-space:nowrap;vertical-align:top;padding:<%= rtl? ? '1em 2em 0 0' : '1em 0 0 2em' %>">
<img src="<%= sender.small_avatar_url -%>" style="height:20px;width:20px;margin:<%= rtl? ? '0 0 5px 0' : '0 5px 0 0' %>;border-radius:50%;clear:both;display:inline-block;outline:0;text-decoration:none;vertical-align:top;" alt="<%= sender.display_name -%>">
<span style="display:inline-block;color:#0a0a0a;vertical-align:top;font-weight:bold;">
<%= sender.display_name -%>
</span>
<span style="display:inline-block;color:#0a0a0a;font-size:0.8em;">
<%= I18n.l(@user_tz.to_local(chat_message.created_at), format: :long) -%>
</span>
</td>
</tr>
<tr>
<td style="width:99%;margin:0;padding:<%= rtl? ? '0 2em 0 0' : '0 0 0 2em' %>;vertical-align:top;">
<%= email_excerpt(chat_message.cooked_for_excerpt) %>
</td>
</tr>
<%- end %>
<%- end %>
<tr>
<td colspan="100%" style="padding:<%= rtl? ? '2em 2em 0 0' : '2em 0 0 2em' %>">
<a class="more-messages-link" href="<%= messages.first.full_url %>">
<%- if SiteSetting.private_email || other_messages_count <= 0 %>
<%= I18n.t("user_notifications.chat_summary.view_messages", count: messages.size) %>
<%- else %>
<%= I18n.t("user_notifications.chat_summary.view_more", count: other_messages_count) %>
<%- end %>
</a>
</td>
</tr>
</tbody>
</table>
<%- end %>
<%- end %>
</div>
<table class='summary-footer with-dir' style="margin-top:2em;width:100%">
<tr>
<td align="center">
<%- if @unsubscribe_link %>
<%= raw(t 'user_notifications.chat_summary.unsubscribe',
site_link: html_site_link,
email_preferences_link: link_to(t('user_notifications.chat_summary.your_chat_settings'), @preferences_path),
unsubscribe_link: link_to(t('user_notifications.digest.click_here'), @unsubscribe_link)) %>
<%- else %>
<%= raw(t 'user_notifications.chat_summary.unsubscribe_no_link',
site_link: html_site_link,
email_preferences_link: link_to(t('user_notifications.chat_summary.your_chat_settings'), @preferences_path)) %>
<%- end %>
</td>
</tr>
</table>