Convert to file in lib/, rough active tracking

This commit is contained in:
riking 2015-04-08 11:10:55 -07:00
parent 41819838ef
commit da9913359c
5 changed files with 215 additions and 191 deletions

View File

@ -1,162 +0,0 @@
// TODO deduplicate controllers/notification.js
function notificationUrl(n) {
const it = Em.Object.create(n);
var badgeId = it.get("data.badge_id");
if (badgeId) {
var badgeName = it.get("data.badge_name");
return '/badges/' + badgeId + '/' + badgeName.replace(/[^A-Za-z0-9_]+/g, '-').toLowerCase();
}
var topicId = it.get('topic_id');
if (topicId) {
return Discourse.Utilities.postUrl(it.get("slug"), topicId, it.get("post_number"));
}
if (it.get('notification_type') === INVITED_TYPE) {
return '/my/invited';
}
}
export default Discourse.Controller.extend({
initSeenNotifications: function() {
const self = this;
// TODO make protocol to elect a tab responsible for desktop notifications
// and choose a new one when a tab is closed
// apparently needs to use localStorage !?
// https://github.com/diy/intercom.js
// Just causes a bit of a visual glitch as multiple are created and
// instantly replaced as is
self.set('primaryTab', true);
self.set('liveEnabled', false);
this.requestPermission().then(function() {
self.set('liveEnabled', true);
}).catch(function() {
self.set('liveEnabled', false);
});
self.set('seenNotificationDates', {});
Discourse.ajax("/notifications.json?silent=true").then(function(result) {
self.updateSeenNotificationDatesFrom(result);
});
}.on('init'),
// Call-in point from message bus
notificationsChanged(currentUser) {
if (!this.get('liveEnabled')) { return; }
if (!this.get('primaryTab')) { return; }
const blueNotifications = currentUser.get('unread_notifications');
const greenNotifications = currentUser.get('unread_private_messages');
const self = this;
if (blueNotifications > 0 || greenNotifications > 0) {
Discourse.ajax("/notifications.json?silent=true").then(function(result) {
const unread = result.filter(n => !n.read);
const unseen = self.updateSeenNotificationDatesFrom(result);
const unreadCount = unread.length;
const unseenCount = unseen.length;
if (unreadCount === 0 || unseenCount === 0) {
return;
}
if (typeof document.hidden !== "undefined" && !document.hidden) {
return;
}
let bodyParts = [];
unread.forEach(function(n) {
const i18nOpts = {
username: n.data['display_username'],
topic: n.data['topic_title'],
badge: n.data['badge_name']
};
bodyParts.push(I18n.t(self.i18nKey(n), i18nOpts));
});
const notificationTitle = I18n.t('notifications.popup_title', { count: unreadCount, site_title: Discourse.SiteSettings.title });
const notificationBody = bodyParts.join("\n");
const notificationIcon = Discourse.SiteSettings.logo_small_url || Discourse.SiteSettings.logo_url;
const notificationTag = self.get('notificationTagName');
// This shows the notification!
const notification = new Notification(notificationTitle, {
body: notificationBody,
icon: notificationIcon,
tag: notificationTag
});
const firstUnseen = unseen[0];
function clickEventHandler() {
Discourse.URL.routeTo(notificationUrl(firstUnseen));
// Cannot delay this until the page renders :(
// due to trigger-based permissions
window.focus();
}
notification.addEventListener('click', clickEventHandler);
setTimeout(function() {
notification.close();
notification.removeEventListener('click', clickEventHandler);
}, 10 * 1000);
});
}
},
// Utility function
// Wraps Notification.requestPermission in a Promise
requestPermission() {
return new Ember.RSVP.Promise(function(resolve, reject) {
Notification.requestPermission(function(status) {
if (status === "granted") {
Em.Logger.info('Discourse desktop notifications are enabled.');
resolve();
} else {
Em.Logger.info('Discourse desktop notifications are disabled.');
reject();
}
});
});
},
i18nKey(notification) {
let key = "notifications.popup." + this.site.get("notificationLookup")[notification.notification_type];
if (notification.data.display_username && notification.data.original_username &&
notification.data.display_username !== notification.data.original_username) {
key += "_mul";
}
return key;
},
notificationTagName: function() {
return "discourse-notification-popup-" + Discourse.SiteSettings.title;
}.property(),
// Utility function
updateSeenNotificationDatesFrom(notifications) {
const oldSeenNotificationDates = this.get('seenNotificationDates');
let newSeenNotificationDates = {};
let previouslyUnseenNotifications = [];
notifications.forEach(function(notification) {
const dateString = new Date(notification.created_at).toUTCString();
if (!oldSeenNotificationDates[dateString]) {
previouslyUnseenNotifications.push(notification);
}
newSeenNotificationDates[dateString] = true;
});
this.set('seenNotificationDates', newSeenNotificationDates);
return previouslyUnseenNotifications;
}
})

