FEATURE: Show draft count in user menu and activity (#13812)

This commit adds the number of drafts a user has next to the "Draft"
label in the user preferences menu and activity tab. The count is
updated via MessageBus when a draft is created or destroyed.
This commit is contained in:
Bianca Nenciu 2021-07-27 14:05:33 +03:00 committed by GitHub
parent d801e33e0b
commit 760c9a5698
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 106 additions and 3 deletions

View File

@ -3,7 +3,7 @@ import I18n from "I18n";
import { alias } from "@ember/object/computed"; import { alias } from "@ember/object/computed";
import bootbox from "bootbox"; import bootbox from "bootbox";
import { exportUserArchive } from "discourse/lib/export-csv"; import { exportUserArchive } from "discourse/lib/export-csv";
import { observes } from "discourse-common/utils/decorators"; import discourseComputed, { observes } from "discourse-common/utils/decorators";
export default Controller.extend({ export default Controller.extend({
application: controller(), application: controller(),
@ -28,6 +28,13 @@ export default Controller.extend({
this.set("application.showFooter", showFooter); this.set("application.showFooter", showFooter);
}, },
@discourseComputed("currentUser.draft_count")
draftLabel(count) {
return count > 0
? I18n.t("drafts.label_with_count", { count })
: I18n.t("drafts.label");
},
actions: { actions: {
exportUserArchive() { exportUserArchive() {
bootbox.confirm( bootbox.confirm(

View File

@ -0,0 +1,15 @@
export default {
name: "subscribe-user-changes",
after: "message-bus",
initialize(container) {
const user = container.lookup("current-user:main");
if (user) {
const bus = container.lookup("message-bus:main");
bus.subscribe("/user", (data) => {
user.setProperties(data);
});
}
},
};

View File

@ -13,7 +13,7 @@
{{#if user.showDrafts}} {{#if user.showDrafts}}
{{#d-navigation-item route="userActivity.drafts"}} {{#d-navigation-item route="userActivity.drafts"}}
{{i18n "user_action_groups.15"}} {{draftLabel}}
{{/d-navigation-item}} {{/d-navigation-item}}
{{/if}} {{/if}}

View File

@ -84,7 +84,12 @@ createWidgetFrom(QuickAccessPanel, "quick-access-profile", {
{ {
icon: "pencil-alt", icon: "pencil-alt",
href: `${this.attrs.path}/activity/drafts`, href: `${this.attrs.path}/activity/drafts`,
content: I18n.t("user_action_groups.15"), content:
this.currentUser.draft_count > 0
? I18n.t("drafts.label_with_count", {
count: this.currentUser.draft_count,
})
: I18n.t("drafts.label"),
className: "drafts", className: "drafts",
}, },
{ {

View File

@ -7,6 +7,8 @@ class Draft < ActiveRecord::Base
belongs_to :user belongs_to :user
after_commit :update_draft_count, on: [:create, :destroy]
class OutOfSequence < StandardError; end class OutOfSequence < StandardError; end
def self.set(user, key, sequence, data, owner = nil, force_save: false) def self.set(user, key, sequence, data, owner = nil, force_save: false)
@ -92,6 +94,8 @@ class Draft < ActiveRecord::Base
owner = :owner, owner = :owner,
updated_at = CURRENT_TIMESTAMP updated_at = CURRENT_TIMESTAMP
SQL SQL
UserStat.update_draft_count(user.id)
end end
sequence sequence
@ -338,6 +342,9 @@ class Draft < ActiveRecord::Base
end end
def update_draft_count
UserStat.update_draft_count(self.user_id)
end
end end
# == Schema Information # == Schema Information

View File

@ -202,6 +202,21 @@ class UserStat < ActiveRecord::Base
self.class.update_distinct_badge_count(self.user_id) self.class.update_distinct_badge_count(self.user_id)
end end
def self.update_draft_count(user_id)
draft_count = DB.query_single <<~SQL, user_id: user_id
UPDATE user_stats
SET draft_count = (SELECT COUNT(*) FROM drafts WHERE user_id = :user_id)
WHERE user_id = :user_id
RETURNING draft_count
SQL
MessageBus.publish(
'/user',
{ draft_count: draft_count.first },
user_ids: [user_id]
)
end
# topic_reply_count is a count of posts in other users' topics # topic_reply_count is a count of posts in other users' topics
def calc_topic_reply_count!(start_time = nil) def calc_topic_reply_count!(start_time = nil)
sql = <<~SQL sql = <<~SQL
@ -292,4 +307,5 @@ end
# distinct_badge_count :integer default(0), not null # distinct_badge_count :integer default(0), not null
# first_unread_pm_at :datetime not null # first_unread_pm_at :datetime not null
# digest_attempted_at :datetime # digest_attempted_at :datetime
# draft_count :integer default(0), not null
# #

View File

@ -65,6 +65,7 @@ class CurrentUserSerializer < BasicUserSerializer
:do_not_disturb_until, :do_not_disturb_until,
:has_topic_draft, :has_topic_draft,
:can_review, :can_review,
:draft_count,
def groups def groups
owned_group_ids = GroupUser.where(user_id: id, owner: true).pluck(:group_id).to_set owned_group_ids = GroupUser.where(user_id: id, owner: true).pluck(:group_id).to_set
@ -315,4 +316,8 @@ class CurrentUserSerializer < BasicUserSerializer
def include_has_topic_draft? def include_has_topic_draft?
Draft.has_topic_draft(object) Draft.has_topic_draft(object)
end end
def draft_count
object.user_stat.draft_count
end
end end

View File

@ -332,6 +332,8 @@ en:
copied: "copied!" copied: "copied!"
drafts: drafts:
label: "Drafts"
label_with_count: "Drafts (%{count})"
resume: "Resume" resume: "Resume"
remove: "Remove" remove: "Remove"
remove_confirmation: "Are you sure you want to delete this draft?" remove_confirmation: "Are you sure you want to delete this draft?"

View File

@ -0,0 +1,18 @@
# frozen_string_literal: true
class AddDraftCountToUserStat < ActiveRecord::Migration[6.1]
def change
add_column :user_stats, :draft_count, :integer, default: 0, null: false
execute <<~SQL
UPDATE user_stats
SET draft_count = new_user_stats.draft_count
FROM (SELECT user_stats.user_id, COUNT(drafts.id) draft_count
FROM user_stats
LEFT JOIN drafts ON user_stats.user_id = drafts.user_id
GROUP BY user_stats.user_id) new_user_stats
WHERE user_stats.user_id = new_user_stats.user_id
AND user_stats.draft_count <> new_user_stats.draft_count
SQL
end
end

View File

@ -176,6 +176,20 @@ describe Draft do
expect(Draft.count).to eq 0 expect(Draft.count).to eq 0
end end
it 'updates draft count when a draft is created or destroyed' do
messages = MessageBus.track_publish("/user") do
Draft.set(user, "test", 0, "data")
end
expect(messages.first.data[:draft_count]).to eq(1)
messages = MessageBus.track_publish("/user") do
Draft.where(user: user).destroy_all
end
expect(messages.first.data[:draft_count]).to eq(0)
end
describe '#stream' do describe '#stream' do
fab!(:public_post) { Fabricate(:post) } fab!(:public_post) { Fabricate(:post) }
let(:public_topic) { public_post.topic } let(:public_topic) { public_post.topic }

View File

@ -222,4 +222,18 @@ describe UserStat do
end end
end end
describe '.update_draft_count' do
fab!(:user) { Fabricate(:user) }
it 'updates draft_count' do
Draft.create!(user: user, draft_key: "topic_1", data: {})
Draft.create!(user: user, draft_key: "new_topic", data: {})
Draft.create!(user: user, draft_key: "topic_2", data: {})
UserStat.update_all(draft_count: 0)
UserStat.update_draft_count(user.id)
expect(user.user_stat.draft_count).to eq(3)
end
end
end end