2012-11-23 23:53:38 +01:00
|
|
|
var notifications = (function () {
|
|
|
|
|
|
|
|
var exports = {};
|
|
|
|
|
2012-11-26 23:57:31 +01:00
|
|
|
var notice_memory = {};
|
2013-09-16 18:22:52 +02:00
|
|
|
|
|
|
|
// When you start Zulip, window_has_focus should be true, but it might not be the
|
|
|
|
// case after a server-initiated reload.
|
|
|
|
var window_has_focus = document.hasFocus && document.hasFocus();
|
|
|
|
|
2013-05-03 21:36:38 +02:00
|
|
|
var supports_sound;
|
2012-11-23 23:53:38 +01:00
|
|
|
|
2013-06-28 20:51:28 +02:00
|
|
|
var unread_pms_favicon = '/static/images/favicon/favicon-pms.png';
|
|
|
|
var current_favicon;
|
|
|
|
var previous_favicon;
|
|
|
|
var flashing = false;
|
|
|
|
|
2014-05-29 05:00:45 +02:00
|
|
|
var notifications_api;
|
2018-04-04 12:55:34 +02:00
|
|
|
|
|
|
|
exports.set_notification_api = function (n) {
|
|
|
|
notifications_api = n;
|
|
|
|
};
|
|
|
|
|
2014-05-29 05:00:45 +02:00
|
|
|
if (window.webkitNotifications) {
|
|
|
|
notifications_api = window.webkitNotifications;
|
|
|
|
} else if (window.Notification) {
|
|
|
|
// Build a shim to the new notification API
|
|
|
|
notifications_api = {
|
|
|
|
checkPermission: function checkPermission() {
|
|
|
|
if (window.Notification.permission === 'granted') {
|
|
|
|
return 0;
|
|
|
|
}
|
2016-12-02 21:34:35 +01:00
|
|
|
return 2;
|
2014-05-29 05:00:45 +02:00
|
|
|
},
|
|
|
|
requestPermission: window.Notification.requestPermission,
|
2017-01-02 21:49:39 +01:00
|
|
|
createNotification: function createNotification(icon, title, content, tag) {
|
|
|
|
var notification_object = new window.Notification(title, {icon: icon,
|
|
|
|
body: content,
|
|
|
|
tag: tag});
|
2014-05-29 05:00:45 +02:00
|
|
|
notification_object.show = function () {};
|
|
|
|
notification_object.cancel = function () { notification_object.close(); };
|
|
|
|
return notification_object;
|
2017-01-12 00:17:43 +01:00
|
|
|
},
|
2014-05-29 05:00:45 +02:00
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2016-12-05 07:02:18 +01:00
|
|
|
function browser_desktop_notifications_on() {
|
2018-06-06 18:19:09 +02:00
|
|
|
return notifications_api &&
|
2013-01-21 21:50:33 +01:00
|
|
|
// Firefox on Ubuntu claims to do webkitNotifications but its notifications are terrible
|
2016-08-24 21:52:09 +02:00
|
|
|
/webkit/i.test(navigator.userAgent) &&
|
2012-11-26 23:57:31 +01:00
|
|
|
// 0 is PERMISSION_ALLOWED
|
2018-06-06 18:19:09 +02:00
|
|
|
notifications_api.checkPermission() === 0;
|
2012-11-26 23:57:31 +01:00
|
|
|
}
|
2012-11-23 23:53:38 +01:00
|
|
|
|
2016-12-05 07:02:18 +01:00
|
|
|
function cancel_notification_object(notification_object) {
|
2018-05-06 21:43:17 +02:00
|
|
|
// We must remove the .onclose so that it does not trigger on .cancel
|
|
|
|
notification_object.onclose = function () {};
|
|
|
|
notification_object.onclick = function () {};
|
|
|
|
notification_object.cancel();
|
2013-08-29 22:58:00 +02:00
|
|
|
}
|
|
|
|
|
2018-04-04 12:55:34 +02:00
|
|
|
exports.get_notifications = function () {
|
|
|
|
return notice_memory;
|
|
|
|
};
|
|
|
|
|
2018-01-11 21:36:11 +01:00
|
|
|
function get_audio_file_path(audio_element, audio_file_without_extension) {
|
|
|
|
if (audio_element.canPlayType('audio/ogg; codecs="vorbis"')) {
|
|
|
|
return audio_file_without_extension + ".ogg";
|
|
|
|
}
|
|
|
|
|
|
|
|
return audio_file_without_extension + ".mp3";
|
|
|
|
}
|
|
|
|
|
2012-11-26 23:57:31 +01:00
|
|
|
exports.initialize = function () {
|
2012-11-23 23:53:38 +01:00
|
|
|
$(window).focus(function () {
|
|
|
|
window_has_focus = true;
|
2012-11-27 18:26:36 +01:00
|
|
|
|
2013-07-30 00:35:44 +02:00
|
|
|
_.each(notice_memory, function (notice_mem_entry) {
|
2018-05-06 21:43:17 +02:00
|
|
|
cancel_notification_object(notice_mem_entry.obj);
|
2012-11-27 18:26:36 +01:00
|
|
|
});
|
2013-08-29 22:58:00 +02:00
|
|
|
notice_memory = {};
|
2013-07-11 17:14:11 +02:00
|
|
|
|
|
|
|
// Update many places on the DOM to reflect unread
|
|
|
|
// counts.
|
2017-03-18 01:41:56 +01:00
|
|
|
unread_ops.process_visible();
|
2013-07-11 17:14:11 +02:00
|
|
|
|
2012-11-23 23:53:38 +01:00
|
|
|
}).blur(function () {
|
|
|
|
window_has_focus = false;
|
|
|
|
});
|
|
|
|
|
2013-05-03 21:36:38 +02:00
|
|
|
var audio = $("<audio>");
|
2013-07-05 20:47:41 +02:00
|
|
|
if (audio[0].canPlayType === undefined) {
|
2013-05-03 21:36:38 +02:00
|
|
|
supports_sound = false;
|
|
|
|
} else {
|
|
|
|
supports_sound = true;
|
2018-01-11 21:36:11 +01:00
|
|
|
|
2013-05-03 21:36:38 +02:00
|
|
|
$("#notifications-area").append(audio);
|
2018-01-11 21:36:11 +01:00
|
|
|
audio.append($("<source>").attr("loop", "yes"));
|
|
|
|
var source = $("#notifications-area audio source");
|
|
|
|
|
2013-05-03 21:36:38 +02:00
|
|
|
if (audio[0].canPlayType('audio/ogg; codecs="vorbis"')) {
|
2018-01-11 21:36:11 +01:00
|
|
|
source.attr("type", "audio/ogg");
|
2013-05-03 21:36:38 +02:00
|
|
|
} else {
|
2018-01-11 21:36:11 +01:00
|
|
|
source.attr("type", "audio/mpeg");
|
2013-05-03 21:36:38 +02:00
|
|
|
}
|
2018-01-11 21:36:11 +01:00
|
|
|
|
|
|
|
var audio_file_without_extension
|
|
|
|
= "/static/audio/notification_sounds/" + page_params.notification_sound;
|
|
|
|
source.attr("src", get_audio_file_path(audio[0], audio_file_without_extension));
|
2013-05-03 21:36:38 +02:00
|
|
|
}
|
2012-11-23 23:53:38 +01:00
|
|
|
};
|
|
|
|
|
2018-01-11 21:36:11 +01:00
|
|
|
function update_notification_sound_source() {
|
|
|
|
// Simplified version of the source creation in `exports.initialize`, for
|
|
|
|
// updating the source instead of creating it for the first time.
|
|
|
|
var audio = $("#notifications-area audio");
|
|
|
|
var source = $("#notifications-area audio source");
|
|
|
|
var audio_file_without_extension
|
|
|
|
= "/static/audio/notification_sounds/" + page_params.notification_sound;
|
|
|
|
source.attr("src", get_audio_file_path(audio[0], audio_file_without_extension));
|
|
|
|
|
|
|
|
// Load it so that it is ready to be played; without this the old sound
|
|
|
|
// is played.
|
|
|
|
$("#notifications-area").find("audio")[0].load();
|
|
|
|
}
|
|
|
|
|
2017-10-19 00:53:26 +02:00
|
|
|
exports.permission_state = function () {
|
2017-10-23 20:32:55 +02:00
|
|
|
if (window.Notification === undefined) {
|
|
|
|
// act like notifications are blocked if they do not have access to
|
|
|
|
// the notification API.
|
|
|
|
return "denied";
|
|
|
|
}
|
2017-10-19 00:53:26 +02:00
|
|
|
return window.Notification.permission;
|
|
|
|
};
|
|
|
|
|
2016-01-09 23:45:38 +01:00
|
|
|
var new_message_count = 0;
|
2014-02-04 00:20:42 +01:00
|
|
|
|
|
|
|
exports.update_title_count = function (count) {
|
|
|
|
new_message_count = count;
|
|
|
|
exports.redraw_title();
|
|
|
|
};
|
|
|
|
|
|
|
|
exports.redraw_title = function () {
|
2013-03-19 21:53:49 +01:00
|
|
|
// Update window title and favicon to reflect unread messages in current view
|
|
|
|
var n;
|
|
|
|
|
2018-06-06 18:19:09 +02:00
|
|
|
var new_title = (new_message_count ? "(" + new_message_count + ") " : "")
|
2015-12-10 20:37:01 +01:00
|
|
|
+ narrow.narrow_title + " - "
|
|
|
|
+ page_params.realm_name + " - "
|
2017-02-21 20:44:43 +01:00
|
|
|
+ "Zulip";
|
2013-03-19 21:53:49 +01:00
|
|
|
|
2013-05-06 22:35:10 +02:00
|
|
|
if (document.title === new_title) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
document.title = new_title;
|
|
|
|
|
2013-03-19 21:53:49 +01:00
|
|
|
// IE doesn't support PNG favicons, *shrug*
|
2016-08-24 21:52:09 +02:00
|
|
|
if (!/msie/i.test(navigator.userAgent)) {
|
2013-03-19 21:53:49 +01:00
|
|
|
// Indicate the message count in the favicon
|
|
|
|
if (new_message_count) {
|
|
|
|
// Make sure we're working with a number, as a defensive programming
|
|
|
|
// measure. And we don't have images above 99, so display those as
|
|
|
|
// 'infinite'.
|
2018-06-06 18:19:09 +02:00
|
|
|
n = +new_message_count;
|
2013-08-01 17:47:48 +02:00
|
|
|
if (n > 99) {
|
2013-03-19 21:53:49 +01:00
|
|
|
n = 'infinite';
|
2013-08-01 17:47:48 +02:00
|
|
|
}
|
2013-03-19 21:53:49 +01:00
|
|
|
|
2018-06-04 21:13:07 +02:00
|
|
|
current_favicon = previous_favicon = '/static/images/favicon/favicon-' + n + '.png';
|
2013-03-19 21:53:49 +01:00
|
|
|
} else {
|
2013-06-28 20:51:28 +02:00
|
|
|
current_favicon = previous_favicon = '/static/favicon.ico?v=2';
|
2013-03-19 21:53:49 +01:00
|
|
|
}
|
2014-03-13 17:44:43 +01:00
|
|
|
favicon.set(current_favicon);
|
2013-03-19 21:53:49 +01:00
|
|
|
}
|
2013-06-02 20:42:03 +02:00
|
|
|
|
2018-02-25 19:51:52 +01:00
|
|
|
// Notify the current desktop app's UI about the new unread count.
|
|
|
|
if (window.electron_bridge !== undefined) {
|
|
|
|
window.electron_bridge.send_event('total_unread_count', new_message_count);
|
|
|
|
}
|
2013-03-19 21:53:49 +01:00
|
|
|
};
|
|
|
|
|
2018-10-01 20:27:22 +02:00
|
|
|
exports.show_history_limit_message = function () {
|
|
|
|
$(".top-messages-logo").hide();
|
|
|
|
$(".history-limited-box").show();
|
|
|
|
narrow.hide_empty_narrow_message();
|
|
|
|
};
|
|
|
|
|
|
|
|
exports.hide_history_limit_message = function () {
|
|
|
|
$(".top-messages-logo").show();
|
|
|
|
$(".history-limited-box").hide();
|
|
|
|
};
|
|
|
|
|
|
|
|
exports.hide_or_show_history_limit_message = function (msg_list) {
|
|
|
|
if (msg_list !== current_msg_list) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (msg_list.fetch_status.history_limited()) {
|
|
|
|
notifications.show_history_limit_message();
|
|
|
|
} else {
|
|
|
|
notifications.hide_history_limit_message();
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2013-06-28 20:51:28 +02:00
|
|
|
function flash_pms() {
|
|
|
|
// When you have unread PMs, toggle the favicon between the unread count and
|
|
|
|
// a special icon indicating that you have unread PMs.
|
|
|
|
if (unread.get_counts().private_message_count > 0) {
|
|
|
|
if (current_favicon === unread_pms_favicon) {
|
2014-03-13 17:44:43 +01:00
|
|
|
favicon.set(previous_favicon);
|
2013-06-28 20:51:28 +02:00
|
|
|
current_favicon = previous_favicon;
|
|
|
|
previous_favicon = unread_pms_favicon;
|
|
|
|
} else {
|
2014-03-13 17:44:43 +01:00
|
|
|
favicon.set(unread_pms_favicon);
|
2013-06-28 20:51:28 +02:00
|
|
|
previous_favicon = current_favicon;
|
|
|
|
current_favicon = unread_pms_favicon;
|
|
|
|
}
|
|
|
|
// Toggle every 2 seconds.
|
|
|
|
setTimeout(flash_pms, 2000);
|
|
|
|
} else {
|
|
|
|
flashing = false;
|
|
|
|
// You have no more unread PMs, so back to only showing the unread
|
|
|
|
// count.
|
2014-03-13 17:44:43 +01:00
|
|
|
favicon.set(current_favicon);
|
2013-06-28 20:51:28 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-05-16 00:50:52 +02:00
|
|
|
exports.update_pm_count = function () {
|
|
|
|
// TODO: Add a `window.electron_bridge.updatePMCount(new_pm_count);` call?
|
2013-06-28 20:51:28 +02:00
|
|
|
if (!flashing) {
|
|
|
|
flashing = true;
|
|
|
|
flash_pms();
|
|
|
|
}
|
2013-06-19 00:00:40 +02:00
|
|
|
};
|
|
|
|
|
2013-03-04 23:44:07 +01:00
|
|
|
exports.window_has_focus = function () {
|
|
|
|
return window_has_focus;
|
|
|
|
};
|
|
|
|
|
2016-12-23 15:33:08 +01:00
|
|
|
function in_browser_notify(message, title, content, raw_operators, opts) {
|
2017-01-03 01:40:18 +01:00
|
|
|
var notification_html = $(templates.render('notification', {
|
2017-01-21 20:29:39 +01:00
|
|
|
gravatar_url: people.small_avatar_url(message),
|
2017-01-03 01:40:18 +01:00
|
|
|
title: title,
|
|
|
|
content: content,
|
2017-01-03 01:42:04 +01:00
|
|
|
message_id: message.id,
|
2017-01-03 01:40:18 +01:00
|
|
|
}));
|
|
|
|
|
2016-12-28 03:41:20 +01:00
|
|
|
$(".top-right").notify({
|
|
|
|
message: {
|
2017-01-12 00:17:43 +01:00
|
|
|
html: notification_html,
|
2016-12-28 03:41:20 +01:00
|
|
|
},
|
|
|
|
fadeOut: {
|
|
|
|
enabled: true,
|
2017-01-12 00:17:43 +01:00
|
|
|
delay: 4000,
|
|
|
|
},
|
2013-06-27 22:15:00 +02:00
|
|
|
}).show();
|
2016-12-28 03:41:20 +01:00
|
|
|
|
2017-01-11 09:47:00 +01:00
|
|
|
$(".notification[data-message-id='" + message.id + "']").expectOne().data("narrow", {
|
2016-12-28 03:41:20 +01:00
|
|
|
raw_operators: raw_operators,
|
2017-01-12 00:17:43 +01:00
|
|
|
opts_notif: opts,
|
2016-12-23 15:33:08 +01:00
|
|
|
});
|
2013-06-27 22:15:00 +02:00
|
|
|
}
|
|
|
|
|
2013-11-13 19:40:02 +01:00
|
|
|
exports.notify_above_composebox = function (note, link_class, link_msg_id, link_text) {
|
|
|
|
var notification_html = $(templates.render('compose_notification', {note: note,
|
2013-10-09 22:42:15 +02:00
|
|
|
link_class: link_class,
|
|
|
|
link_msg_id: link_msg_id,
|
|
|
|
link_text: link_text}));
|
2013-11-13 19:40:02 +01:00
|
|
|
exports.clear_compose_notifications();
|
|
|
|
$('#out-of-view-notification').append(notification_html);
|
|
|
|
$('#out-of-view-notification').show();
|
2013-10-09 22:42:15 +02:00
|
|
|
};
|
|
|
|
|
2013-06-19 01:41:27 +02:00
|
|
|
function process_notification(notification) {
|
2016-12-02 17:09:31 +01:00
|
|
|
var i;
|
|
|
|
var notification_object;
|
|
|
|
var key;
|
|
|
|
var content;
|
|
|
|
var other_recipients;
|
2013-06-19 01:41:27 +02:00
|
|
|
var message = notification.message;
|
2012-11-23 23:53:38 +01:00
|
|
|
var title = message.sender_full_name;
|
|
|
|
var msg_count = 1;
|
2014-02-05 17:33:07 +01:00
|
|
|
var notification_source;
|
2016-12-23 15:33:08 +01:00
|
|
|
var raw_operators = [];
|
2018-04-23 06:02:11 +02:00
|
|
|
var opts = {trigger: "notification click"};
|
2013-07-22 22:21:34 +02:00
|
|
|
// Convert the content to plain text, replacing emoji with their alt text
|
|
|
|
content = $('<div/>').html(message.content);
|
2013-07-23 00:25:25 +02:00
|
|
|
ui.replace_emoji_with_text(content);
|
2013-07-22 22:21:34 +02:00
|
|
|
content = content.text();
|
|
|
|
|
2018-12-22 23:15:04 +01:00
|
|
|
var topic = util.get_message_topic(message);
|
|
|
|
|
2014-03-10 16:26:39 +01:00
|
|
|
if (message.is_me_message) {
|
|
|
|
content = message.sender_full_name + content.slice(3);
|
|
|
|
}
|
|
|
|
|
2013-01-09 23:49:54 +01:00
|
|
|
if (message.type === "private") {
|
2016-12-07 17:29:12 +01:00
|
|
|
if (page_params.pm_content_in_desktop_notifications !== undefined
|
|
|
|
&& !page_params.pm_content_in_desktop_notifications) {
|
|
|
|
content = "New private message from " + message.sender_full_name;
|
|
|
|
}
|
2013-01-09 23:49:54 +01:00
|
|
|
key = message.display_reply_to;
|
|
|
|
other_recipients = message.display_reply_to;
|
|
|
|
// Remove the sender from the list of other recipients
|
|
|
|
other_recipients = other_recipients.replace(", " + message.sender_full_name, "");
|
|
|
|
other_recipients = other_recipients.replace(message.sender_full_name + ", ", "");
|
2014-02-05 17:33:07 +01:00
|
|
|
notification_source = 'pm';
|
2013-01-09 23:49:54 +01:00
|
|
|
} else {
|
|
|
|
key = message.sender_full_name + " to " +
|
2018-12-22 23:15:04 +01:00
|
|
|
message.stream + " > " + topic;
|
2014-02-05 17:33:07 +01:00
|
|
|
if (message.mentioned) {
|
|
|
|
notification_source = 'mention';
|
|
|
|
} else if (message.alerted) {
|
|
|
|
notification_source = 'alert';
|
|
|
|
} else {
|
|
|
|
notification_source = 'stream';
|
|
|
|
}
|
2013-01-09 23:49:54 +01:00
|
|
|
}
|
2018-05-16 00:50:52 +02:00
|
|
|
blueslip.debug("Desktop notification from source " + notification_source);
|
2012-11-23 23:53:38 +01:00
|
|
|
|
|
|
|
if (content.length > 150) {
|
|
|
|
// Truncate content at a word boundary
|
2016-11-30 19:05:04 +01:00
|
|
|
for (i = 150; i > 0; i -= 1) {
|
2012-11-23 23:53:38 +01:00
|
|
|
if (content[i] === ' ') {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
content = content.substring(0, i);
|
|
|
|
content += " [...]";
|
|
|
|
}
|
|
|
|
|
2018-05-16 00:55:28 +02:00
|
|
|
if (notice_memory[key] !== undefined) {
|
2012-11-23 23:53:38 +01:00
|
|
|
msg_count = notice_memory[key].msg_count + 1;
|
|
|
|
title = msg_count + " messages from " + title;
|
|
|
|
notification_object = notice_memory[key].obj;
|
2013-08-29 22:58:00 +02:00
|
|
|
cancel_notification_object(notification_object);
|
2012-11-23 23:53:38 +01:00
|
|
|
}
|
|
|
|
|
2017-01-03 01:44:49 +01:00
|
|
|
if (message.type === "private") {
|
|
|
|
if (message.display_recipient.length > 2) {
|
|
|
|
// If the message has too many recipients to list them all...
|
|
|
|
if (content.length + title.length + other_recipients.length > 230) {
|
|
|
|
// Then count how many people are in the conversation and summarize
|
|
|
|
// by saying the conversation is with "you and [number] other people"
|
|
|
|
other_recipients = other_recipients.replace(/[^,]/g, "").length +
|
|
|
|
" other people";
|
|
|
|
}
|
|
|
|
|
|
|
|
title += " (to you and " + other_recipients + ")";
|
|
|
|
} else {
|
|
|
|
title += " (to you)";
|
2012-11-23 23:53:38 +01:00
|
|
|
}
|
2017-01-03 01:44:49 +01:00
|
|
|
|
2016-12-23 15:33:08 +01:00
|
|
|
raw_operators = [{operand: message.reply_to, operator: "pm-with"}];
|
2012-11-23 23:53:38 +01:00
|
|
|
}
|
2017-01-03 01:44:49 +01:00
|
|
|
|
2013-01-09 23:49:54 +01:00
|
|
|
if (message.type === "stream") {
|
2018-12-22 23:15:04 +01:00
|
|
|
title += " (to " + message.stream + " > " + topic + ")";
|
2018-05-06 21:43:17 +02:00
|
|
|
raw_operators = [
|
|
|
|
{operator: "stream", operand: message.stream},
|
2018-12-22 23:15:04 +01:00
|
|
|
{operator: "topic", operand: topic},
|
2018-05-06 21:43:17 +02:00
|
|
|
];
|
2013-01-09 23:49:54 +01:00
|
|
|
}
|
2012-11-23 23:53:38 +01:00
|
|
|
|
2018-05-16 00:55:28 +02:00
|
|
|
if (notification.webkit_notify === true) {
|
2017-01-21 20:29:39 +01:00
|
|
|
var icon_url = people.small_avatar_url(message);
|
2013-05-31 22:25:39 +02:00
|
|
|
notice_memory[key] = {
|
2018-05-06 21:43:17 +02:00
|
|
|
obj: notifications_api.createNotification(icon_url, title, content, message.id),
|
2013-08-29 22:58:00 +02:00
|
|
|
msg_count: msg_count,
|
2017-01-12 00:17:43 +01:00
|
|
|
message_id: message.id,
|
2013-05-31 22:25:39 +02:00
|
|
|
};
|
|
|
|
notification_object = notice_memory[key].obj;
|
|
|
|
notification_object.onclick = function () {
|
|
|
|
notification_object.cancel();
|
2013-11-22 19:49:48 +01:00
|
|
|
if (feature_flags.clicking_notification_causes_narrow) {
|
2018-11-13 16:13:41 +01:00
|
|
|
narrow.by_topic(message.id, {trigger: 'notification'});
|
2013-11-22 19:49:48 +01:00
|
|
|
}
|
2013-05-31 22:25:39 +02:00
|
|
|
window.focus();
|
|
|
|
};
|
|
|
|
notification_object.onclose = function () {
|
|
|
|
delete notice_memory[key];
|
|
|
|
};
|
|
|
|
notification_object.show();
|
2016-08-24 21:52:09 +02:00
|
|
|
} else if (notification.webkit_notify === false && typeof Notification !== "undefined" && /mozilla/i.test(navigator.userAgent) === true) {
|
2013-06-27 22:15:00 +02:00
|
|
|
Notification.requestPermission(function (perm) {
|
|
|
|
if (perm === 'granted') {
|
2015-10-01 00:23:18 +02:00
|
|
|
notification_object = new Notification(title, {
|
2013-06-27 22:15:00 +02:00
|
|
|
body: content,
|
2017-01-21 20:29:39 +01:00
|
|
|
iconUrl: people.small_avatar_url(message),
|
2017-01-12 00:17:43 +01:00
|
|
|
tag: message.id,
|
2013-06-27 22:15:00 +02:00
|
|
|
});
|
2017-06-04 22:46:05 +02:00
|
|
|
notification_object.onclick = function () {
|
|
|
|
// We don't need to bring the browser window into focus explicitly
|
|
|
|
// by calling `window.focus()` as well as don't need to clear the
|
|
|
|
// notification since it is the default behavior in Firefox.
|
|
|
|
if (feature_flags.clicking_notification_causes_narrow) {
|
2018-11-13 16:13:41 +01:00
|
|
|
narrow.by_topic(message.id, {trigger: 'notification'});
|
2017-06-04 22:46:05 +02:00
|
|
|
}
|
|
|
|
};
|
2013-06-27 22:15:00 +02:00
|
|
|
} else {
|
2016-12-23 15:33:08 +01:00
|
|
|
in_browser_notify(message, title, content, raw_operators, opts);
|
2013-06-27 22:15:00 +02:00
|
|
|
}
|
|
|
|
});
|
|
|
|
} else if (notification.webkit_notify === false) {
|
2016-12-23 15:33:08 +01:00
|
|
|
in_browser_notify(message, title, content, raw_operators, opts);
|
2013-05-31 22:25:39 +02:00
|
|
|
}
|
2012-11-23 23:53:38 +01:00
|
|
|
}
|
|
|
|
|
2018-04-04 12:55:34 +02:00
|
|
|
exports.process_notification = process_notification;
|
|
|
|
|
2013-08-29 22:58:00 +02:00
|
|
|
exports.close_notification = function (message) {
|
|
|
|
_.each(Object.keys(notice_memory), function (key) {
|
2018-05-06 21:43:17 +02:00
|
|
|
if (notice_memory[key].message_id === message.id) {
|
|
|
|
cancel_notification_object(notice_memory[key].obj);
|
|
|
|
delete notice_memory[key];
|
|
|
|
}
|
2013-08-29 22:58:00 +02:00
|
|
|
});
|
|
|
|
};
|
|
|
|
|
2018-02-12 22:15:57 +01:00
|
|
|
exports.message_is_notifiable = function (message) {
|
2014-02-05 22:56:30 +01:00
|
|
|
// Independent of the user's notification settings, are there
|
|
|
|
// properties of the message that unconditionally mean we
|
|
|
|
// shouldn't notify about it.
|
2013-10-18 19:28:29 +02:00
|
|
|
|
|
|
|
if (message.sent_by_me) {
|
|
|
|
return false;
|
|
|
|
}
|
2014-01-08 22:02:02 +01:00
|
|
|
|
|
|
|
// If a message is edited multiple times, we want to err on the side of
|
|
|
|
// not spamming notifications.
|
|
|
|
if (message.notification_sent) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2017-08-25 08:56:10 +02:00
|
|
|
// @-<username> mentions take precedence over muted-ness. Note
|
|
|
|
// that @all mentions are still suppressed by muting.
|
|
|
|
if (message.mentioned_me_directly) {
|
2013-12-09 23:13:33 +01:00
|
|
|
return true;
|
|
|
|
}
|
2017-08-25 08:56:10 +02:00
|
|
|
|
|
|
|
// Messages to muted streams that don't mention us specifically
|
|
|
|
// are not notifiable.
|
2018-06-06 18:50:09 +02:00
|
|
|
if (message.type === "stream" &&
|
2017-05-13 20:54:53 +02:00
|
|
|
!stream_data.in_home_view(message.stream_id)) {
|
2013-10-18 19:47:16 +02:00
|
|
|
return false;
|
|
|
|
}
|
2017-08-25 08:56:10 +02:00
|
|
|
|
2018-06-06 18:50:09 +02:00
|
|
|
if (message.type === "stream" &&
|
2018-12-22 23:15:04 +01:00
|
|
|
muting.is_topic_muted(message.stream_id, util.get_message_topic(message))) {
|
2013-10-18 19:47:16 +02:00
|
|
|
return false;
|
|
|
|
}
|
2013-10-18 19:28:29 +02:00
|
|
|
|
2014-02-05 22:56:30 +01:00
|
|
|
// Everything else is on the table; next filter based on notification
|
|
|
|
// settings.
|
|
|
|
return true;
|
2018-02-12 22:15:57 +01:00
|
|
|
};
|
2014-02-05 22:56:30 +01:00
|
|
|
|
|
|
|
function should_send_desktop_notification(message) {
|
|
|
|
// For streams, send if desktop notifications are enabled for this
|
|
|
|
// stream.
|
2018-06-06 18:50:09 +02:00
|
|
|
if (message.type === "stream" &&
|
2016-10-17 15:53:06 +02:00
|
|
|
stream_data.receives_desktop_notifications(message.stream)) {
|
2014-02-05 22:56:30 +01:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
// For PMs and @-mentions, send if desktop notifications are
|
|
|
|
// enabled.
|
2018-06-06 18:50:09 +02:00
|
|
|
if (message.type === "private" &&
|
2017-04-29 08:13:47 +02:00
|
|
|
page_params.enable_desktop_notifications) {
|
2014-02-05 22:56:30 +01:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
// For alert words and @-mentions, send if desktop notifications
|
|
|
|
// are enabled.
|
|
|
|
if (alert_words.notifies(message) &&
|
2017-04-29 08:13:47 +02:00
|
|
|
page_params.enable_desktop_notifications) {
|
2014-02-05 22:56:30 +01:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2017-08-25 08:56:10 +02:00
|
|
|
if (message.mentioned &&
|
2017-04-29 08:13:47 +02:00
|
|
|
page_params.enable_desktop_notifications) {
|
2013-10-18 19:28:29 +02:00
|
|
|
return true;
|
|
|
|
}
|
2014-02-05 22:56:30 +01:00
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
function should_send_audible_notification(message) {
|
|
|
|
// For streams, ding if sounds are enabled for this stream.
|
2018-06-06 18:50:09 +02:00
|
|
|
if (message.type === "stream" &&
|
2016-10-17 15:53:06 +02:00
|
|
|
stream_data.receives_audible_notifications(message.stream)) {
|
2014-02-05 22:56:30 +01:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
// For PMs and @-mentions, ding if sounds are enabled.
|
2018-06-06 18:50:09 +02:00
|
|
|
if (message.type === "private" && page_params.enable_sounds) {
|
2014-02-05 22:56:30 +01:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
// For alert words and @-mentions, ding if sounds are enabled.
|
2017-04-29 06:53:28 +02:00
|
|
|
if (alert_words.notifies(message) && page_params.enable_sounds) {
|
2013-10-18 19:28:29 +02:00
|
|
|
return true;
|
|
|
|
}
|
2014-02-05 22:56:30 +01:00
|
|
|
|
2017-08-25 08:56:10 +02:00
|
|
|
if (message.mentioned && page_params.enable_sounds) {
|
2013-10-18 19:28:29 +02:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
2013-05-03 21:49:01 +02:00
|
|
|
}
|
|
|
|
|
2017-09-23 11:27:18 +02:00
|
|
|
exports.granted_desktop_notifications_permission = function () {
|
2018-06-06 18:19:09 +02:00
|
|
|
return notifications_api &&
|
2017-10-19 20:29:43 +02:00
|
|
|
// 0 is PERMISSION_ALLOWED
|
2018-06-06 18:19:09 +02:00
|
|
|
notifications_api.checkPermission() === 0;
|
2017-09-23 11:27:18 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
exports.request_desktop_notifications_permission = function () {
|
|
|
|
if (notifications_api) {
|
|
|
|
return notifications_api.requestPermission();
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2012-11-23 23:53:38 +01:00
|
|
|
exports.received_messages = function (messages) {
|
2013-07-30 00:35:44 +02:00
|
|
|
_.each(messages, function (message) {
|
2018-02-12 22:15:57 +01:00
|
|
|
if (!exports.message_is_notifiable(message)) {
|
2013-08-01 17:47:48 +02:00
|
|
|
return;
|
|
|
|
}
|
2017-12-27 13:17:58 +01:00
|
|
|
if (!unread.message_unread(message)) {
|
2018-01-31 01:17:16 +01:00
|
|
|
// The message is already read; Zulip is currently in focus.
|
2013-08-01 17:47:48 +02:00
|
|
|
return;
|
|
|
|
}
|
2013-07-02 20:33:00 +02:00
|
|
|
|
2014-01-08 22:02:02 +01:00
|
|
|
message.notification_sent = true;
|
|
|
|
|
2014-03-03 22:59:15 +01:00
|
|
|
if (should_send_desktop_notification(message)) {
|
|
|
|
if (browser_desktop_notifications_on()) {
|
|
|
|
process_notification({message: message, webkit_notify: true});
|
|
|
|
} else {
|
|
|
|
process_notification({message: message, webkit_notify: false});
|
|
|
|
}
|
2013-06-14 16:46:37 +02:00
|
|
|
}
|
2014-02-05 22:56:30 +01:00
|
|
|
if (should_send_audible_notification(message) && supports_sound) {
|
2018-05-16 00:50:52 +02:00
|
|
|
$("#notifications-area").find("audio")[0].play();
|
2012-11-23 23:53:38 +01:00
|
|
|
}
|
|
|
|
});
|
|
|
|
};
|
|
|
|
|
2013-11-13 19:40:02 +01:00
|
|
|
function get_message_header(message) {
|
|
|
|
if (message.type === "stream") {
|
2018-12-22 23:15:04 +01:00
|
|
|
return message.stream + " > " + util.get_message_topic(message);
|
2013-11-13 19:40:02 +01:00
|
|
|
}
|
|
|
|
if (message.display_recipient.length > 2) {
|
2018-12-16 20:14:09 +01:00
|
|
|
return i18n.t("group private messages with __recipient__",
|
|
|
|
{recipient: message.display_reply_to});
|
2013-11-13 19:40:02 +01:00
|
|
|
}
|
2017-01-19 20:18:03 +01:00
|
|
|
if (people.is_current_user(message.reply_to)) {
|
2018-12-16 20:14:09 +01:00
|
|
|
return i18n.t("private messages with yourself");
|
2013-11-13 19:40:02 +01:00
|
|
|
}
|
2018-12-16 20:14:09 +01:00
|
|
|
return i18n.t("private messages with __recipient__",
|
|
|
|
{recipient: message.display_reply_to});
|
2013-11-13 19:40:02 +01:00
|
|
|
}
|
|
|
|
|
2017-07-18 20:33:51 +02:00
|
|
|
exports.get_local_notify_mix_reason = function (message) {
|
|
|
|
var row = current_msg_list.get_row(message.id);
|
|
|
|
if (row.length > 0) {
|
|
|
|
// If our message is in the current message list, we do
|
|
|
|
// not have a mix, so we are happy.
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2018-12-22 23:15:04 +01:00
|
|
|
if (message.type === "stream" && muting.is_topic_muted(message.stream_id, util.get_message_topic(message))) {
|
2018-12-16 20:14:09 +01:00
|
|
|
return i18n.t("Sent! Your message was sent to a topic you have muted.");
|
2017-07-18 20:33:51 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
if (message.type === "stream" && !stream_data.in_home_view(message.stream_id)) {
|
2018-12-16 20:14:09 +01:00
|
|
|
return i18n.t("Sent! Your message was sent to a stream you have muted.");
|
2017-07-18 20:33:51 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// offscreen because it is outside narrow
|
|
|
|
// we can only look for these on non-search (can_apply_locally) messages
|
|
|
|
// see also: exports.notify_messages_outside_current_search
|
2018-12-16 20:14:09 +01:00
|
|
|
return i18n.t("Sent! Your message is outside your current narrow.");
|
2017-07-18 20:33:51 +02:00
|
|
|
};
|
|
|
|
|
message scrolling: Fix "Scroll down to view" warning.
We recently added a feature to warn users that they
may need to scroll down to view messages that they
just sent, but it was broken due to various complexities
in the rendering code path.
Now we compute it a bit more rigorously.
It requires us to pass some info about rendering up
and down the stack, which is why it's kind of a long
commit, but the bulk of the logic is in these JS files:
* message_list_view.js
* notifications.js
I choose to pass structs around instead of booleans,
because I anticipate we may eventually add more metadata
about rendering to it, plus bools are just kinda brittle.
(The exceptions are that `_maybe_autoscroll`, which
is at the bottom of the stack, just passes back a simple
boolean, and `notify_local_mixes`, also at the bottom
of the stack, just accepts a simple boolean.)
This errs on the side of warning the user, even if the
new message is partially visible.
Fixes #11138
2019-01-07 21:00:03 +01:00
|
|
|
exports.notify_local_mixes = function (messages, need_user_to_scroll) {
|
2017-07-18 20:03:14 +02:00
|
|
|
/*
|
|
|
|
This code should only be called when we are locally echoing
|
|
|
|
messages. It notifies users that their messages aren't
|
|
|
|
actually in the view that they composed to.
|
|
|
|
|
|
|
|
This code is called after we insert messages into our
|
|
|
|
message list widgets. All of the conditions here are
|
|
|
|
checkable locally, so we may want to execute this code
|
|
|
|
earlier in the codepath at some point and possibly punt
|
|
|
|
on local rendering.
|
|
|
|
*/
|
2017-07-18 17:47:08 +02:00
|
|
|
|
2013-10-09 22:42:15 +02:00
|
|
|
_.each(messages, function (message) {
|
2017-07-18 17:47:08 +02:00
|
|
|
if (!people.is_my_user_id(message.sender_id)) {
|
|
|
|
blueslip.warn('We did not expect messages sent by others to get here');
|
2013-10-09 22:42:15 +02:00
|
|
|
return;
|
|
|
|
}
|
2013-11-13 19:40:02 +01:00
|
|
|
|
2017-07-18 20:33:51 +02:00
|
|
|
var reason = exports.get_local_notify_mix_reason(message);
|
|
|
|
|
|
|
|
if (!reason) {
|
message scrolling: Fix "Scroll down to view" warning.
We recently added a feature to warn users that they
may need to scroll down to view messages that they
just sent, but it was broken due to various complexities
in the rendering code path.
Now we compute it a bit more rigorously.
It requires us to pass some info about rendering up
and down the stack, which is why it's kind of a long
commit, but the bulk of the logic is in these JS files:
* message_list_view.js
* notifications.js
I choose to pass structs around instead of booleans,
because I anticipate we may eventually add more metadata
about rendering to it, plus bools are just kinda brittle.
(The exceptions are that `_maybe_autoscroll`, which
is at the bottom of the stack, just passes back a simple
boolean, and `notify_local_mixes`, also at the bottom
of the stack, just accepts a simple boolean.)
This errs on the side of warning the user, even if the
new message is partially visible.
Fixes #11138
2019-01-07 21:00:03 +01:00
|
|
|
if (need_user_to_scroll) {
|
|
|
|
reason = i18n.t("Sent! Scroll down to view your message.");
|
2018-11-30 15:06:43 +01:00
|
|
|
exports.notify_above_composebox(reason, "", null, "");
|
|
|
|
setTimeout(function () {
|
|
|
|
$('#out-of-view-notification').hide();
|
|
|
|
}, 3000);
|
|
|
|
}
|
|
|
|
|
message scrolling: Fix "Scroll down to view" warning.
We recently added a feature to warn users that they
may need to scroll down to view messages that they
just sent, but it was broken due to various complexities
in the rendering code path.
Now we compute it a bit more rigorously.
It requires us to pass some info about rendering up
and down the stack, which is why it's kind of a long
commit, but the bulk of the logic is in these JS files:
* message_list_view.js
* notifications.js
I choose to pass structs around instead of booleans,
because I anticipate we may eventually add more metadata
about rendering to it, plus bools are just kinda brittle.
(The exceptions are that `_maybe_autoscroll`, which
is at the bottom of the stack, just passes back a simple
boolean, and `notify_local_mixes`, also at the bottom
of the stack, just accepts a simple boolean.)
This errs on the side of warning the user, even if the
new message is partially visible.
Fixes #11138
2019-01-07 21:00:03 +01:00
|
|
|
// This is the HAPPY PATH--for most messages we do nothing
|
|
|
|
// other than maybe sending the above message.
|
2013-10-09 22:42:15 +02:00
|
|
|
return;
|
|
|
|
}
|
2017-07-18 20:33:51 +02:00
|
|
|
|
2019-01-08 00:34:03 +01:00
|
|
|
var link_msg_id = message.id;
|
|
|
|
var link_class = "compose_notification_narrow_by_topic";
|
|
|
|
var link_text = i18n.t("Narrow to __- message_recipient__",
|
|
|
|
{message_recipient: get_message_header(message)});
|
|
|
|
|
2017-07-18 20:33:51 +02:00
|
|
|
exports.notify_above_composebox(reason, link_class, link_msg_id, link_text);
|
2013-10-09 22:42:15 +02:00
|
|
|
});
|
|
|
|
};
|
|
|
|
|
|
|
|
// for callback when we have to check with the server if a message should be in
|
|
|
|
// the current_msg_list (!can_apply_locally; a.k.a. "a search").
|
|
|
|
exports.notify_messages_outside_current_search = function (messages) {
|
|
|
|
_.each(messages, function (message) {
|
2017-01-19 20:18:03 +01:00
|
|
|
if (!people.is_current_user(message.sender_email)) {
|
2013-10-09 22:42:15 +02:00
|
|
|
return;
|
|
|
|
}
|
2018-12-16 20:14:09 +01:00
|
|
|
var link_text = i18n.t("Narrow to __- message_recipient__",
|
|
|
|
{message_recipient: get_message_header(message)});
|
|
|
|
exports.notify_above_composebox(i18n.t("Sent! Your recent message is outside the current search."),
|
2018-11-13 16:11:42 +01:00
|
|
|
"compose_notification_narrow_by_topic",
|
2013-10-09 22:42:15 +02:00
|
|
|
message.id,
|
2018-12-16 20:14:09 +01:00
|
|
|
link_text);
|
2013-10-09 22:42:15 +02:00
|
|
|
});
|
|
|
|
};
|
|
|
|
|
|
|
|
exports.clear_compose_notifications = function () {
|
2013-11-13 19:40:02 +01:00
|
|
|
$('#out-of-view-notification').empty();
|
|
|
|
$('#out-of-view-notification').stop(true, true);
|
|
|
|
$('#out-of-view-notification').hide();
|
2013-10-09 22:42:15 +02:00
|
|
|
};
|
|
|
|
|
2017-07-19 14:39:28 +02:00
|
|
|
exports.reify_message_id = function (opts) {
|
|
|
|
var old_id = opts.old_id;
|
|
|
|
var new_id = opts.new_id;
|
|
|
|
|
|
|
|
// If a message ID that we're currently storing (as a link) has changed,
|
|
|
|
// update that link as well
|
|
|
|
_.each($('#out-of-view-notification a'), function (e) {
|
|
|
|
var elem = $(e);
|
|
|
|
var msgid = elem.data('msgid');
|
|
|
|
|
|
|
|
if (msgid === old_id) {
|
|
|
|
elem.data('msgid', new_id);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
};
|
|
|
|
|
2013-10-09 22:42:15 +02:00
|
|
|
exports.register_click_handlers = function () {
|
2018-11-13 16:11:42 +01:00
|
|
|
$('#out-of-view-notification').on('click', '.compose_notification_narrow_by_topic', function (e) {
|
2013-10-09 22:42:15 +02:00
|
|
|
var msgid = $(e.currentTarget).data('msgid');
|
2018-11-13 16:13:41 +01:00
|
|
|
narrow.by_topic(msgid, {trigger: 'compose_notification'});
|
2013-10-09 22:42:15 +02:00
|
|
|
e.stopPropagation();
|
|
|
|
e.preventDefault();
|
|
|
|
});
|
2013-11-13 19:40:02 +01:00
|
|
|
$('#out-of-view-notification').on('click', '.compose_notification_scroll_to_message', function (e) {
|
2013-10-09 22:42:15 +02:00
|
|
|
var msgid = $(e.currentTarget).data('msgid');
|
2013-11-13 19:40:02 +01:00
|
|
|
current_msg_list.select_id(msgid);
|
2016-05-25 13:26:57 +02:00
|
|
|
navigate.scroll_to_selected();
|
2013-11-13 19:40:02 +01:00
|
|
|
e.stopPropagation();
|
|
|
|
e.preventDefault();
|
|
|
|
});
|
|
|
|
$('#out-of-view-notification').on('click', '.out-of-view-notification-close', function (e) {
|
|
|
|
exports.clear_compose_notifications();
|
2013-10-09 22:42:15 +02:00
|
|
|
e.stopPropagation();
|
|
|
|
e.preventDefault();
|
|
|
|
});
|
|
|
|
};
|
|
|
|
|
2014-02-13 23:48:03 +01:00
|
|
|
exports.handle_global_notification_updates = function (notification_name, setting) {
|
|
|
|
// Update the global settings checked when determining if we should notify
|
|
|
|
// for a given message. These settings do not affect whether or not a
|
|
|
|
// particular stream should receive notifications.
|
2018-03-08 07:50:16 +01:00
|
|
|
if (settings_notifications.notification_settings.indexOf(notification_name) !== -1) {
|
|
|
|
page_params[notification_name] = setting;
|
2014-02-13 23:48:03 +01:00
|
|
|
}
|
2018-01-11 21:36:11 +01:00
|
|
|
|
|
|
|
if (notification_name === "notification_sound") {
|
|
|
|
// Change the sound source with the new page `notification_sound`.
|
|
|
|
update_notification_sound_source();
|
|
|
|
}
|
2014-02-13 23:48:03 +01:00
|
|
|
};
|
|
|
|
|
2012-11-23 23:53:38 +01:00
|
|
|
return exports;
|
|
|
|
|
|
|
|
}());
|
2016-12-04 08:59:56 +01:00
|
|
|
|
|
|
|
if (typeof module !== 'undefined') {
|
|
|
|
module.exports = notifications;
|
|
|
|
}
|
2018-05-28 08:04:36 +02:00
|
|
|
window.notifications = notifications;
|