View File

@ -1,4 +1,5 @@
import ObjectController from 'discourse/controllers/object';
import { notificationUrl } from 'discourse/lib/desktop-notifications';
var INVITED_TYPE= 8;
@ -10,36 +11,17 @@ const NotificationController = ObjectController.extend({
username: Em.computed.alias("data.display_username"),
safe(prop) {
let val = this.get(prop);
if (val) { val = Handlebars.Utils.escapeExpression(val); }
return val;
},
// This is model logic
// It belongs in a model
// TODO deduplicate controllers/background-notifications.js
url: function() {
const badgeId = this.safe("data.badge_id");
if (badgeId) {
const badgeName = this.safe("data.badge_name");
return Discourse.getURL('/badges/' + badgeId + '/' + badgeName.replace(/[^A-Za-z0-9_]+/g, '-').toLowerCase());
}
const topicId = this.safe('topic_id');
if (topicId) {
return Discourse.Utilities.postUrl(this.safe("slug"), topicId, this.safe("post_number"));
}
if (this.get('notification_type') === INVITED_TYPE) {
return Discourse.getURL('/my/invited');
}
return notificationUrl(this);
}.property("data.{badge_id,badge_name}", "slug", "topic_id", "post_number"),
description: function() {
const badgeName = this.safe("data.badge_name");
if (badgeName) { return badgeName; }
return this.blank("data.topic_title") ? "" : this.safe("data.topic_title");
const badgeName = this.get("data.badge_name");
if (badgeName) { return Handlebars.Utils.escapeExpression(badgeName); }
return this.blank("data.topic_title") ? "" : Handlebars.Utils.escapeExpression(this.get("data.topic_title"));
}.property("data.{badge_name,topic_title}")
});

View File

@ -1,4 +1,6 @@
// Subscribes to user events on the message bus
import { init as initDesktopNotifications, onNotification } from 'discourse/lib/desktop-notifications'
export default {
name: 'subscribe-user-notifications',
after: 'message-bus',
@ -6,8 +8,7 @@ export default {
const user = container.lookup('current-user:main'),
site = container.lookup('site:main'),
siteSettings = container.lookup('site-settings:main'),
bus = container.lookup('message-bus:main'),
bgController = container.lookup('controller:background-notifications');
bus = container.lookup('message-bus:main');
bus.callbackInterval = siteSettings.anon_polling_interval;
bus.backgroundCallbackInterval = siteSettings.background_polling_interval;
@ -36,24 +37,26 @@ export default {
user.set('post_queue_new_count', data.post_queue_new_count);
});
}
bus.subscribe("/notification/" + user.get('id'), (function(data) {
bus.subscribe("/notification/" + user.get('id'), function(data) {
const oldUnread = user.get('unread_notifications');
const oldPM = user.get('unread_private_messages');
user.set('unread_notifications', data.unread_notifications);
user.set('unread_private_messages', data.unread_private_messages);
if(oldUnread !== data.unread_notifications || oldPM !== data.unread_private_messages) {
if (oldUnread !== data.unread_notifications || oldPM !== data.unread_private_messages) {
user.set('lastNotificationChange', new Date());
bgController.notificationsChanged(user);
onNotification(user);
}
}), user.notification_channel_position);
}, user.notification_channel_position);
bus.subscribe("/categories", function(data){
_.each(data.categories,function(c){
site.updateCategory(c);
});
});
initDesktopNotifications(container);
}
}
};

View File

