FEATURE: by default users track a group (as opposed to watch)

FEATURE: a rollup counting number of messages in the group inbox to tracking users
This commit is contained in:
Sam Saffron 2016-01-27 21:38:14 +11:00
parent 74f22f95da
commit a764cc3a42
8 changed files with 93 additions and 12 deletions

View File

@ -42,6 +42,11 @@ export default Ember.Component.extend({
if (it.get('notification_type') === INVITED_TYPE) {
return Discourse.getURL('/users/' + it.get('data.display_username'));
}
if (it.get('data.group_id')) {
return Discourse.getURL('/users/' + it.get('data.username') + '/messages/group/' + it.get('data.group_name'));
}
}.property("notification.data.{badge_id,badge_name,display_username}", "model.slug", "model.topic_id", "model.post_number"),
description: function() {
@ -63,7 +68,15 @@ export default Ember.Component.extend({
const notification = this.get('notification');
const description = this.get('description');
const username = notification.get('data.display_username');
const text = Discourse.Emoji.unescape(I18n.t(this.get('scope'), {description, username}));
var text;
if (notification.get('data.inbox_count')) {
const count = notification.get('data.inbox_count');
const group_name = notification.get('data.group_name');
text = I18n.t(this.get('scope'), {count, group_name});
} else {
text = I18n.t(this.get('scope'), {description, username});
}
text = Discourse.Emoji.unescape(text);
const url = this.get('url');
if (url) {

View File

@ -41,7 +41,9 @@ class Notification < ActiveRecord::Base
granted_badge: 12,
invited_to_topic: 13,
custom: 14,
group_mentioned: 15)
group_mentioned: 15,
group_message_summary: 16
)
end
def self.mark_posts_read(user, topic_id, post_numbers)

View File

@ -294,8 +294,13 @@ class User < ActiveRecord::Base
User.where("id = ? and seen_notification_id < ?", id, notification_id)
.update_all ["seen_notification_id = ?", notification_id]
# mark all "badge granted" and "invite accepted" notifications read
Notification.where('user_id = ? AND NOT read AND notification_type IN (?)', id, [Notification.types[:granted_badge], Notification.types[:invitee_accepted]])
# some notifications are considered read once seen
Notification.where('user_id = ? AND NOT read AND notification_type IN (?)', id, [
Notification.types[:granted_badge],
Notification.types[:invitee_accepted],
Notification.types[:group_message_summary],
Notification.types[:liked]
])
.update_all ["read = ?", true]
end

View File

@ -28,7 +28,7 @@ class PostAlerter
allowed_users(post) - allowed_group_users(post)
end
def undirectly_targeted_users(post)
def indirectly_targeted_users(post)
allowed_group_users(post)
end
@ -77,12 +77,16 @@ class PostAlerter
end
end
# users that are part of all mentionned groups
undirectly_targeted_users(post).each do |user|
indirectly_targeted_users(post).each do |user|
if !notified.include?(user)
# only create a notification when watching the group
if TopicUser.get(post.topic, user).try(:notification_level) == TopicUser.notification_levels[:watching]
notification_level = TopicUser.get(post.topic, user).try(:notification_level)
if notification_level == TopicUser.notification_levels[:watching]
create_notification(user, Notification.types[:private_message], post)
notified += [user]
elsif notification_level == TopicUser.notification_levels[:tracking]
notify_group_summary(user, post)
notified += [user]
end
end
end
@ -141,6 +145,55 @@ class PostAlerter
Notification.types[t]
}
def group_stats(topic)
topic.allowed_groups.map do |g|
{
group_id: g.id,
group_name: g.name.downcase,
inbox_count: Topic.exec_sql(
"SELECT COUNT(*) FROM topics t
JOIN topic_allowed_groups g ON g.id = :group_id AND g.topic_id = t.id
LEFT JOIN group_archived_messages a ON a.topic_id = t.id AND a.group_id = g.id
WHERE a.id IS NULL AND t.deleted_at is NULL AND t.archetype = 'private_message'",
group_id: g.id).values[0][0].to_i
}
end
end
def notify_group_summary(user,post)
@group_stats ||= {}
stats = (@group_stats[post.topic_id] ||= group_stats(post.topic))
return unless stats
group_id = post.topic
.topic_allowed_groups
.where(group_id: user.groups.pluck(:id))
.pluck(:group_id).first
stat = stats.find{|s| s[:group_id] == group_id}
return unless stat
notification_type = Notification.types[:group_message_summary]
Notification.where(notification_type: notification_type, user_id: user.id).each do |n|
n.destroy if n.data_hash[:group_id] == stat[:group_id]
end
Notification.create(
notification_type: notification_type,
user_id: user.id,
data: {
group_id: stat[:group_id],
group_name: stat[:group_name],
inbox_count: stat[:inbox_count],
username: user.username_lower
}.to_json
)
# TODO decide if it makes sense to also publish a desktop notification
end
def create_notification(user, type, post, opts=nil)
return if user.blank?
return if user.id == Discourse::SYSTEM_USER_ID

View File

@ -1002,6 +1002,10 @@ en:
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>"
group_message_summary:
one: "<i title='messages in group inbox' class='fa fa-group'></i><p> there is {{count}} message in your {{group_name}} inbox</p>"
other: "<i title='messages in group inbox' class='fa fa-group'></i><p> there are {{count}} messages in your {{group_name}} inbox</p>"
alt:
mentioned: "Mentioned by"
quoted: "Quoted by"
@ -1016,6 +1020,7 @@ en:
moved_post: "Your post was moved by"
linked: "Link to your post"
granted_badge: "Badge granted"
group_message_summary: "Messages in group inbox"
popup:
mentioned: '{{username}} mentioned you in "{{topic}}" - {{site_title}}'

View File

@ -73,10 +73,10 @@ class TopicCreator
tag.group.group_users.each do |gu|
next if gu.user_id == -1 || gu.user_id == topic.user_id
action = case gu.notification_level
when TopicUser.notification_levels[:tracking] then "track!"
when TopicUser.notification_levels[:tracking] then "watch!"
when TopicUser.notification_levels[:regular] then "regular!"
when TopicUser.notification_levels[:muted] then "mute!"
else "watch!"
else "track!"
end
topic.notifier.send(action, gu.user_id)
end

View File

@ -18,7 +18,7 @@ describe "i18n integrity checks" do
it "needs an i18n key (notification_types) for each Notification type" do
Notification.types.each_key do |type|
next if type == :custom
next if type == :custom || type == :group_message_summary
expect(I18n.t("notification_types.#{type}")).not_to match(/translation missing/)
end
end

View File

@ -43,8 +43,11 @@ describe PostAction do
expect(topic_user_ids).to include(codinghorror.id)
expect(topic_user_ids).to include(mod.id)
# Notification level should be "Watching" for everyone
expect(topic.topic_users(true).map(&:notification_level).uniq).to eq([TopicUser.notification_levels[:watching]])
expect(topic.topic_users.where(user_id: mod.id)
.pluck(:notification_level).first).to eq(TopicUser.notification_levels[:tracking])
expect(topic.topic_users.where(user_id: codinghorror.id)
.pluck(:notification_level).first).to eq(TopicUser.notification_levels[:watching])
# reply to PM should not clear flag
PostCreator.new(mod, topic_id: posts[0].topic_id, raw: "This is my test reply to the user, it should clear flags").create