FEATURE: Don't display muted/ignored users under "who liked" (#10084)

* FEATURE: Don't display muted/ignored users under "who liked"

Previously, if you clicked on the heart icon below a post
it would show you the avatar for a user even if you ignored or muted
them.

This commit will instead display a (?) icon. The count of likes will
remain correct, but you needn't be reminded of the person you
preferred not to see.

* Use a circle instead of (?) for unknown user
This commit is contained in:
Robin Ward 2020-06-19 10:44:21 -04:00 committed by GitHub
parent 8a86705e51
commit 4a2871f7f6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 96 additions and 12 deletions

View File

@ -6,17 +6,18 @@ import { h } from "virtual-dom";
import { userPath } from "discourse/lib/url"; import { userPath } from "discourse/lib/url";
import hbs from "discourse/widgets/hbs-compiler"; import hbs from "discourse/widgets/hbs-compiler";
export function avatarAtts(user) { export function smallUserAtts(user) {
return { return {
template: user.avatar_template, template: user.avatar_template,
username: user.username, username: user.username,
post_url: user.post_url, post_url: user.post_url,
url: userPath(user.username_lower) url: userPath(user.username_lower),
unknown: user.unknown
}; };
} }
createWidget("small-user-list", { createWidget("small-user-list", {
tagName: "div.clearfix", tagName: "div.clearfix.small-user-list",
buildClasses(atts) { buildClasses(atts) {
return atts.listClassName; return atts.listClassName;
@ -30,7 +31,7 @@ createWidget("small-user-list", {
atts.addSelf && atts.addSelf &&
!users.some(u => u.username === currentUser.username) !users.some(u => u.username === currentUser.username)
) { ) {
users = users.concat(avatarAtts(currentUser)); users = users.concat(smallUserAtts(currentUser));
} }
let description = null; let description = null;
@ -43,7 +44,13 @@ createWidget("small-user-list", {
let postUrl; let postUrl;
const icons = users.map(u => { const icons = users.map(u => {
postUrl = postUrl || u.post_url; postUrl = postUrl || u.post_url;
if (u.unknown) {
return h("div.unknown", {
attributes: { title: I18n.t("post.unknown_user") }
});
} else {
return avatarFor.call(this, "small", u); return avatarFor.call(this, "small", u);
}
}); });
if (postUrl) { if (postUrl) {

View File

@ -12,8 +12,13 @@ export default createWidget("emoji", {
tagName: "img.emoji", tagName: "img.emoji",
buildAttributes(attrs) { buildAttributes(attrs) {
let result = { src: emojiUrlFor(attrs.name), alt: `:${attrs.name}:` }; let result = {
if (attrs.title) result.title = attrs.name; src: emojiUrlFor(attrs.name),
alt: `:${attrs.alt || attrs.name}:`
};
if (attrs.title) {
result.title = typeof attrs.title === "string" ? attrs.title : attrs.name;
}
return result; return result;
} }
}); });

View File

@ -1,6 +1,6 @@
import { next, run } from "@ember/runloop"; import { next, run } from "@ember/runloop";
import { applyDecorators, createWidget } from "discourse/widgets/widget"; import { applyDecorators, createWidget } from "discourse/widgets/widget";
import { avatarAtts } from "discourse/widgets/actions-summary"; import { smallUserAtts } from "discourse/widgets/actions-summary";
import { h } from "virtual-dom"; import { h } from "virtual-dom";
import showModal from "discourse/lib/show-modal"; import showModal from "discourse/lib/show-modal";
import { Promise } from "rsvp"; import { Promise } from "rsvp";
@ -696,7 +696,7 @@ export default createWidget("post-menu", {
post_action_type_id: LIKE_ACTION post_action_type_id: LIKE_ACTION
}) })
.then(users => { .then(users => {
state.likedUsers = users.map(avatarAtts); state.likedUsers = users.map(smallUserAtts);
state.total = users.totalRows; state.total = users.totalRows;
}); });
}, },
@ -705,7 +705,7 @@ export default createWidget("post-menu", {
const { attrs, state } = this; const { attrs, state } = this;
return this.store.find("post-reader", { id: attrs.id }).then(users => { return this.store.find("post-reader", { id: attrs.id }).then(users => {
state.readers = users.map(avatarAtts); state.readers = users.map(smallUserAtts);
state.totalReaders = users.totalRows; state.totalReaders = users.totalRows;
}); });
}, },

View File

@ -264,6 +264,16 @@ blockquote {
} }
} }
.small-user-list .unknown {
display: inline-block;
background-color: $primary-low;
width: 25px;
height: 25px;
border-radius: 50%;
vertical-align: middle;
margin-right: 0.25em;
}
.post-hidden { .post-hidden {
.topic-avatar, .topic-avatar,
.cooked, .cooked,

View File

@ -15,6 +15,16 @@ class PostActionUsersController < ApplicationController
post = finder.first post = finder.first
guardian.ensure_can_see!(post) guardian.ensure_can_see!(post)
unknown_user_ids = Set.new
if current_user.present?
result = DB.query_single(<<~SQL, user_id: current_user.id)
SELECT mu.muted_user_id AS id FROM muted_users AS mu WHERE mu.user_id = :user_id
UNION
SELECT iu.ignored_user_id AS id FROM ignored_users AS iu WHERE iu.user_id = :user_id
SQL
unknown_user_ids.merge(result)
end
post_actions = post.post_actions.where(post_action_type_id: post_action_type_id) post_actions = post.post_actions.where(post_action_type_id: post_action_type_id)
.includes(:user) .includes(:user)
.offset(page * page_size) .offset(page * page_size)
@ -29,7 +39,13 @@ class PostActionUsersController < ApplicationController
action_type = PostActionType.types.key(post_action_type_id) action_type = PostActionType.types.key(post_action_type_id)
total_count = post["#{action_type}_count"].to_i total_count = post["#{action_type}_count"].to_i
data = { post_action_users: serialize_data(post_actions.to_a, PostActionUserSerializer) } data = {
post_action_users: serialize_data(
post_actions.to_a,
PostActionUserSerializer,
unknown_user_ids: unknown_user_ids
)
}
if total_count > page_size if total_count > page_size
data[:total_rows_post_action_users] = total_count data[:total_rows_post_action_users] = total_count

View File

@ -2,7 +2,8 @@
class PostActionUserSerializer < BasicUserSerializer class PostActionUserSerializer < BasicUserSerializer
attributes :post_url, attributes :post_url,
:username_lower :username_lower,
:unknown
def id def id
object.user.id object.user.id
@ -24,4 +25,12 @@ class PostActionUserSerializer < BasicUserSerializer
object.related_post.url if object.related_post_id && object.related_post object.related_post.url if object.related_post_id && object.related_post
end end
def unknown
true
end
def include_unknown?
(@options[:unknown_user_ids] || []).include?(object.user.id)
end
end end

View File

@ -2603,6 +2603,7 @@ en:
one: "%{count} Reply" one: "%{count} Reply"
other: "%{count} Replies" other: "%{count} Replies"
unknown_user: "(unknown/deleted user)"
has_likes_title: has_likes_title:
one: "%{count} person liked this post" one: "%{count} person liked this post"
other: "%{count} people liked this post" other: "%{count} people liked this post"

View File

@ -59,6 +59,22 @@ describe PostActionUsersController do
expect(response.status).to eq(200) expect(response.status).to eq(200)
end end
it 'will return an unknown attribute for muted users' do
ignored_user = Fabricate(:user)
PostActionCreator.like(ignored_user, post)
regular_user = Fabricate(:user)
PostActionCreator.like(regular_user, post)
IgnoredUser.create(user: user, ignored_user: ignored_user)
get "/post_action_users.json", params: {
id: post.id, post_action_type_id: PostActionType.types[:like]
}
expect(response.status).to eq(200)
json_users = response.parsed_body['post_action_users']
expect(json_users.find { |u| u['id'] == regular_user.id }['unknown']).to be_blank
expect(json_users.find { |u| u['id'] == ignored_user.id }['unknown']).to eq(true)
end
it "paginates post actions" do it "paginates post actions" do
user_ids = [] user_ids = []
5.times do 5.times do

View File

@ -0,0 +1,20 @@
import { moduleForWidget, widgetTest } from "helpers/widget-test";
moduleForWidget("small-user-list");
widgetTest("renders avatars and support for unknown", {
template: '{{mount-widget widget="small-user-list" args=args}}',
beforeEach() {
this.set("args", {
users: [
{ id: 456, username: "eviltrout" },
{ id: 457, username: "someone", unknown: true }
]
});
},
async test(assert) {
assert.ok(find("[data-user-card=eviltrout]").length === 1);
assert.ok(find("[data-user-card=someone]").length === 0);
assert.ok(find(".unknown").length, "includes unkown user");
}
});