@ -0,0 +1,200 @@
let primaryTab;
let liveEnabled;
let seenNotificationDates = {};
let notificationTagName;
let mbClientId;
const focusTrackerKey = "focus-tracker";
function init(container) {
liveEnabled = false;
requestPermission().then(function () {
try {
localStorage.getItem(focusTrackerKey);
} catch (e) {
liveEnabled = false;
Em.Logger.info('Discourse desktop notifications are disabled - localStorage denied.');
return;
}
liveEnabled = true;
Em.Logger.info('Discourse desktop notifications are enabled.');
init2(container);
}).catch(function () {
liveEnabled = false;
Em.Logger.info('Discourse desktop notifications are disabled - permission denied.');
});
}
function init2(container) {
// Load up the current state of the notifications
seenNotificationDates = {};
Discourse.ajax("/notifications.json?silent=true").then(function(result) {
updateSeenNotificationDatesFrom(result);
});
notificationTagName = "discourse-notification-popup-" + Discourse.SiteSettings.title;
const messageBus = container.lookup('message-bus:main');
mbClientId = messageBus.clientId;
console.info("My client ID is", mbClientId);
window.addEventListener("storage", function(e) {
// note: This event only fires when other tabs setItem()
const key = e.key;
if (key !== focusTrackerKey) {
return true;
}
if (primaryTab) {
primaryTab = false;
console.debug("Releasing focus to", e.oldValue);
}
});
window.addEventListener("focus", function() {
if (!primaryTab) {
console.debug("Grabbing focus from", localStorage.getItem(focusTrackerKey));
primaryTab = true;
localStorage.setItem(focusTrackerKey, mbClientId);
}
});
if (document.hidden) {
primaryTab = false;
} else {
primaryTab = true;
localStorage.setItem(focusTrackerKey, mbClientId);
console.debug("Grabbing focus");
}
}
// Call-in point from message bus
function onNotification(currentUser) {
if (!liveEnabled) { return; }
if (!primaryTab) { return; }
const blueNotifications = currentUser.get('unread_notifications');
const greenNotifications = currentUser.get('unread_private_messages');
if (blueNotifications > 0 || greenNotifications > 0) {
Discourse.ajax("/notifications.json?silent=true").then(function(result) {
const unread = result.filter(n => !n.read);
const unseen = updateSeenNotificationDatesFrom(result);
const unreadCount = unread.length;
const unseenCount = unseen.length;
if (unreadCount === 0 || unseenCount === 0) {
return;
}
let bodyParts = [];
unread.forEach(function(n) {
const i18nOpts = {
username: n.data['display_username'],
topic: n.data['topic_title'],
badge: n.data['badge_name']
};
bodyParts.push(I18n.t(i18nKey(n), i18nOpts));
});
const notificationTitle = I18n.t('notifications.popup_title', { count: unreadCount, site_title: Discourse.SiteSettings.title });
const notificationBody = bodyParts.join("\n");
const notificationIcon = Discourse.SiteSettings.logo_small_url || Discourse.SiteSettings.logo_url;
// This shows the notification!
const notification = new Notification(notificationTitle, {
body: notificationBody,
icon: notificationIcon,
tag: notificationTagName
});
const firstUnseen = unseen[0];
function clickEventHandler() {
Discourse.URL.routeTo(_notificationUrl(firstUnseen));
// Cannot delay this until the page renders :(
// due to trigger-based permissions
window.focus();
}
notification.addEventListener('click', clickEventHandler);
setTimeout(function() {
notification.close();
notification.removeEventListener('click', clickEventHandler);
}, 10 * 1000);
});
}
}
// Utility function
// Wraps Notification.requestPermission in a Promise
function requestPermission() {
return new Ember.RSVP.Promise(function(resolve, reject) {
Notification.requestPermission(function(status) {
if (status === "granted") {
resolve();
} else {
reject();
}
});
});
}
function i18nKey(notification) {
let key = "notifications.popup." + Discourse.Site.current().get("notificationLookup")[notification.notification_type];
if (notification.data.display_username && notification.data.original_username &&
notification.data.display_username !== notification.data.original_username) {
key += "_mul";
}
return key;
}
// Utility function
function updateSeenNotificationDatesFrom(notifications) {
const oldSeenNotificationDates = seenNotificationDates;
let newSeenNotificationDates = {};
let previouslyUnseenNotifications = [];
notifications.forEach(function(notification) {
const dateString = new Date(notification.created_at).toUTCString();
if (!oldSeenNotificationDates[dateString]) {
previouslyUnseenNotifications.push(notification);
}
newSeenNotificationDates[dateString] = true;
});
seenNotificationDates = newSeenNotificationDates;
return previouslyUnseenNotifications;
}
// Exported for controllers/notification.js.es6
function notificationUrl(it) {
var badgeId = it.get("data.badge_id");
if (badgeId) {
var badgeName = it.get("data.badge_name");
return Discourse.getURL('/badges/' + badgeId + '/' + badgeName.replace(/[^A-Za-z0-9_]+/g, '-').toLowerCase());
}
var topicId = it.get('topic_id');
if (topicId) {
return Discourse.Utilities.postUrl(it.get("slug"), topicId, it.get("post_number"));
}
if (it.get('notification_type') === INVITED_TYPE) {
return Discourse.getURL('/my/invited');
}
}
function _notificationUrl(notificationJson) {
const it = Em.Object.create(notificationJson);
return notificationUrl(it);
}
export { init, notificationUrl, onNotification };

View File

@ -66,6 +66,7 @@
//= require ./discourse/dialects/dialect
//= require ./discourse/lib/emoji/emoji
//= require ./discourse/lib/sharing
//= require discourse/lib/desktop-notifications
//= require_tree ./discourse/dialects
//= require_tree ./discourse/controllers