mirror of
https://github.com/codeninjasllc/discourse.git
synced 2024-11-30 10:58:31 -05:00
Simplify desktop notifications, only include if mention/reply/pm/link
Include post excerpt in the notification
This commit is contained in:
parent
803feefd54
commit
4dfc1907ac
5 changed files with 87 additions and 159 deletions
|
@ -1,8 +1,26 @@
|
||||||
import ObjectController from 'discourse/controllers/object';
|
import ObjectController from 'discourse/controllers/object';
|
||||||
import { notificationUrl } from 'discourse/lib/desktop-notifications';
|
|
||||||
|
const INVITED_TYPE = 8;
|
||||||
|
|
||||||
export default ObjectController.extend({
|
export default ObjectController.extend({
|
||||||
|
|
||||||
|
notificationUrl: function(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');
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
scope: function() {
|
scope: function() {
|
||||||
return "notifications." + this.site.get("notificationLookup")[this.get("notification_type")];
|
return "notifications." + this.site.get("notificationLookup")[this.get("notification_type")];
|
||||||
}.property("notification_type"),
|
}.property("notification_type"),
|
||||||
|
@ -10,7 +28,7 @@ export default ObjectController.extend({
|
||||||
username: Em.computed.alias("data.display_username"),
|
username: Em.computed.alias("data.display_username"),
|
||||||
|
|
||||||
url: function() {
|
url: function() {
|
||||||
return notificationUrl(this);
|
return this.notificationUrl(this);
|
||||||
}.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() {
|
||||||
|
|
|
@ -37,6 +37,11 @@ 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-alert/" + user.get('id'), function(data){
|
||||||
|
onNotification(data, user);
|
||||||
|
});
|
||||||
|
|
||||||
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');
|
||||||
|
@ -46,7 +51,6 @@ export default {
|
||||||
|
|
||||||
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());
|
||||||
onNotification(user);
|
|
||||||
}
|
}
|
||||||
}, user.notification_channel_position);
|
}, user.notification_channel_position);
|
||||||
|
|
||||||
|
|
|
@ -6,10 +6,7 @@ let mbClientId = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx";
|
||||||
let lastAction = -1;
|
let lastAction = -1;
|
||||||
|
|
||||||
const focusTrackerKey = "focus-tracker";
|
const focusTrackerKey = "focus-tracker";
|
||||||
const seenDataKey = "seen-notifications";
|
|
||||||
const recentUpdateThreshold = 1000 * 60 * 2; // 2 minutes
|
|
||||||
const idleThresholdTime = 1000 * 10; // 10 seconds
|
const idleThresholdTime = 1000 * 10; // 10 seconds
|
||||||
const INVITED_TYPE = 8;
|
|
||||||
let notificationTagName; // "discourse-notification-popup-" + Discourse.SiteSettings.title;
|
let notificationTagName; // "discourse-notification-popup-" + Discourse.SiteSettings.title;
|
||||||
|
|
||||||
// Called from an initializer
|
// Called from an initializer
|
||||||
|
@ -57,28 +54,9 @@ function init(messageBus) {
|
||||||
|
|
||||||
// This function is only called if permission was granted
|
// This function is only called if permission was granted
|
||||||
function setupNotifications() {
|
function setupNotifications() {
|
||||||
// Load up the current state of the notifications
|
|
||||||
const seenData = JSON.parse(localStorage.getItem(seenDataKey));
|
|
||||||
let markAllSeen = true;
|
|
||||||
if (seenData) {
|
|
||||||
const lastUpdatedAt = new Date(seenData.updated_at);
|
|
||||||
if (lastUpdatedAt.getTime() + recentUpdateThreshold > new Date().getTime()) {
|
|
||||||
// The following conditions are met:
|
|
||||||
// - This is a new Discourse tab
|
|
||||||
// - The seen notification data was updated in the last 2 minutes
|
|
||||||
// Therefore, there is no need to reset the data.
|
|
||||||
markAllSeen = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (markAllSeen) {
|
|
||||||
Discourse.ajax("/notifications.json?silent=true").then(function(result) {
|
|
||||||
updateSeenNotificationDatesFrom(result);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
notificationTagName = "discourse-notification-popup-" + Discourse.SiteSettings.title;
|
notificationTagName = "discourse-notification-popup-" + Discourse.SiteSettings.title;
|
||||||
|
|
||||||
|
|
||||||
window.addEventListener("storage", function(e) {
|
window.addEventListener("storage", function(e) {
|
||||||
// note: This event only fires when other tabs setItem()
|
// note: This event only fires when other tabs setItem()
|
||||||
const key = e.key;
|
const key = e.key;
|
||||||
|
@ -117,96 +95,41 @@ function isIdle() {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Call-in point from message bus
|
// Call-in point from message bus
|
||||||
function onNotification(currentUser) {
|
function onNotification(data) {
|
||||||
if (!liveEnabled) { return; }
|
if (!liveEnabled) { return; }
|
||||||
if (!primaryTab) { return; }
|
if (!primaryTab) { return; }
|
||||||
|
if (!isIdle()) { return; }
|
||||||
|
|
||||||
const blueNotifications = currentUser.get('unread_notifications');
|
const notificationTitle = I18n.t(i18nKey(data.notification_type), {
|
||||||
const greenNotifications = currentUser.get('unread_private_messages');
|
site_title: Discourse.SiteSettings.title,
|
||||||
|
topic: data.topic_title,
|
||||||
if (blueNotifications > 0 || greenNotifications > 0) {
|
username: data.username
|
||||||
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 all notifications are seen, don't display
|
|
||||||
if (unreadCount === 0 || unseenCount === 0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// If active in last 10 seconds, don't display
|
|
||||||
if (!isIdle()) {
|
|
||||||
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;
|
|
||||||
|
|
||||||
requestPermission().then(function() {
|
|
||||||
// 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);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const DATA_VERSION = 2;
|
|
||||||
function updateSeenNotificationDatesFrom(notifications) {
|
|
||||||
const oldSeenData = JSON.parse(localStorage.getItem(seenDataKey));
|
|
||||||
const oldSeenNotificationDates = (oldSeenData && oldSeenData.v === DATA_VERSION) ? oldSeenData.data : [];
|
|
||||||
let newSeenNotificationDates = [];
|
|
||||||
let previouslyUnseenNotifications = [];
|
|
||||||
|
|
||||||
notifications.forEach(function(notification) {
|
|
||||||
const dateString = new Date(notification.created_at).toUTCString();
|
|
||||||
|
|
||||||
if (oldSeenNotificationDates.indexOf(dateString) === -1) {
|
|
||||||
previouslyUnseenNotifications.push(notification);
|
|
||||||
}
|
|
||||||
newSeenNotificationDates.push(dateString);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
localStorage.setItem(seenDataKey, JSON.stringify({
|
const notificationBody = data.excerpt;
|
||||||
data: newSeenNotificationDates,
|
const notificationIcon = Discourse.SiteSettings.logo_small_url || Discourse.SiteSettings.logo_url;
|
||||||
updated_at: new Date(),
|
|
||||||
v: DATA_VERSION
|
requestPermission().then(function() {
|
||||||
}));
|
// This shows the notification!
|
||||||
return previouslyUnseenNotifications;
|
const notification = new Notification(notificationTitle, {
|
||||||
|
body: notificationBody,
|
||||||
|
icon: notificationIcon,
|
||||||
|
tag: notificationTagName
|
||||||
|
});
|
||||||
|
|
||||||
|
function clickEventHandler() {
|
||||||
|
Discourse.URL.routeTo(data.post_url);
|
||||||
|
// 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
|
// Utility function
|
||||||
|
@ -229,36 +152,10 @@ function requestPermission() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function i18nKey(notification) {
|
function i18nKey(notification_type) {
|
||||||
let key = "notifications.popup." + Discourse.Site.current().get("notificationLookup")[notification.notification_type];
|
return "notifications.popup." + Discourse.Site.current().get("notificationLookup")[notification_type];
|
||||||
if (notification.data.display_username && notification.data.original_username &&
|
|
||||||
notification.data.display_username !== notification.data.original_username) {
|
|
||||||
key += "_mul";
|
|
||||||
}
|
|
||||||
return key;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Exported for controllers/notification.js.es6
|
// 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');
|
export { init, onNotification };
|
||||||
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 };
|
|
||||||
|
|
|
@ -80,6 +80,10 @@ class PostAlerter
|
||||||
user.reload
|
user.reload
|
||||||
end
|
end
|
||||||
|
|
||||||
|
NOTIFIABLE_TYPES = [:mentioned, :replied, :quoted, :posted, :linked, :private_message].map{ |t|
|
||||||
|
Notification.types[t]
|
||||||
|
}
|
||||||
|
|
||||||
def create_notification(user, type, post, opts={})
|
def create_notification(user, type, post, opts={})
|
||||||
return if user.blank?
|
return if user.blank?
|
||||||
return if user.id == Discourse::SYSTEM_USER_ID
|
return if user.id == Discourse::SYSTEM_USER_ID
|
||||||
|
@ -101,9 +105,9 @@ class PostAlerter
|
||||||
# Don't notify the same user about the same notification on the same post
|
# Don't notify the same user about the same notification on the same post
|
||||||
existing_notification = user.notifications
|
existing_notification = user.notifications
|
||||||
.order("notifications.id desc")
|
.order("notifications.id desc")
|
||||||
.find_by(notification_type: type, topic_id: post.topic_id, post_number: post.post_number)
|
.find_by(topic_id: post.topic_id, post_number: post.post_number)
|
||||||
|
|
||||||
if existing_notification
|
if existing_notification && existing_notification.notification_type == type
|
||||||
return unless existing_notification.notification_type == Notification.types[:edited] &&
|
return unless existing_notification.notification_type == Notification.types[:edited] &&
|
||||||
existing_notification.data_hash["display_username"] = opts[:display_username]
|
existing_notification.data_hash["display_username"] = opts[:display_username]
|
||||||
end
|
end
|
||||||
|
@ -143,6 +147,23 @@ class PostAlerter
|
||||||
original_post_id: original_post.id,
|
original_post_id: original_post.id,
|
||||||
original_username: original_username,
|
original_username: original_username,
|
||||||
display_username: opts[:display_username] || post.user.username }.to_json)
|
display_username: opts[:display_username] || post.user.username }.to_json)
|
||||||
|
|
||||||
|
if (!existing_notification) && NOTIFIABLE_TYPES.include?(type)
|
||||||
|
|
||||||
|
# we may have an invalid post somehow, dont blow up
|
||||||
|
post_url = original_post.url rescue nil
|
||||||
|
if post_url
|
||||||
|
MessageBus.publish("/notification-alert/#{user.id}", {
|
||||||
|
notification_type: type,
|
||||||
|
post_number: original_post.post_number,
|
||||||
|
topic_title: original_post.topic.title,
|
||||||
|
excerpt: original_post.excerpt(400, text_entities: true, strip_links: true),
|
||||||
|
username: original_username,
|
||||||
|
post_url: post_url
|
||||||
|
}, user_ids: [user.id])
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
# TODO: Move to post-analyzer?
|
# TODO: Move to post-analyzer?
|
||||||
|
|
|
@ -831,25 +831,13 @@ en:
|
||||||
linked: "<i title='linked post' class='fa fa-arrow-left'></i><p><span>{{username}}</span> {{description}}</p>"
|
linked: "<i title='linked post' class='fa fa-arrow-left'></i><p><span>{{username}}</span> {{description}}</p>"
|
||||||
granted_badge: "<i title='badge granted' class='fa fa-certificate'></i><p>Earned '{{description}}'</p>"
|
granted_badge: "<i title='badge granted' class='fa fa-certificate'></i><p>Earned '{{description}}'</p>"
|
||||||
|
|
||||||
popup_title:
|
|
||||||
one: "New notification on {{site_title}}"
|
|
||||||
other: "{{count}} new notifications on {{site_title}}"
|
|
||||||
popup:
|
popup:
|
||||||
mentioned: '{{username}} mentioned you in "{{topic}}"'
|
mentioned: '{{username}} mentioned you in "{{topic}}" - {{site_title}}'
|
||||||
quoted: '{{username}} quoted you in "{{topic}}"'
|
quoted: '{{username}} quoted you in "{{topic}}" - {{site_title}}'
|
||||||
replied: '{{username}} replied to you in "{{topic}}"'
|
replied: '{{username}} replied to you in "{{topic}}" - {{site_title}}'
|
||||||
replied_mul: '{{username}} in "{{topic}}"'
|
posted: '{{username}} posted in "{{topic}}" - {{site_title}}'
|
||||||
posted: '{{username}} posted in "{{topic}}"'
|
private_message: '{{username}} sent you a private message in "{{topic}}" - {{site_title}}'
|
||||||
posted_mul: '{{username}} posted in "{{topic}}"'
|
linked: '{{username}} linked to your post from "{{topic}}" - {{site_title}}'
|
||||||
edited: '{{username}} edited your post in "{{topic}}"'
|
|
||||||
liked: '{{username}} liked your post in "{{topic}}"'
|
|
||||||
private_message: '{{username}} sent you a private message in "{{topic}}"'
|
|
||||||
private_message_mul: '{{username}} in "{{topic}}"'
|
|
||||||
invited_to_private_message: '{{username}} invited you to a private message: "{{topic}}"'
|
|
||||||
invitee_accepted: '{{username}} joined the forum!'
|
|
||||||
moved_post: '{{username}} moved your post in "{{topic}}"'
|
|
||||||
linked: '{{username}} linked to your post from "{{topic}}"'
|
|
||||||
granted_badge: 'You earned the "{{badge}}" badge!'
|
|
||||||
|
|
||||||
upload_selector:
|
upload_selector:
|
||||||
title: "Add an image"
|
title: "Add an image"
|
||||||
|
|
Loading…
Reference in a new issue