/** * Store the number of unread notifications on load. * That way, if the number got higher while the page was * still open, we can announce it by popping a nice dialog. */ var unread_on_load = 0; var first_load = false; var unread_new = 0; /** * Clear notifications by emptying the count number * and removing the has-notification class that styles the toggle */ function clearNotificationIcon(){ $('#notifications-count').html(''); $('#notifications-toggle').removeClass('has-notifications'); } // Get notifications by fetching /notifications/ JSON every 30 seconds function getNotifications(){ $.getJSON( "/notifications/", function( data ) { if (!first_load) { unread_on_load = data['items'].length; first_load = true; } var items = []; unread_new = 0; // Only if there's actual data if (data['items'][0]){ // Loop through each item $.each(data['items'], function(i, no){ // Increase the unread_new counter if (!no['is_read']){ unread_new++ }; // Check if the current item has been read, to style it var is_read = no['is_read'] ? 'is_read' : ''; var read_info = 'data-id="'+ no['_id'] + '" data-read="' + no['is_read'] + '"'; // Notification list item var content = '
  • '; // User's avatar content += '
    '; content += ' '; content += '
    '; // Text of the notification content += '
    '; // Username and action content += no['username'] + ' ' + no['action'] + ' '; // Object content += ''; content += no['context_object_name'] + ' '; content += ' '; // Date content += ''; content += ''; content += no['date']; content += ''; content += ''; // Read Toggle content += ''; if (no['is_read']){ content += ''; } else { content += ''; }; content += ''; // Subscription Toggle content += ''; if (no['is_subscribed']){ content += ''; } else { content += ''; }; content += ''; content += '
    '; content += '
  • '; items.push(content); }); // each if (unread_new > 0) { unread_on_load = unread_new; // Display notifications count $('#notifications-count').html('' + unread_new + ''); $('#notifications-toggle').addClass('has-notifications'); // Update notifications count on the document title DocumentTitleAPI.set_notification_count(unread_new); } else { clearNotificationIcon(); }; checkPopNotification( data['items'][0]['_id'], data['items'][0]['username'], data['items'][0]['username_avatar'], data['items'][0]['action'], data['items'][0]['date'], data['items'][0]['context_object_name'], data['items'][0]['object_url']); } else { var content = '
  • '; content += 'No notifications... yet.'; content += '
  • '; items.push(content); }; // if items // Populate the list $('ul#notifications-list').html(items.join('')); }) .done(function(){ // clear the counter unread_on_load = unread_new; }); }; // Used when we click somewhere in the page function hideNotifications(){ $('#notifications').hide(); $('#notifications-toggle').removeClass('active'); }; function popNotification(){ var $notification_pop = $('#notification-pop'); // Pop in! $notification_pop.addClass('in'); // After 10s, add a class to make it pop out... setTimeout(function(){ $notification_pop.addClass('out'); // ...and a second later, remove all classes setTimeout(function(){ $notification_pop.removeAttr('class'); }, 1000); }, 10000); // Set them the same so it doesn't pop up again unread_on_load = unread_new; }; function checkPopNotification(id,username,username_avatar,action,date,context_object_name,object_url) { var $notification_pop = $('#notification-pop'); // If there's new content if (unread_new > unread_on_load){ // Fill in the urls for redirect on click, and mark-read $notification_pop.attr('data-url', object_url); $notification_pop.attr('data-read-toggle', '/notifications/' + id + '/read-toggle'); // The text in the pop var text = '' + username + ' '; text += action + ' '; text += context_object_name + ' '; text += '' + date + ''; // Fill the html $notification_pop.find('.nc-text').html(text); $notification_pop.find('.nc-avatar img').attr('src', username_avatar); // Pop in! popNotification(); }; }; // Function to set #notifications flyout height and resize if needed function notificationsResize(){ var height = $(window).height() - 80; if ($('#notifications').height() > height){ $('#notifications').css({ 'max-height' : height / 2, 'overflow-y' : 'scroll' } ); } else { $('#notifications').css({ 'max-height' : '1000%', 'overflow-y' : 'initial' } ); }; }; $(function() { // Click anywhere in the page to hide #notifications $(document).click(function () { hideNotifications(); }); // ...but clicking inside #notifications shouldn't hide itself $('#notifications').on('click', function (e) { e.stopPropagation(); }); // Toggle the #notifications flyout $('#notifications-toggle').on('click', function (e) { e.stopPropagation(); var $navbarCollapse = $('nav.navbar-collapse'); var $notificationIcon = $('.nav-notifications-icon'); $('#notifications').toggle(); $(this).toggleClass("active"); notificationsResize(); // Hide other dropdowns $('nav .dropdown').removeClass('open'); if ($navbarCollapse.hasClass('in')){ $navbarCollapse .addClass('show-notifications') .removeClass('in'); $notificationIcon .removeClass('pi-notifications-none') .addClass('pi-cancel'); } else { $navbarCollapse.removeClass('show-notifications'); $notificationIcon .addClass('pi-notifications-none') .removeClass('pi-cancel'); } }); // Hide flyout when clicking other dropdowns $('nav').on('click', '.dropdown', function (e) { $('#notifications').hide(); $('#notifications-toggle').removeClass('active'); }); $('#notification-pop').on('click', function (e) { e.preventDefault(); e.stopPropagation(); var link_url = $(this).data('url'); var read_url = $(this).data('read-toggle'); $.get(read_url) .done(function () { window.location.href = link_url; }); }); // Read/Subscription Toggles $('ul#notifications-list').on('click', '.nc-button', function (e) { e.preventDefault(); var nc = $(this); // Swap to spin icon while we wait for the response $('i', nc).addClass('spin'); $.get($(nc).attr('href')) .done(function (data) { if ($(nc).hasClass('nc-read_toggle')) { if (data.data.is_read) { $('i', nc).removeClass('pi-circle').addClass('pi-circle-dot'); $(nc).closest('.nc-item').addClass('is_read'); } else { $('i', nc).removeClass('pi-circle-dot').addClass('pi-circle'); $(nc).closest('.nc-item').removeClass('is_read'); } } ; if ($(nc).hasClass('nc-subscription_toggle')) { if (data.data.is_subscribed) { $('i', nc).removeClass('pi-toggle-on').addClass('pi-toggle-off'); } else { $('i', nc).removeClass('pi-toggle-off').addClass('pi-toggle-on'); } } ; $('i', nc).removeClass('spin'); }); }); // When clicking on links, toggle as read $('ul#notifications-list').on('click', '.nc-a', function (e) { e.preventDefault(); var is_read = $(this).data('read'); var link_url = $(this).attr('href'); var read_url = '/notifications/' + $(this).data('id') + '/read-toggle'; if (is_read) { window.location.href = link_url; } else { $.get(read_url) .done(function () { window.location.href = link_url; }); } }); // Mark All as Read $('#notifications-markallread').on('click', function (e) { e.preventDefault(); $.get("/notifications/read-all"); $('ul#notifications-list li.nc-item:not(.is_read)').each(function () { $(this).addClass('is_read'); }); clearNotificationIcon(); // Update notifications count on the document title DocumentTitleAPI.set_notification_count(0); }); }); function getNotificationsLoop() { getNotifications(); var getLoop = setTimeout(function () { getNotificationsLoop(); }, 30000); } /* Returns a more-or-less reasonable message given an error response object. */ function xhrErrorResponseMessage(err) { if (typeof err.responseJSON == 'undefined') return err.statusText; if (typeof err.responseJSON._error != 'undefined' && typeof err.responseJSON._error.message != 'undefined') return err.responseJSON._error.message; if (typeof err.responseJSON._message != 'undefined') return err.responseJSON._message return err.statusText; } /* Notifications: Toastr Defaults */ toastr.options.showDuration = 50; toastr.options.progressBar = true; toastr.options.positionClass = 'toast-bottom-left';