Skip to content

Instantly share code, notes, and snippets.

@azybler
Last active August 29, 2015 13:58
Show Gist options
  • Save azybler/9977709 to your computer and use it in GitHub Desktop.
Save azybler/9977709 to your computer and use it in GitHub Desktop.
Pooling-based Notification sample JavaScript code.
var baseUrl = 'http://www.test.com/';
var Notification = {
ajaxFrontendLock: false, // to block user-triggered ajax call.
ajaxBackendLock: false, // to block backend-triggered ajax call.
ajaxTimeout: 5000, // 5 seconds.
getListUrl: baseUrl + 'notifications/get-list/',
getUnreadCountUrl: baseUrl + 'notifications/get-unread-count/',
getNewCountPoolingInterval: 10000), // 10 seconds.
markAsReadUrl: baseUrl + 'notifications/mark-as-read/',
notificationBtn: 'a#notification-popover-toggle',
selectAllBtn: 'a#notification-select-all',
selectNoneBtn: 'a#notification-select-none',
markSelectedCbx: '.notification-mark-selected',
loadingLbl: '.popover-container ul li.loading-label',
isEmptyLbl: '.popover-container ul li.isempty-label',
markAllAsReadBtn: 'a#notification-mark-all-as-read',
optionLbl: '.popover-container ul li.options-label',
setup: function() {
// Select all notifications.
$(Notification.selectAllBtn).bind({
click: function() {
$(markSelectedCbx).prop('checked', true);
Notification.onSelectionChange();
}
});
// Unselects all notifications.
$(selectNoneBtn).bind({
click: function() {
$(markSelectedCbx).prop('checked', false);
Notification.onSelectionChange();
}
});
// Handle click on Notification link to load notifications.
$(notificationBtn).bind({
click: function() {
if (!$(this).hasClass('over')) {
// Populate notification pop over with top x notifications.
Notification.getList();
}
}
});
// Update UI to show "? new notifications" based on new count with an
// interval.
(function poll() {
setTimeout(function() {
Notification.getUnreadCount(function() {
// Setup the next poll recursively
poll();
});
}, Notification.getNewCountPoolingInterval);
})();
},
updateUiBeginLoading: function() {
// Show the loading icon.
$(loadingLbl).css({ 'display': 'block' });
// Remove all li elements after the first one with the class
// 'loading-label'
$(loadingLbl).nextAll('li.item').remove();
// Hides the "there is no notification" message.
$(isEmptyLbl).css({ 'display': 'none' });
// Hides the top options bar on the notification pop over.
$(optionLbl).css({ 'display': 'none' });
},
updateUiEndLoading: function() {
// Hides the loading icon.
$(loadingLbl).css({ 'display': 'none' });
},
markAllOrSelectedToRead: function(e) {
var nids = [],
notifications = e.data.notifications || [];
notifications.forEach(function(notification) {
if (notification.is(':checked')) {
nids.push(notification.data('id'));
}
});
if (0 === nids.length) {
notifications.forEach(function(notification) {
nids.push(notification.data('id'));
});
}
if (0 === nids.length) {
nids = 'none';
}
if (Notification.ajaxFrontendLock) return;
Notification.ajaxFrontendLock = true;
// indicate Ui that ajax call has started.
Notification.updateUiBeginLoading();
$.ajax({
type: 'POST',
url: Notification.markAsReadUrl,
data: { 'nids[]': nids },
timeout: Notification.ajaxTimeout,
success: function(response) {
Notification.ajaxFrontendLock = false;
// No need to call updateUiEndLoading(), after getList() has ended, it
// will call updateUiEndLoading() for us.
if (200 == response.status) {
Notification.getList();
}
},
error: function(jqXHR, textStatus, errorThrown) {
Notification.ajaxFrontendLock = false;
// indicate Ui that ajax call has ended.
Notification.updateUiEndLoading();
},
dataType: 'json'
});
},
getUnreadCount: function(callback) {
if (!Notification.ajaxFrontendLock) {
Notification.ajaxBackendLock = true;
$.ajax({
type: 'GET',
url: Notification.getUnreadCountUrl,
data: { },
timeout: Notification.ajaxTimeout,
success: function(response) {
Notification.ajaxBackendLock = false;
if (200 == response.status) {
Notification.updateUiWithUnreadCount(response.count);
}
if (callback) {
callback();
}
},
error: function(jqXHR, textStatus, errorThrown) {
Notification.ajaxBackendLock = false;
if (callback) {
callback();
}
},
dataType: 'json'
});
} else {
if (callback) {
callback();
}
}
},
updateUiWithUnreadCount: function(count) {
if (0 === count) {
$(notificationBtn).removeClass('hasNew');
$(notificationBtn).text('Notifications');
} else if (1 === count) {
$(notificationBtn).addClass('hasNew');
$(notificationBtn).text('1 New Notification');
} else if (count >= 2) {
$(notificationBtn).addClass('hasNew');
$(notificationBtn).text(count + ' New Notifications');
}
},
isAllSelectableChecked: function() {
// Check if all the notification is selected.
return $(markSelectedCbx).is(':checked').length ===
$(markSelectedCbx).length;
},
isAllSelectableUnchecked: function() {
// Check if all the notification is not selected.
return $(markSelectedCbx).not(':checked').length ===
$(markSelectedCbx).length;
},
onSelectionChange: function() {
if (Notification.isAllSelectableChecked()) {
// All notifications are checked.
$(Notification.selectAllBtn).css({ 'display': 'none' });
} else {
// There are some checked notifications.
$(Notification.selectAllBtn).css({ 'display': '' });
}
if (Notification.isAllSelectableUnchecked()) {
// All notifications are unchecked.
$(selectNoneBtn).css({ 'display': 'none' });
$(markAllAsReadBtn).text('Mark all as read');
} else {
// There are some unchecked notifications.
$(selectNoneBtn).css({ 'display': '' });
$(markAllAsReadBtn).text('Mark selected as read');
}
},
getList: function() {
if (Notification.ajaxFrontendLock) return;
Notification.ajaxFrontendLock = true;
// indicate Ui that ajax call has started.
Notification.updateUiBeginLoading();
$.ajax({
type: 'GET',
url: Notification.getListUrl,
data: { },
timeout: Notification.ajaxTimeout,
success: function(response) {
Notification.ajaxFrontendLock = false;
// indicate Ui that ajax call has ended.
Notification.updateUiEndLoading();
if (200 == response.status) {
Notification.updateUiWithUnreadCount(response.total_count);
Notification.updateUiWithNotifications(response.notifications);
}
},
error: function(jqXHR, textStatus, errorThrown) {
Notification.ajaxFrontendLock = false;
// indicate Ui that ajax call has ended.
Notification.updateUiEndLoading();
},
dataType: 'json'
});
},
updateUiWithNotifications: function(result) {
var partialHtml = '';
result = result || [];
if (result.length > 0) {
for (var i in result) {
partialHtml += '<li class="item';
// Add class name 'read' if the notification has been read by the user.
if (null != result[i].read_on) {
partialHtml += ' read';
}
partialHtml += '">';
partialHtml += '<input type="checkbox" ';
partialHtml += 'class="notification-mark-selected notification-mark" ';
partialHtml += 'data-id="' + result[i].id + '">';
partialHtml += '<a href="' + baseUrl + result[i].url + '">';
partialHtml += result[i].message;
partialHtml += '</a>';
partialHtml += '</li>';
}
$(loadingLbl).after(partialHtml);
$(isEmptyLbl).css({ 'display': 'none' });
$(optionLbl).css({ 'display': 'block' });
// bind the checkbox to perform mark one notification as read.
$(markSelectedCbx).unbind();
$(markSelectedCbx).bind({
click: Notification.onSelectionChange
});
var notifications = $(markSelectedCbx).map(function(i, n) {
return $(n);
}).get(); //converts it to an array
// Handle ajax logic: mark all notifications as read
$(markAllAsReadBtn).unbind();
$(markAllAsReadBtn).bind('click',
{ notifications: notifications },
Notification.markAllOrSelectedToRead);
Notification.onSelectionChange();
} else {
$(isEmptyLbl).css({ 'display': 'block' });
}
}
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment