Skip to content

Commit

Permalink
notifications: Add initial implementation for reaction notifications.
Browse files Browse the repository at this point in the history
  • Loading branch information
akarsh-jain-790 committed Feb 11, 2024
1 parent d3b5a76 commit c741c14
Show file tree
Hide file tree
Showing 14 changed files with 555 additions and 0 deletions.
9 changes: 9 additions & 0 deletions web/src/message_notifications.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,11 @@ import * as user_topics from "./user_topics";

function get_notification_content(message) {
let content;

if (message.reaction_label) {
return message.reaction_label;
}

// Convert the content to plain text, replacing emoji with their alt text
const $content = $("<div>").html(message.content);
ui_util.replace_emoji_with_text($content);
Expand Down Expand Up @@ -89,6 +94,10 @@ function get_notification_title(message, msg_count) {
let title = message.sender_full_name;
let other_recipients;

if (message.reacted_by) {
title = message.reacted_by;
}

if (msg_count > 1) {
title = msg_count + " messages from " + title;
}
Expand Down
121 changes: 121 additions & 0 deletions web/src/reaction_notifications.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
import $ from "jquery";

import * as desktop_notifications from "./desktop_notifications";
import {$t} from "./i18n";
import * as message_notifications from "./message_notifications";
import * as message_store from "./message_store";
import {page_params} from "./page_params";
import * as people from "./people";
import {get_local_reaction_id} from "./reactions";
import * as ui_util from "./ui_util";
import {user_settings} from "./user_settings";
import * as user_topics from "./user_topics";

function generate_notification_title(emoji_name, user_ids) {
const usernames = people.get_display_full_names(
user_ids.filter((user_id) => user_id !== page_params.user_id),
);

const current_user_reacted = user_ids.length !== usernames.length;

const context = {
emoji_name: ":" + emoji_name + ":",
};

if (user_ids.length === 1) {
context.username = usernames[0];
return $t({defaultMessage: "{username} reacted with {emoji_name}"}, context);
}

if (user_ids.length === 2 && current_user_reacted) {
context.other_username = usernames[0];
return $t(
{
defaultMessage: "You and {other_username} reacted with {emoji_name}",
},
context,
);
}

context.total_reactions = usernames.length;
context.last_username = usernames.at(-1);

return $t(
{
defaultMessage:
"{last_username} and {total_reactions} others reacted with {emoji_name}",
},
context,
);
}

export function reaction_is_notifiable(message) {
if (message.current_user_reacted) {
return false;
}

if (!message.sent_by_me) {
return false;
}

if (message.type === "private" && user_settings.enable_dm_reactions_notifications) {
return true;
}

if (
message.type === "stream" &&
user_topics.is_topic_followed(message.stream_id, message.topic) &&
user_settings.enable_followed_topics_reactions_notifications
) {
return true;
}

if (
message.type === "stream" &&
!user_topics.is_topic_unmuted(message.stream_id, message.topic) &&
user_settings.enable_unmuted_topic_reactions_notifications
) {
return true;
}

// Everything else is on the table; next filter based on notification
// settings.
return false;
}

export function received_reaction(event) {
const message_id = event.message_id;
const message = message_store.get(message_id);

if (message === undefined) {
// If we don't have the message in cache, do nothing; if we
// ever fetch it from the server, it'll come with the
// latest reactions attached
return;
}

const user_id = event.user_id;
const local_id = get_local_reaction_id(event);
const clean_reaction_object = message.clean_reactions.get(local_id);

message.current_user_reacted = user_id === page_params.user_id;
message.reaction_label = generate_notification_title(
clean_reaction_object.emoji_name,
clean_reaction_object.user_ids,
);
message.reacted_by = people.get_full_name(user_id);

if (!reaction_is_notifiable(message)) {
return;
}

if (message_notifications.should_send_desktop_notification(message)) {
message_notifications.process_notification({
message,
desktop_notify: desktop_notifications.granted_desktop_notifications_permission(),
});
}
if (message_notifications.should_send_audible_notification(message)) {
ui_util.play_audio($("#user-notification-sound-audio").get(0));
}
}
3 changes: 3 additions & 0 deletions web/src/realm_user_settings_defaults.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,9 @@ export type RealmDefaultSettings = {
enable_followed_topic_push_notifications: boolean;
enable_followed_topic_email_notifications: boolean;
enable_followed_topic_wildcard_mentions_notify: boolean;
enable_dm_reactions_notifications: boolean;
enable_followed_topics_reactions_notifications: boolean;
enable_unmuted_topic_reactions_notifications: boolean;
enter_sends: boolean;
web_escape_navigates_to_home_view: boolean;
fluid_layout_width: boolean;
Expand Down
2 changes: 2 additions & 0 deletions web/src/server_events_dispatch.js
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ import {page_params} from "./page_params";
import * as peer_data from "./peer_data";
import * as people from "./people";
import * as pm_list from "./pm_list";
import {received_reaction} from "./reaction_notifications";
import * as reactions from "./reactions";
import * as realm_icon from "./realm_icon";
import * as realm_logo from "./realm_logo";
Expand Down Expand Up @@ -183,6 +184,7 @@ export function dispatch_normal_event(event) {
switch (event.op) {
case "add":
reactions.add_reaction(event);
received_reaction(event);
break;
case "remove":
reactions.remove_reaction(event);
Expand Down
18 changes: 18 additions & 0 deletions web/src/settings_config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -558,6 +558,15 @@ export const preferences_settings_labels = {
};

export const notification_settings_labels = {
enable_dm_reactions_notifications: $t({
defaultMessage: "Notify me about reactions to my DMs",
}),
enable_followed_topics_reactions_notifications: $t({
defaultMessage: "Notify me about reactions to my message in topics I'm following",
}),
enable_unmuted_topic_reactions_notifications: $t({
defaultMessage: "Notify me about reactions to my message in topics I haven't muted",
}),
enable_online_push_notifications: $t({
defaultMessage: "Send mobile notifications even if I'm online",
}),
Expand Down Expand Up @@ -693,6 +702,12 @@ export const followed_topic_notification_settings: (keyof FollowedTopicNotificat
"enable_followed_topic_wildcard_mentions_notify",
];

const reaction_notification_settings = [
"enable_dm_reactions_notifications",
"enable_followed_topics_reactions_notifications",
"enable_unmuted_topic_reactions_notifications",
];

const desktop_notification_settings = ["pm_content_in_desktop_notifications"];

const mobile_notification_settings = ["enable_online_push_notifications"];
Expand Down Expand Up @@ -750,6 +765,7 @@ const email_notification_settings = [
];

const other_notification_settings = [
...reaction_notification_settings,
...desktop_notification_settings,
"desktop_icon_count_display",
...mobile_notification_settings,
Expand Down Expand Up @@ -815,6 +831,7 @@ export function get_notifications_table_row_data(
export type AllNotifications = {
general_settings: {label: string; notification_settings: NotificationSettingCheckbox[]}[];
settings: {
reaction_notification_settings: string[];
desktop_notification_settings: string[];
mobile_notification_settings: string[];
email_message_notification_settings: string[];
Expand Down Expand Up @@ -851,6 +868,7 @@ export const all_notifications = (settings_object: Settings): AllNotifications =
},
],
settings: {
reaction_notification_settings,
desktop_notification_settings,
mobile_notification_settings,
email_message_notification_settings,
Expand Down
3 changes: 3 additions & 0 deletions web/src/user_settings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,9 @@ export type UserSettings = (StreamNotificationSettings &
automatically_unmute_topics_in_muted_streams_policy: number;
automatically_follow_topics_where_mentioned: boolean;
timezone: string;
enable_dm_reactions_notifications: boolean;
enable_followed_topics_reactions_notifications: boolean;
enable_unmuted_topic_reactions_notifications: boolean;
};

export let user_settings: UserSettings;
Expand Down
17 changes: 17 additions & 0 deletions web/templates/settings/notification_settings.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,23 @@
prefix=prefix}}
</div>

<div
class="reaction_notifications m-10 {{#if for_realm_settings}}settings-subsection-parent{{else}}subsection-parent{{/if}}">
<div class="subsection-header inline-block">
<h3>{{t "Reaction notifications" }}</h3>
{{> settings_save_discard_widget section_name="reaction-notifications-settings" show_only_indicator=(not
for_realm_settings) }}
</div>

{{#each notification_settings.reaction_notification_settings}}
{{> settings_checkbox
setting_name=this
is_checked=(lookup ../settings_object this)
label=(lookup ../settings_label this)
prefix=../prefix}}
{{/each}}
</div>

<div class="desktop_notifications m-10 {{#if for_realm_settings}}settings-subsection-parent{{else}}subsection-parent{{/if}}">

<div class="subsection-header inline-block">
Expand Down
Loading

0 comments on commit c741c14

Please sign in to comment.