mirror of
https://github.com/discourse/discourse.git
synced 2025-01-31 18:01:01 +08:00
Convert to file in lib/, rough active tracking
This commit is contained in:
parent
41819838ef
commit
da9913359c
|
@ -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;
|
|
||||||
}
|
|
||||||
})
|
|
|
@ -1,4 +1,5 @@
|
||||||
import ObjectController from 'discourse/controllers/object';
|
import ObjectController from 'discourse/controllers/object';
|
||||||
|
import { notificationUrl } from 'discourse/lib/desktop-notifications';
|
||||||
|
|
||||||
var INVITED_TYPE= 8;
|
var INVITED_TYPE= 8;
|
||||||
|
|
||||||
|
@ -10,36 +11,17 @@ const NotificationController = ObjectController.extend({
|
||||||
|
|
||||||
username: Em.computed.alias("data.display_username"),
|
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
|
// This is model logic
|
||||||
// It belongs in a model
|
// It belongs in a model
|
||||||
// TODO deduplicate controllers/background-notifications.js
|
// TODO deduplicate controllers/background-notifications.js
|
||||||
url: function() {
|
url: function() {
|
||||||
const badgeId = this.safe("data.badge_id");
|
return notificationUrl(this);
|
||||||
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');
|
|
||||||
}
|
|
||||||
}.property("data.{badge_id,badge_name}", "slug", "topic_id", "post_number"),
|
}.property("data.{badge_id,badge_name}", "slug", "topic_id", "post_number"),
|
||||||
|
|
||||||
description: function() {
|
description: function() {
|
||||||
const badgeName = this.safe("data.badge_name");
|
const badgeName = this.get("data.badge_name");
|
||||||
if (badgeName) { return badgeName; }
|
if (badgeName) { return Handlebars.Utils.escapeExpression(badgeName); }
|
||||||
return this.blank("data.topic_title") ? "" : this.safe("data.topic_title");
|
return this.blank("data.topic_title") ? "" : Handlebars.Utils.escapeExpression(this.get("data.topic_title"));
|
||||||
}.property("data.{badge_name,topic_title}")
|
}.property("data.{badge_name,topic_title}")
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,4 +1,6 @@
|
||||||
// Subscribes to user events on the message bus
|
// Subscribes to user events on the message bus
|
||||||
|
import { init as initDesktopNotifications, onNotification } from 'discourse/lib/desktop-notifications'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'subscribe-user-notifications',
|
name: 'subscribe-user-notifications',
|
||||||
after: 'message-bus',
|
after: 'message-bus',
|
||||||
|
@ -6,8 +8,7 @@ export default {
|
||||||
const user = container.lookup('current-user:main'),
|
const user = container.lookup('current-user:main'),
|
||||||
site = container.lookup('site:main'),
|
site = container.lookup('site:main'),
|
||||||
siteSettings = container.lookup('site-settings:main'),
|
siteSettings = container.lookup('site-settings:main'),
|
||||||
bus = container.lookup('message-bus:main'),
|
bus = container.lookup('message-bus:main');
|
||||||
bgController = container.lookup('controller:background-notifications');
|
|
||||||
|
|
||||||
bus.callbackInterval = siteSettings.anon_polling_interval;
|
bus.callbackInterval = siteSettings.anon_polling_interval;
|
||||||
bus.backgroundCallbackInterval = siteSettings.background_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);
|
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 oldUnread = user.get('unread_notifications');
|
||||||
const oldPM = user.get('unread_private_messages');
|
const oldPM = user.get('unread_private_messages');
|
||||||
|
|
||||||
user.set('unread_notifications', data.unread_notifications);
|
user.set('unread_notifications', data.unread_notifications);
|
||||||
user.set('unread_private_messages', data.unread_private_messages);
|
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());
|
user.set('lastNotificationChange', new Date());
|
||||||
bgController.notificationsChanged(user);
|
onNotification(user);
|
||||||
}
|
}
|
||||||
}), user.notification_channel_position);
|
}, user.notification_channel_position);
|
||||||
|
|
||||||
bus.subscribe("/categories", function(data){
|
bus.subscribe("/categories", function(data){
|
||||||
_.each(data.categories,function(c){
|
_.each(data.categories,function(c){
|
||||||
site.updateCategory(c);
|
site.updateCategory(c);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
initDesktopNotifications(container);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -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 };
|
|
@ -66,6 +66,7 @@
|
||||||
//= require ./discourse/dialects/dialect
|
//= require ./discourse/dialects/dialect
|
||||||
//= require ./discourse/lib/emoji/emoji
|
//= require ./discourse/lib/emoji/emoji
|
||||||
//= require ./discourse/lib/sharing
|
//= require ./discourse/lib/sharing
|
||||||
|
//= require discourse/lib/desktop-notifications
|
||||||
|
|
||||||
//= require_tree ./discourse/dialects
|
//= require_tree ./discourse/dialects
|
||||||
//= require_tree ./discourse/controllers
|
//= require_tree ./discourse/controllers
|
||||||
|
|
Loading…
Reference in New Issue
Block a user