diff --git a/tools/test-js-with-node b/tools/test-js-with-node
index d785de7631..2ad2d2f5ca 100755
--- a/tools/test-js-with-node
+++ b/tools/test-js-with-node
@@ -151,7 +151,7 @@ EXEMPT_FILES = make_set(
"web/src/message_list_view.ts",
"web/src/message_lists.ts",
"web/src/message_live_update.ts",
- "web/src/message_notifications.js",
+ "web/src/message_notifications.ts",
"web/src/message_scroll.js",
"web/src/message_scroll_state.ts",
"web/src/message_util.ts",
diff --git a/web/src/desktop_notifications.ts b/web/src/desktop_notifications.ts
index 94a8a4680e..7f2ca66be8 100644
--- a/web/src/desktop_notifications.ts
+++ b/web/src/desktop_notifications.ts
@@ -21,7 +21,7 @@ export function set_notification_api(n: typeof NotificationAPI): void {
NotificationAPI = n;
}
-class ElectronBridgeNotification extends EventTarget {
+export class ElectronBridgeNotification extends EventTarget {
title: string;
dir: NotificationDirection;
lang: string;
diff --git a/web/src/message_notifications.js b/web/src/message_notifications.ts
similarity index 84%
rename from web/src/message_notifications.js
rename to web/src/message_notifications.ts
index 3cff30ee68..e35d197020 100644
--- a/web/src/message_notifications.js
+++ b/web/src/message_notifications.ts
@@ -3,8 +3,10 @@ import $ from "jquery";
import * as alert_words from "./alert_words";
import * as blueslip from "./blueslip";
import * as desktop_notifications from "./desktop_notifications";
+import type {ElectronBridgeNotification} from "./desktop_notifications";
import {$t} from "./i18n";
import * as message_parser from "./message_parser";
+import type {Message} from "./message_store";
import * as message_view from "./message_view";
import * as people from "./people";
import * as spoilers from "./spoilers";
@@ -14,7 +16,27 @@ import {user_settings} from "./user_settings";
import * as user_topics from "./user_topics";
import * as util from "./util";
-function get_notification_content(message) {
+type TestNotificationMessage = {
+ id: number;
+ type: "test-notification";
+ sender_email: string;
+ sender_full_name: string;
+ display_reply_to: string;
+ content: string;
+ unread: boolean;
+ notification_sent?: boolean;
+ // Needed for functions that handle Message
+ is_me_message?: undefined;
+ sent_by_me?: undefined;
+ mentioned_me_directly?: undefined;
+};
+
+function small_avatar_url_for_test_notification(message: TestNotificationMessage): string {
+ // this is a heavily simplified version of people.small_avatar_url
+ return people.gravatar_url_for_email(message.sender_email);
+}
+
+function get_notification_content(message: Message | TestNotificationMessage): string {
let content;
// Convert the content to plain text, replacing emoji with their alt text
const $content = $("
").html(message.content);
@@ -50,7 +72,7 @@ function get_notification_content(message) {
return content;
}
-function debug_notification_source_value(message) {
+function debug_notification_source_value(message: Message | TestNotificationMessage): void {
let notification_source;
if (message.type === "private" || message.type === "test-notification") {
@@ -66,7 +88,7 @@ function debug_notification_source_value(message) {
blueslip.debug("Desktop notification from source " + notification_source);
}
-function get_notification_key(message) {
+function get_notification_key(message: Message | TestNotificationMessage): string {
let key;
if (message.type === "private" || message.type === "test-notification") {
@@ -79,13 +101,16 @@ function get_notification_key(message) {
return key;
}
-function remove_sender_from_list_of_recipients(message) {
+function remove_sender_from_list_of_recipients(message: Message): string {
return `, ${message.display_reply_to}, `
.replace(`, ${message.sender_full_name}, `, ", ")
.slice(", ".length, -", ".length);
}
-function get_notification_title(message, msg_count) {
+function get_notification_title(
+ message: Message | TestNotificationMessage,
+ msg_count: number,
+): string {
let title_prefix = message.sender_full_name;
let title_suffix = "";
let other_recipients;
@@ -139,29 +164,36 @@ function get_notification_title(message, msg_count) {
return title_prefix + title_suffix;
}
-export function process_notification(notification) {
+export function process_notification(notification: {
+ message: Message | TestNotificationMessage;
+ desktop_notify: boolean;
+}): void {
const message = notification.message;
const content = get_notification_content(message);
const key = get_notification_key(message);
- let notification_object;
+ let notification_object: ElectronBridgeNotification | Notification;
let msg_count = 1;
debug_notification_source_value(message);
- if (desktop_notifications.notice_memory.has(key)) {
- msg_count = desktop_notifications.notice_memory.get(key).msg_count + 1;
- notification_object = desktop_notifications.notice_memory.get(key).obj;
+ const notice_memory = desktop_notifications.notice_memory.get(key);
+ if (notice_memory) {
+ msg_count = notice_memory.msg_count + 1;
+ notification_object = notice_memory.obj;
notification_object.close();
}
const title = get_notification_title(message, msg_count);
if (notification.desktop_notify && desktop_notifications.NotificationAPI !== undefined) {
- const icon_url = people.small_avatar_url(message);
+ const icon_url =
+ message.type === "test-notification"
+ ? small_avatar_url_for_test_notification(message)
+ : people.small_avatar_url(message);
notification_object = new desktop_notifications.NotificationAPI(title, {
icon: icon_url,
body: content,
- tag: message.id,
+ tag: message.id.toString(),
});
desktop_notifications.notice_memory.set(key, {
obj: notification_object,
@@ -188,7 +220,7 @@ export function process_notification(notification) {
}
}
-export function message_is_notifiable(message) {
+export function message_is_notifiable(message: Message | TestNotificationMessage): boolean {
// Independent of the user's notification settings, are there
// properties of the message that unconditionally mean we
// shouldn't notify about it.
@@ -235,7 +267,9 @@ export function message_is_notifiable(message) {
return true;
}
-export function should_send_desktop_notification(message) {
+export function should_send_desktop_notification(
+ message: Message | TestNotificationMessage,
+): boolean {
// Always notify for testing notifications.
if (message.type === "test-notification") {
return true;
@@ -304,7 +338,9 @@ export function should_send_desktop_notification(message) {
return false;
}
-export function should_send_audible_notification(message) {
+export function should_send_audible_notification(
+ message: Message | TestNotificationMessage,
+): boolean {
// If `None` is selected as the notification sound, never send
// audible notifications regardless of other configuration.
if (user_settings.notification_sound === "none") {
@@ -374,7 +410,7 @@ export function should_send_audible_notification(message) {
return false;
}
-export function received_messages(messages) {
+export function received_messages(messages: (Message | TestNotificationMessage)[]): void {
for (const message of messages) {
if (!message_is_notifiable(message)) {
continue;
@@ -393,12 +429,12 @@ export function received_messages(messages) {
});
}
if (should_send_audible_notification(message)) {
- ui_util.play_audio($("#user-notification-sound-audio")[0]);
+ void ui_util.play_audio(util.the($("#user-notification-sound-audio")));
}
}
}
-export function send_test_notification(content) {
+export function send_test_notification(content: string): void {
received_messages([
{
id: Math.random(),
diff --git a/web/src/message_store.ts b/web/src/message_store.ts
index b1a38f5150..7fc4a214cf 100644
--- a/web/src/message_store.ts
+++ b/web/src/message_store.ts
@@ -173,6 +173,8 @@ export type Message = (
status_emoji_info?: UserStatusEmojiInfo | undefined; // Used in `message_body.hbs`
local_edit_timestamp?: number; // Used for edited messages
+
+ notification_sent?: boolean; // Used in message_notifications
} & (
| {
type: "private";
diff --git a/web/src/people.ts b/web/src/people.ts
index 3bc6e66102..851b5a774a 100644
--- a/web/src/people.ts
+++ b/web/src/people.ts
@@ -800,7 +800,7 @@ export function user_can_direct_message(recipient_ids_string: string): boolean {
return !other_human_recipients_exist;
}
-function gravatar_url_for_email(email: string): string {
+export function gravatar_url_for_email(email: string): string {
const hash = md5(email.toLowerCase());
return "https://secure.gravatar.com/avatar/" + hash + "?d=identicon";
}
diff --git a/web/tests/notifications.test.js b/web/tests/notifications.test.js
index 27c853c411..54ee383336 100644
--- a/web/tests/notifications.test.js
+++ b/web/tests/notifications.test.js
@@ -402,14 +402,14 @@ test("basic_notifications", () => {
n = desktop_notifications.get_notifications();
assert.equal(n.has("Jesse Pinkman to general > whatever"), true);
assert.equal(n.size, 1);
- assert.equal(last_shown_message_id, message_1.id);
+ assert.equal(last_shown_message_id, message_1.id.toString());
// Remove notification.
desktop_notifications.close_notification(message_1);
n = desktop_notifications.get_notifications();
assert.equal(n.has("Jesse Pinkman to general > whatever"), false);
assert.equal(n.size, 0);
- assert.equal(last_closed_message_id, message_1.id);
+ assert.equal(last_closed_message_id, message_1.id.toString());
// Send notification.
message_1.id = 1001;
@@ -417,7 +417,7 @@ test("basic_notifications", () => {
n = desktop_notifications.get_notifications();
assert.equal(n.has("Jesse Pinkman to general > whatever"), true);
assert.equal(n.size, 1);
- assert.equal(last_shown_message_id, message_1.id);
+ assert.equal(last_shown_message_id, message_1.id.toString());
// Process same message again. Notification count shouldn't increase.
message_1.id = 1002;
@@ -425,7 +425,7 @@ test("basic_notifications", () => {
n = desktop_notifications.get_notifications();
assert.equal(n.has("Jesse Pinkman to general > whatever"), true);
assert.equal(n.size, 1);
- assert.equal(last_shown_message_id, message_1.id);
+ assert.equal(last_shown_message_id, message_1.id.toString());
// Send another message. Notification count should increase.
message_notifications.process_notification({message: message_2, desktop_notify: true});
@@ -433,7 +433,7 @@ test("basic_notifications", () => {
assert.equal(n.has("Gus Fring to general > lunch"), true);
assert.equal(n.has("Jesse Pinkman to general > whatever"), true);
assert.equal(n.size, 2);
- assert.equal(last_shown_message_id, message_2.id);
+ assert.equal(last_shown_message_id, message_2.id.toString());
// Remove notifications.
desktop_notifications.close_notification(message_1);
@@ -441,5 +441,5 @@ test("basic_notifications", () => {
n = desktop_notifications.get_notifications();
assert.equal(n.has("Jesse Pinkman to general > whatever"), false);
assert.equal(n.size, 0);
- assert.equal(last_closed_message_id, message_2.id);
+ assert.equal(last_closed_message_id, message_2.id.toString());
});