diff --git a/templates/zephyr/index.html b/templates/zephyr/index.html index 9ab6b6044f..1be55a568d 100644 --- a/templates/zephyr/index.html +++ b/templates/zephyr/index.html @@ -48,6 +48,7 @@ + {% if debug %} @@ -62,6 +63,7 @@ var initial_pointer = {{ user_profile.pointer }}; var email = "{{ user_profile.user.email|escapejs }}"; var have_initial_messages = {{ have_initial_messages|escapejs }}; +var desktop_notifications_enabled = "{{ user_profile.enable_desktop_notifications|escapejs }}" === "True"; var stream_list = [ {% for stream in streams %} diff --git a/tools/jslint/check-all.js b/tools/jslint/check-all.js index 645ab8b719..aad319ca43 100644 --- a/tools/jslint/check-all.js +++ b/tools/jslint/check-all.js @@ -7,6 +7,7 @@ var globals = // index.html + ' initial_pointer email stream_list people_list have_initial_messages' + + ' desktop_notifications_enabled' // common.js + ' status_classes' @@ -41,6 +42,9 @@ var globals = // typeahead_helper.js + ' typeahead_helper' + // notifications.js + + ' notifications' + // ui.js + ' ui' diff --git a/zephyr/static-access-control/4nrjx8cwce2bka8r/js/notifications.js b/zephyr/static-access-control/4nrjx8cwce2bka8r/js/notifications.js new file mode 120000 index 0000000000..f847a2ea9f --- /dev/null +++ b/zephyr/static-access-control/4nrjx8cwce2bka8r/js/notifications.js @@ -0,0 +1 @@ +../../../static/js/notifications.js \ No newline at end of file diff --git a/zephyr/static/js/notifications.js b/zephyr/static/js/notifications.js new file mode 100644 index 0000000000..62c1f9f447 --- /dev/null +++ b/zephyr/static/js/notifications.js @@ -0,0 +1,113 @@ +var notifications = (function () { + +var exports = {}; + +var window_has_focus = true; + +exports.initialize = function () { + if (!window.webkitNotifications) { + return; + } + + $(window).focus(function () { + window_has_focus = true; + }).blur(function () { + window_has_focus = false; + }); + + $(document).click(function () { + if (!desktop_notifications_enabled) { + return; + } + if (window.webkitNotifications.checkPermission() !== 0) { // 0 is PERMISSION_ALLOWED + window.webkitNotifications.requestPermission(); + } + }); +}; + +function gravatar_url(message) { + return "https://secure.gravatar.com/avatar/" + message.gravatar_hash + + "?d=identicon&s=30?stamp=" + ui.get_gravatar_stamp(); +} + +var notice_memory = {}; + +function process_message(message) { + var i, notification_object; + var key = message.display_reply_to; + var title = message.sender_full_name; + var content = $('
').html(message.content).text(); + var other_recipients = message.display_reply_to; + var msg_count = 1; + + // 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 + ", ", ""); + + if (content.length > 150) { + // Truncate content at a word boundary + for (i = 150; i > 0; i--) { + if (content[i] === ' ') { + break; + } + } + content = content.substring(0, i); + content += " [...]"; + } + + if (notice_memory[key] !== undefined) { + msg_count = notice_memory[key].msg_count + 1; + title = msg_count + " messages from " + title; + notification_object = notice_memory[key].obj; + // We must remove the .onclose so that it does not trigger on .cancel + notification_object.onclose = function () {}; + notification_object.onclick = function () {}; + notification_object.cancel(); + } + + if (message.type === "huddle") { + // 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 + ")"; + } + + notice_memory[key] = { + obj: window.webkitNotifications.createNotification( + gravatar_url(message), title, content), + msg_count: msg_count + }; + notification_object = notice_memory[key].obj; + notification_object.onclick = function () { + notification_object.cancel(); + delete notice_memory[key]; + }; + notification_object.onclose = function () { + delete notice_memory[key]; + }; + notification_object.show(); +} + +exports.received_messages = function (messages) { + var i; + if (!window.webkitNotifications || + !desktop_notifications_enabled || + window_has_focus) { + return; + } + + $.each(messages, function (index, message) { + if (message.sender_email !== email && + (message.type === "personal" || message.type === "huddle")) { + process_message(message); + } + }); +}; + +return exports; + +}()); diff --git a/zephyr/static/js/ui.js b/zephyr/static/js/ui.js index cb708ee05e..1695d302d2 100644 --- a/zephyr/static/js/ui.js +++ b/zephyr/static/js/ui.js @@ -412,6 +412,10 @@ $(function () { } update_gravatars(); + if (result.enable_desktop_notifications !== undefined) { + desktop_notifications_enabled = result.enable_desktop_notifications; + } + settings_status.removeClass(status_classes) .addClass('alert-success') .text(message).stop(true).fadeTo(0,1); @@ -473,6 +477,7 @@ $(function () { composebox_typeahead.initialize(); search.initialize(); + notifications.initialize(); $("body").bind('click', function() { ui.hide_userinfo_popover(); diff --git a/zephyr/static/js/zephyr.js b/zephyr/static/js/zephyr.js index bd37a645f1..31fde4607d 100644 --- a/zephyr/static/js/zephyr.js +++ b/zephyr/static/js/zephyr.js @@ -594,6 +594,7 @@ function get_updates(options) { if (data.messages.length !== 0) { add_messages(data.messages, true); + notifications.received_messages(data.messages); } if (data.zephyr_mirror_active === false) {